diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,158 +2,178 @@
 
 ## Changelog guidelines (apply to `5.5.0.0` onward)
 
-Changes ported from the upstream raylib code are not mentioned unless they are breaking changes. Changes and bug fixes in the h-raylib API are mentioned, breaking and non-breaking. Internal changes that do not change the API or affect functionality are not mentioned (e.g. performance improvements).
+Changes ported from the upstream raylib code are not mentioned unless they are
+breaking changes. Changes and bug fixes in the h-raylib API are mentioned,
+breaking and non-breaking. Internal changes that do not change the API or affect
+functionality are not mentioned (e.g. performance improvements).
 
 ### Versioning scheme
 
-h-raylib's version numbers do not follow the usual format. The first two numbers in the version track the underlying C raylib version. For example, `5.1.x.x` versions use raylib 5.1 under the hood. The third number represents breaking changes (renamed/deleted functions or modules). The last number represents non-breaking changes (new functions or modules, bug fixes, etc). The safest version bound format to use is `h-raylib >=x.y.z.w && <x.y.(z+1)` (instead of the usual `^>=` bound).
+h-raylib's version numbers do not follow the usual format. The first two numbers
+in the version track the underlying C raylib version. For example, `5.1.x.x`
+versions use raylib 5.1 under the hood. The third number represents breaking
+changes (renamed/deleted functions or modules). The last number represents
+non-breaking changes (new functions or modules, bug fixes, etc). The safest
+version bound format to use is `h-raylib >=x.y.z.w && <x.y.(z+1)` (instead of
+the usual `^>=` bound).
 
-## Version 5.5.3.1
-_14 October 2025_
+## Version 5.6.0.0 _19 January 2026_
 
+- \[[#61](https://github.com/Anut-py/h-raylib/issues/61)\] Fixed a bug where
+  model animations didn't load properly
+- \[[#68](https://github.com/Anut-py/h-raylib/issues/68)\] `vectorNormalize` no
+  longer attempts to normalize zero vectors
+- \[[#69](https://github.com/Anut-py/h-raylib/issues/69)\] Most types are now
+  instances of `Read`
+
+## Version 5.5.3.1 _14 October 2025_
+
 - Fixed a bug with `setTraceLogCallback` not working properly
 
-## Version 5.5.3.0
-_14 August 2025_
+## Version 5.5.3.0 _14 August 2025_
 
 - **BREAKING CHANGE**: Bracket functions now only work with the `IO` monad
-- \[[#60](https://github.com/Anut-py/h-raylib/issues/60)\] Added the `disable-lens` flag
+- \[[#60](https://github.com/Anut-py/h-raylib/issues/60)\] Added the
+  `disable-lens` flag
 - Loosened version bounds
 
-## Version 5.5.2.1
-_28 October 2024_
+## Version 5.5.2.1 _28 October 2024_
 
 - Bug fixes for web compilation
 
-## Version 5.5.2.0
-_21 October 2024_
+## Version 5.5.2.0 _21 October 2024_
 
-- **BREAKING CHANGE**: `is*Ready` functions renamed to `is*Valid` (upstream change in raylib)
-- **BREAKING CHANGE**: `setGamepadVibration` takes a fourth argument, `duration` (upstream change in raylib)
+- **BREAKING CHANGE**: `is*Ready` functions renamed to `is*Valid` (upstream
+  change in raylib)
+- **BREAKING CHANGE**: `setGamepadVibration` takes a fourth argument, `duration`
+  (upstream change in raylib)
 - Loosened the version bound on `base`
 
-## Version 5.5.1.0
-_11 October 2024_
+## Version 5.5.1.0 _11 October 2024_
 
-- **BREAKING CHANGE**: `set*Callback` functions are no longer managed and do not return `IO C'*Callback` values
+- **BREAKING CHANGE**: `set*Callback` functions are no longer managed and do not
+  return `IO C'*Callback` values
 - **BREAKING CHANGE**: Removed `loadImageSvg` (upstream change in raylib)
-- \[[#58](https://github.com/Anut-py/h-raylib/issues/58)\] Added support for `setTraceLogCallback`
+- \[[#58](https://github.com/Anut-py/h-raylib/issues/58)\] Added support for
+  `setTraceLogCallback`
 
-## Version 5.5.0.0
-_12 July 2024_
+## Version 5.5.0.0 _12 July 2024_
 
 - **BREAKING CHANGE**: Reimplemented `Vector2/3/4` using `linear`'s `V2/3/4`
-- \[[#55](https://github.com/Anut-py/h-raylib/issues/55)\] **BREAKING CHANGE**: Completely reworked memory management (check `DOCUMENTATION.md` and the example programs)
+- \[[#55](https://github.com/Anut-py/h-raylib/issues/55)\] **BREAKING CHANGE**:
+  Completely reworked memory management (check `DOCUMENTATION.md` and the
+  example programs)
 - Dropped ghc 8.10 support
-- \[[#54](https://github.com/Anut-py/h-raylib/issues/54)\] Exposed internal modules
-- \[[#56](https://github.com/Anut-py/h-raylib/issues/56)\] Fixed a bug with `loadFontEx` and other font-related functions not working
+- \[[#54](https://github.com/Anut-py/h-raylib/issues/54)\] Exposed internal
+  modules
+- \[[#56](https://github.com/Anut-py/h-raylib/issues/56)\] Fixed a bug with
+  `loadFontEx` and other font-related functions not working
 
-## Version 5.1.3.0
-_13 April, 2024_
+## Version 5.1.3.0 _13 April, 2024_
 
 - Made some utility functions pure
 - Improved the performance of `Raylib.Util.Math`
 
-## Version 5.1.2.0
-_9 April, 2024_
+## Version 5.1.2.0 _9 April, 2024_
 
 - Internal changes for performance
 - Fixed a typo in `filePathList'capacity` (previously `filePathlist'capacity`)
 - Fixed marshalling bugs with `FilePathList`
 
-## Version 5.1.1.0
-_22 February, 2024_
+## Version 5.1.1.0 _22 February, 2024_
 
-- Split `Raylib.Types` into different modules (`Raylib.Types` reexports everything, so this will not break existing code)
+- Split `Raylib.Types` into different modules (`Raylib.Types` reexports
+  everything, so this will not break existing code)
 - Fixed `flake.nix` and `default.nix`
 - Exposed all the native functions
 - Added support for callbacks
 - Added more thorough Haddock documentation
 - Added pointer utility functions (`p'*`)
-- \[[#4](https://github.com/Anut-py/h-raylib/issues/4)\] Started working on web support
-- \[[#34](https://github.com/Anut-py/h-raylib/issues/34)\] Added bindings for raygui (`Raylib.Util.GUI`, `Raylib.Util.GUI.Styles`)
+- \[[#4](https://github.com/Anut-py/h-raylib/issues/4)\] Started working on web
+  support
+- \[[#34](https://github.com/Anut-py/h-raylib/issues/34)\] Added bindings for
+  raygui (`Raylib.Util.GUI`, `Raylib.Util.GUI.Styles`)
 
-## Version 5.1.0.1
-_16 January, 2024_
+## Version 5.1.0.1 _16 January, 2024_
 
 - Updated raylib to the master branch
-- \[[#26](https://github.com/Anut-py/h-raylib/pull/26)\] New `platform-nixos` build flag
-- \[[#27](https://github.com/Anut-py/h-raylib/pull/27), [#35](https://github.com/Anut-py/h-raylib/pull/35)\] Added flake.nix
+- \[[#26](https://github.com/Anut-py/h-raylib/pull/26)\] New `platform-nixos`
+  build flag
+- \[[#27](https://github.com/Anut-py/h-raylib/pull/27),
+  [#35](https://github.com/Anut-py/h-raylib/pull/35)\] Added flake.nix
 
-## Version 4.6.0.7
-_10 September, 2023_
+## Version 4.6.0.7 _10 September, 2023_
 
 - Updated raylib to the master branch
 
-## Version 4.6.0.6
-_24 July, 2023_
+## Version 4.6.0.6 _24 July, 2023_
 
 - Updated raylib to the master branch
 
-## Version 4.6.0.5
-_29 June, 2023_
+## Version 4.6.0.5 _29 June, 2023_
 
-- \[[#22](https://github.com/Anut-py/h-raylib/pull/22)\] Allowed `base-4.18` to support GHC 9.4 and higher
-- \[[#23](https://github.com/Anut-py/h-raylib/pull/23)\] Added helper functions in `Raylib.Util` as an alternative to the `begin*` and `end*` functions
+- \[[#22](https://github.com/Anut-py/h-raylib/pull/22)\] Allowed `base-4.18` to
+  support GHC 9.4 and higher
+- \[[#23](https://github.com/Anut-py/h-raylib/pull/23)\] Added helper functions
+  in `Raylib.Util` as an alternative to the `begin*` and `end*` functions
 
-## Version 4.6.0.4
-_16 June, 2023_
+## Version 4.6.0.4 _16 June, 2023_
 
 - Updated raylib to the master branch
-- \[[#19](https://github.com/Anut-py/h-raylib/pull/19)\] Changed all the `Raylib.Util` functions to use `MonadIO` for flexibility
-- \[[#20](https://github.com/Anut-py/h-raylib/pull/20)\] Changed `CFloat` to `Float` in `drawCapsule` and `drawCapsuleWires`
+- \[[#19](https://github.com/Anut-py/h-raylib/pull/19)\] Changed all the
+  `Raylib.Util` functions to use `MonadIO` for flexibility
+- \[[#20](https://github.com/Anut-py/h-raylib/pull/20)\] Changed `CFloat` to
+  `Float` in `drawCapsule` and `drawCapsuleWires`
 
-## Version 4.6.0.3
-_23 April, 2023_
+## Version 4.6.0.3 _23 April, 2023_
 
 - Updated raylib to the master branch
-- \[[#18](https://github.com/Anut-py/h-raylib/pull/18)\] Added lenses for raylib data structures
+- \[[#18](https://github.com/Anut-py/h-raylib/pull/18)\] Added lenses for raylib
+  data structures
 
-## Version 4.6.0.2
-_8 April, 2023_
+## Version 4.6.0.2 _8 April, 2023_
 
 - Fixed a bug in `clamp`
 - Updated raylib to the master branch
 
-## Version 4.6.0.1
-_2 April, 2023_
+## Version 4.6.0.1 _2 April, 2023_
 
-- Created the `Raylib.Util.Math` and `Raylib.Util.Camera` modules. They are Haskell implementations of `raymath` and `rcamera`.
-- \[[#15](https://github.com/Anut-py/h-raylib/pull/15)\] Fixed a memory issue with `getFontDefault`
+- Created the `Raylib.Util.Math` and `Raylib.Util.Camera` modules. They are
+  Haskell implementations of `raymath` and `rcamera`.
+- \[[#15](https://github.com/Anut-py/h-raylib/pull/15)\] Fixed a memory issue
+  with `getFontDefault`
 
-## Version 4.5.3.4
-_19 March, 2023_
+## Version 4.5.3.4 _19 March, 2023_
 
 - Updated raylib to the master branch
 
-## Version 4.5.3.3
-_15 March, 2023_
+## Version 4.5.3.3 _15 March, 2023_
 
 - Added GHCi support (see README.md for usage instructions)
 - Updated raylib to the master branch
 - Removed global state; use `WindowResources` (see examples for usage)
 
-## Version 4.5.3.2
-_1 March, 2023_
+## Version 4.5.3.2 _1 March, 2023_
 
-- \[[#12](https://github.com/Anut-py/h-raylib/pull/12)\] Added rlgl bindings (`Raylib.Util.RLGL`)
+- \[[#12](https://github.com/Anut-py/h-raylib/pull/12)\] Added rlgl bindings
+  (`Raylib.Util.RLGL`)
 
-## Version 4.5.3.1
-_27 February, 2023_
+## Version 4.5.3.1 _27 February, 2023_
 
 - Added manual asset unloading functions
 - Updated raylib to the master branch
-- \[[#11](https://github.com/Anut-py/h-raylib/pull/11)\] Fixed a build issue on MacOS
+- \[[#11](https://github.com/Anut-py/h-raylib/pull/11)\] Fixed a build issue on
+  MacOS
 
-## Version 4.5.3.0
-_24 February, 2023_
+## Version 4.5.3.0 _24 February, 2023_
 
--  **BREAKING CHANGE**: Restructured project; the main modules are moved into `Raylib/Core` and `Raylib.Colors` is now `Raylib.Util.Colors`
-- Changed `setShaderValue` and `setShaderValueV` to consume Haskell values rather than `Ptr`s
+-  **BREAKING CHANGE**: Restructured project; the main modules are moved into
+   `Raylib/Core` and `Raylib.Colors` is now `Raylib.Util.Colors`
+- Changed `setShaderValue` and `setShaderValueV` to consume Haskell values
+  rather than `Ptr`s
 - Added the `Raylib.Util` module for utility functions
 
-## Version 4.5.2.0
-_21 February, 2023_
+## Version 4.5.2.0 _21 February, 2023_
 
 - **BREAKING CHANGE**: Removed asset unloading functions
 - **BREAKING CHANGE**: Changed `Camera3D` API to match C code
@@ -161,67 +181,62 @@
 - Added mathematical operators for vector types
 - Updated raylib to the master branch
 
-## Version 4.5.1.1
-_14 February, 2023_
+## Version 4.5.1.1 _14 February, 2023_
 
 - Added finalizers to auto-unload audio data
 - Fixed C include errors
 
-## Version 4.5.1.0
-_12 February, 2023_
+## Version 4.5.1.0 _12 February, 2023_
 
 - **BREAKING CHANGE**: Changed all types to minimize usage of `Ptr`s
-- **BREAKING CHANGE**: Split the `Raylib` module into six modules: `Raylib.Audio`, `Raylib.Core`, `Raylib.Models`, `Raylib.Shapes`, `Raylib.Text`, and `Raylib.Textures`
+- **BREAKING CHANGE**: Split the `Raylib` module into six modules:
+  `Raylib.Audio`, `Raylib.Core`, `Raylib.Models`, `Raylib.Shapes`,
+  `Raylib.Text`, and `Raylib.Textures`
 - Added the internal `Freeable` typeclass to prevent memory leaks
-- \[[#8](https://github.com/Anut-py/h-raylib/issues/8)\] Added `Xext` as a dependency again
+- \[[#8](https://github.com/Anut-py/h-raylib/issues/8)\] Added `Xext` as a
+  dependency again
 
-## Version 4.5.0.12
-_14 January, 2023_
+## Version 4.5.0.12 _14 January, 2023_
 
 - Removed `ShaderLocationIndex` from some function types
 
-## Version 4.5.0.11
-_14 January, 2023_
+## Version 4.5.0.11 _14 January, 2023_
 
 - Fixed some function types
 - Allowed omitting fragment/vertex shaders in `loadShader` functions
 
-## Version 4.5.0.10
-_5 January, 2023_
+## Version 4.5.0.10 _5 January, 2023_
 
 - Restructured to make the examples easier to run
 - Updated raylib to the master branch
 
-## Version 4.5.0.9
-_23 December, 2022_
+## Version 4.5.0.9 _23 December, 2022_
 
 - Changed `setConfigFlags` and `setGesturesEnabled` to use an array of flags
 
-## Version 4.5.0.8
-_18 December, 2022_
+## Version 4.5.0.8 _18 December, 2022_
 
-- \[[#9](https://github.com/Anut-py/h-raylib/issues/9)\] Fixed an issue on Mac where `clang` failed to detect that `rglfw.c` was using objective-c
+- \[[#9](https://github.com/Anut-py/h-raylib/issues/9)\] Fixed an issue on Mac
+  where `clang` failed to detect that `rglfw.c` was using objective-c
 
-## Version 4.5.0.7
-_26 November, 2022_
+## Version 4.5.0.7 _26 November, 2022_
 
 \[[#7](https://github.com/Anut-py/h-raylib/pull/7)\]
 
-- Removed all constants that were enums in the original C API and replaced them with sum types deriving `Enum`
+- Removed all constants that were enums in the original C API and replaced them
+  with sum types deriving `Enum`
 - Removed some `CInt` usage in the main API
 - Removed `Raylib.Constants`
 
-## Version 4.5.0.6
-_24 November, 2022_
+## Version 4.5.0.6 _24 November, 2022_
 
-- \[[#6](https://github.com/Anut-py/h-raylib/issues/6)\] Fixed `Font` marshalling
+- \[[#6](https://github.com/Anut-py/h-raylib/issues/6)\] Fixed `Font`
+  marshalling
 
-## Version 4.5.0.5
-_19 November, 2022_
+## Version 4.5.0.5 _19 November, 2022_
 - Replaced `CInt` with `CBool` in `RayCollision`
 - Updated raylib to the master branch
 
-## Version 4.5.0.4
-_13 November, 2022_
+## Version 4.5.0.4 _13 November, 2022_
 - Replaced `CInt` with `CBool` for functions that return booleans
 - Removed `Xext` dependency (it is no longer required for Nix builds)
diff --git a/default.nix b/default.nix
--- a/default.nix
+++ b/default.nix
@@ -3,7 +3,7 @@
 }:
 mkDerivation {
   pname = "h-raylib";
-  version = "5.5.3.1";
+  version = "5.6.0.0";
   src = ./.;
   isLibrary = true;
   isExecutable = buildExamples;
diff --git a/flake.nix b/flake.nix
--- a/flake.nix
+++ b/flake.nix
@@ -9,10 +9,10 @@
       forAllSystems' = nixpkgs.lib.genAttrs;
       forAllSystems = forAllSystems' supportedSystems;
 
-      raylibRev = "2a0963ce0936abe0cd3ec32882638d860e435d16";
-      raylibHash = "sha256-4p3nq04irS8AFojH88Bh1r8KiOjQhZf7nFmQhf1EDU8=";
-      rayguiRev = "1e03efca48c50c5ea4b4a053d5bf04bad58d3e43";
-      rayguiHash = "sha256-PzQZxCz63EPd7sVFBYY0T1s9jA5kOAxF9K4ojRoIMz4=";
+      raylibRev = "29896a24039fb687d6ede44c63a78dd3b5829f8b";
+      raylibHash = "sha256-eKquM7Dumd++eIYXBNKi8lhyGGgGt+PO0QPxcSux+/o=";
+      rayguiRev = "9a1c183d8539e2470635b22f92bdaefaf5218aaa";
+      rayguiHash = "sha256-eRcF41quNwlEn5lHVaoyn1wmppZ5Ou+M8c908qFvLek=";
 
       pkgsForSystem =
         system:
@@ -52,7 +52,7 @@
         with pkgs; (
           [raylib raygui]
           ++ lib.optionals stdenv.isLinux (with xorg; [libGL libX11 libXcursor libXext libXi libXinerama libXrandr])
-          ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [OpenGL Cocoa IOKit CoreVideo CoreAudio CoreFoundation])
+          ++ lib.optionals stdenv.isDarwin [apple-sdk]
         );
     in
       {
diff --git a/h-raylib.cabal b/h-raylib.cabal
--- a/h-raylib.cabal
+++ b/h-raylib.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               h-raylib
-version:            5.5.3.1
+version:            5.6.0.0
 synopsis:           Raylib bindings for Haskell
 category:           graphics
 description:
@@ -249,15 +249,15 @@
   if !flag(disable-lens)
     other-modules:
       Raylib.Util.Lenses.TH
-  
+
   build-depends:
-    , base              >=4.0      && <4.22
+    , base              >=4.0      && <4.23
     , bytestring        >=0.11.0   && <0.13
     , containers        >=0.6.0    && <0.9
     , template-haskell  >=2.16.0.0 && <2.24
     , text              >=2.0      && <2.2
-  
-  
+
+
   if !flag(disable-lens)
     build-depends:
       , linear            >=1.22     && <1.24
@@ -316,7 +316,7 @@
       Xext
 
     cc-options:
-      -Wno-implicit-function-declaration -Wno-unused-result -D_GLFW_X11
+      -Wno-implicit-function-declaration -Wno-unused-result -Wno-ignored-qualifiers -D_GLFW_X11
 
   elif (flag(platform-mac) || (flag(detect-platform) && os(osx)))
     frameworks: OpenGL Cocoa IOKit CoreVideo CoreAudio CoreFoundation
@@ -340,7 +340,7 @@
 
   else
     cc-options:
-      -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast -Wno-discarded-qualifiers
+      -DPLATFORM_DESKTOP_GLFW -Wno-int-to-void-pointer-cast -Wno-ignored-qualifiers
 
     c-sources:
       lib/rgui_bindings.c
@@ -364,7 +364,6 @@
         raylib/src/rshapes.c
         raylib/src/rtext.c
         raylib/src/rtextures.c
-        raylib/src/utils.c
 
       if (flag(platform-mac) || (flag(detect-platform) && os(osx)))
         -- Use rgflw.m instead of .c on Mac to force objective-c
@@ -378,6 +377,6 @@
 
   if flag(ghci)
     cpp-options: -DGHCI
-  
+
   if flag(disable-lens)
     cpp-options: -DDISABLE_LENS
diff --git a/lib/rgui_bindings.c b/lib/rgui_bindings.c
--- a/lib/rgui_bindings.c
+++ b/lib/rgui_bindings.c
@@ -178,6 +178,11 @@
   return GuiDrawIcon(iconId, posX, posY, pixelSize, *color);
 }
 
+RLBIND int GuiGetTextWidth_(char *a)
+{
+  return GuiGetTextWidth(a);
+}
+
 RLBIND int GuiWindowBox_(Rectangle *bounds, const char *title)
 {
   return GuiWindowBox(*bounds, title);
diff --git a/lib/rgui_bindings.h b/lib/rgui_bindings.h
--- a/lib/rgui_bindings.h
+++ b/lib/rgui_bindings.h
@@ -86,6 +86,8 @@
 
 void GuiDrawIcon_(int iconId, int posX, int posY, int pixelSize, Color *color);
 
+int GuiGetTextWidth_(char *a);
+
 int GuiWindowBox_(Rectangle *bounds, const char *title);
 
 int GuiGroupBox_(Rectangle *bounds, const char *text);
diff --git a/lib/rl_bindings.c b/lib/rl_bindings.c
--- a/lib/rl_bindings.c
+++ b/lib/rl_bindings.c
@@ -4,6 +4,7 @@
 
 #include "rl_bindings.h"
 #include <stdio.h>
+#include <string.h>
 
 RLBIND void SetWindowIcon_(Image *a)
 {
@@ -337,6 +338,11 @@
     DrawLineBezier(*a, *b, c, *d);
 }
 
+RLBIND void DrawLineDashed_(Vector2 *a, Vector2 *b, int c, int d, Color *e)
+{
+    DrawLineDashed(*a, *b, c, d, *e);
+}
+
 RLBIND void DrawCircle_(int a, int b, float c, Color *d)
 {
     DrawCircle(a, b, c, *d);
@@ -377,11 +383,21 @@
     DrawEllipse(a, b, c, d, *e);
 }
 
+RLBIND void DrawEllipseV_(Vector2 *a, float b, float c, Color *d)
+{
+    DrawEllipseV(*a, b, c, *d);
+}
+
 RLBIND void DrawEllipseLines_(int a, int b, float c, float d, Color *e)
 {
     DrawEllipseLines(a, b, c, d, *e);
 }
 
+RLBIND void DrawEllipseLinesV_(Vector2 *a, float b, float c, Color *d)
+{
+    DrawEllipseLinesV(*a, b, c, *d);
+}
+
 RLBIND void DrawRing_(Vector2 *a, float b, float c, float d, float e, int f, Color *g)
 {
     DrawRing(*a, b, c, d, e, f, *g);
@@ -1625,9 +1641,9 @@
     return IsModelAnimationValid(*a, *b);
 }
 
-RLBIND void UpdateModelAnimationBoneMatrices_(Model *a, ModelAnimation *b, int c)
+RLBIND void UpdateModelAnimationBones_(Model *a, ModelAnimation *b, int c)
 {
-    UpdateModelAnimationBoneMatrices(*a, *b, c);
+    UpdateModelAnimationBones(*a, *b, c);
 }
 
 RLBIND bool CheckCollisionSpheres_(Vector3 *a, float b, Vector3 *c, float d)
@@ -2193,6 +2209,13 @@
     return GetClipboardText();
 }
 
+RLBIND Image *GetClipboardImage_()
+{
+    Image *ptr = (Image *) malloc(sizeof(Image));
+    *ptr = GetClipboardImage();
+    return ptr;
+}
+
 RLBIND void EnableEventWaiting_()
 {
     EnableEventWaiting();
@@ -2464,6 +2487,36 @@
     return FileExists(a);
 }
 
+RLBIND int FileRename_(const char *a, const char *b)
+{
+    return FileRename(a, b);
+}
+
+RLBIND int FileRemove_(const char *a)
+{
+    return FileRemove(a);
+}
+
+RLBIND int FileCopy_(const char *a, const char *b)
+{
+    return FileCopy(a, b);
+}
+
+RLBIND int FileMove_(const char *a, const char *b)
+{
+    return FileMove(a, b);
+}
+
+RLBIND int FileTextReplace_(const char *a, const char *b, const char *c)
+{
+    return FileTextReplace(a, b, c);
+}
+
+RLBIND int FileTextFindIndex_(const char *a, const char *b)
+{
+    return FileTextFindIndex(a, b);
+}
+
 RLBIND bool DirectoryExists_(const char *a)
 {
     return DirectoryExists(a);
@@ -2559,7 +2612,7 @@
     return EncodeDataBase64(a, b, c);
 }
 
-RLBIND unsigned char *DecodeDataBase64_(const unsigned char *a, int *b)
+RLBIND unsigned char *DecodeDataBase64_(const char *a, int *b)
 {
     return DecodeDataBase64(a, b);
 }
@@ -2899,9 +2952,9 @@
     GenTextureMipmaps(a);
 }
 
-RLBIND GlyphInfo *LoadFontData_(const unsigned char *a, int b, int c, int *d, int e, int f)
+RLBIND GlyphInfo *LoadFontData_(const unsigned char *a, int b, int c, int *d, int e, int f, int *g)
 {
-    return LoadFontData(a, b, c, d, e, f);
+    return LoadFontData(a, b, c, d, e, f, g);
 }
 
 RLBIND void UnloadFontData_(GlyphInfo *a, int b)
diff --git a/lib/rl_bindings.h b/lib/rl_bindings.h
--- a/lib/rl_bindings.h
+++ b/lib/rl_bindings.h
@@ -124,6 +124,8 @@
 
 void DrawLineBezier_(Vector2 *a, Vector2 *b, float c, Color *d);
 
+void DrawLineDashed_(Vector2 *a, Vector2 *b, int c, int d, Color *e);
+
 void DrawCircle_(int a, int b, float c, Color *d);
 
 void DrawCircleSector_(Vector2 *a, float b, float c, float d, int e, Color *f);
@@ -140,8 +142,12 @@
 
 void DrawEllipse_(int a, int b, float c, float d, Color *e);
 
+void DrawEllipseV_(Vector2 *a, float b, float c, Color *d);
+
 void DrawEllipseLines_(int a, int b, float c, float d, Color *e);
 
+void DrawEllipseLinesV_(Vector2 *a, float b, float c, Color *d);
+
 void DrawRing_(Vector2 *a, float b, float c, float d, float e, int f, Color *g);
 
 void DrawRingLines_(Vector2 *a, float b, float c, float d, float e, int f, Color *g);
@@ -574,7 +580,7 @@
 
 bool IsModelAnimationValid_(Model *a, ModelAnimation *b);
 
-void UpdateModelAnimationBoneMatrices_(Model *a, ModelAnimation *b, int c);
+void UpdateModelAnimationBones_(Model *a, ModelAnimation *b, int c);
 
 bool CheckCollisionSpheres_(Vector3 *a, float b, Vector3 *c, float d);
 
@@ -790,6 +796,8 @@
 
 const char *GetClipboardText_();
 
+Image *GetClipboardImage_();
+
 void EnableEventWaiting_();
 
 void DisableEventWaiting_();
@@ -890,8 +898,18 @@
 
 bool FileExists_(const char *a);
 
-bool DirectoryExists_(const char *a);
+int FileRename_(const char *a, const char *b);
 
+int FileRemove_(const char *a);
+
+int FileCopy_(const char *a, const char *b);
+
+int FileMove_(const char *a, const char *b);
+
+int FileTextReplace_(const char *a, const char *b, const char *c);
+
+int FileTextFindIndex_(const char *a, const char *b);
+
 bool IsFileExtension_(const char *a, const char *b);
 
 int GetFileLength_(const char *a);
@@ -928,7 +946,7 @@
 
 char *EncodeDataBase64_(const unsigned char *a, int b, int *c);
 
-unsigned char *DecodeDataBase64_(const unsigned char *a, int *b);
+unsigned char *DecodeDataBase64_(const char *a, int *b);
 
 unsigned int ComputeCRC32_(unsigned char *a, int b);
 
@@ -1064,7 +1082,7 @@
 
 void GenTextureMipmaps_(Texture2D *a);
 
-GlyphInfo *LoadFontData_(const unsigned char *a, int b, int c, int *d, int e, int f);
+GlyphInfo *LoadFontData_(const unsigned char *a, int b, int c, int *d, int e, int f, int *g);
 
 void UnloadFontData_(GlyphInfo *a, int b);
 
diff --git a/lib/rl_common.h b/lib/rl_common.h
--- a/lib/rl_common.h
+++ b/lib/rl_common.h
@@ -1,7 +1,7 @@
 #include <raylib.h>
 #include <rlgl.h>
 #include <raygui.h>
-#include <config.h>
+// #include <config.h>
 #include <stdlib.h>
 
 #ifndef RLBIND
diff --git a/lib/rlgl_bindings.c b/lib/rlgl_bindings.c
--- a/lib/rlgl_bindings.c
+++ b/lib/rlgl_bindings.c
@@ -314,6 +314,11 @@
   rlLoadExtensions(a);
 }
 
+RLBIND void *rlGetProcAddress_(char *a)
+{
+  return rlGetProcAddress(a);
+}
+
 RLBIND int rlGetVersion_()
 {
   return rlGetVersion();
@@ -454,9 +459,9 @@
   return rlLoadTextureDepth(a, b, c);
 }
 
-RLBIND unsigned int rlLoadTextureCubemap_(const void *a, int b, int c)
+RLBIND unsigned int rlLoadTextureCubemap_(const void *a, int b, int c, int d)
 {
-  return rlLoadTextureCubemap(a, b, c);
+  return rlLoadTextureCubemap(a, b, c, d);
 }
 
 RLBIND void rlUpdateTexture_(unsigned int a, int b, int c, int d, int e, int f, const void *g)
@@ -509,6 +514,16 @@
   return rlFramebufferComplete(a);
 }
 
+RLBIND void rlCopyFramebuffer_(int a, int b, int c, int d, int e, void *f)
+{
+  return rlCopyFramebuffer(a, b, c, d, e, f);
+}
+
+RLBIND void rlResizeFramebuffer_(int a, int b)
+{
+  return rlResizeFramebuffer(a, b);
+}
+
 RLBIND void rlUnloadFramebuffer_(unsigned int a)
 {
   rlUnloadFramebuffer(a);
@@ -719,14 +734,29 @@
   rlDisableScissorTest();
 }
 
-RLBIND void rlEnableWireMode_()
+RLBIND void rlEnablePointMode_()
 {
-  rlEnableWireMode();
+  rlEnablePointMode();
 }
 
-RLBIND void rlEnablePointMode_()
+RLBIND void rlDisablePointMode_()
 {
-  rlEnablePointMode();
+  rlDisablePointMode();
+}
+
+RLBIND void rlSetPointSize_(float a)
+{
+  rlSetPointSize(a);
+}
+
+RLBIND float rlGetPointSize_()
+{
+  return rlGetPointSize();
+}
+
+RLBIND void rlEnableWireMode_()
+{
+  rlEnableWireMode();
 }
 
 RLBIND void rlDisableWireMode_()
diff --git a/lib/rlgl_bindings.h b/lib/rlgl_bindings.h
--- a/lib/rlgl_bindings.h
+++ b/lib/rlgl_bindings.h
@@ -126,6 +126,8 @@
 
 void rlLoadExtensions_(void *a);
 
+void *rlGetProcAddress_(char *a);
+
 int rlGetVersion_();
 
 void rlSetFramebufferWidth_(int a);
@@ -182,7 +184,7 @@
 
 unsigned int rlLoadTextureDepth_(int a, int b, bool c);
 
-unsigned int rlLoadTextureCubemap_(const void *a, int b, int c);
+unsigned int rlLoadTextureCubemap_(const void *a, int b, int c, int d);
 
 void rlUpdateTexture_(unsigned int a, int b, int c, int d, int e, int f, const void *g);
 
@@ -204,6 +206,10 @@
 
 bool rlFramebufferComplete_(unsigned int a);
 
+void rlCopyFramebuffer_(int a, int b, int c, int d, int e, void *f);
+
+void rlResizeFramebuffer_(int a, int b);
+
 void rlUnloadFramebuffer_(unsigned int a);
 
 unsigned int rlLoadShaderCode_(const char *a, const char *b);
@@ -288,9 +294,15 @@
 
 void rlDisableScissorTest_();
 
-void rlEnableWireMode_();
-
 void rlEnablePointMode_();
+
+void rlDisablePointMode_();
+
+void rlSetPointSize_(float a);
+
+float rlGetPointSize_();
+
+void rlEnableWireMode_();
 
 void rlDisableWireMode_();
 
diff --git a/raygui/src/raygui.h b/raygui/src/raygui.h
--- a/raygui/src/raygui.h
+++ b/raygui/src/raygui.h
@@ -4,7 +4,7 @@
 *
 *   DESCRIPTION:
 *       raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
-*       available as a standalone library, as long as input and drawing functions are provided.
+*       available as a standalone library, as long as input and drawing functions are provided
 *
 *   FEATURES:
 *       - Immediate-mode gui, minimal retained data
@@ -27,7 +27,7 @@
 *       - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
 *         font atlas recs and glyphs, freeing that memory is (usually) up to the user,
 *         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
-*         by default any previously loaded font (texture, recs, glyphs).
+*         by default any previously loaded font (texture, recs, glyphs)
 *       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
 *
 *   CONTROLS PROVIDED:
@@ -65,7 +65,7 @@
 *       - MessageBox    --> Window, Label, Button
 *       - TextInputBox  --> Window, Label, TextBox, Button
 *
-*     It also provides a set of functions for styling the controls based on its properties (size, color).
+*     It also provides a set of functions for styling the controls based on its properties (size, color)
 *
 *
 *   RAYGUI STYLE (guiStyle):
@@ -77,11 +77,11 @@
 *
 *           static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
 *
-*       guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+*       guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
 *
 *       Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
 *       used for all controls, when any of those base values is set, it is automatically populated to all
-*       controls, so, specific control values overwriting generic style should be set after base values.
+*       controls, so, specific control values overwriting generic style should be set after base values
 *
 *       After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
 *       properties are actually common to all controls and can not be overwritten individually (like BASE ones)
@@ -100,7 +100,7 @@
 *       Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
 *       requires 8 integers (16*16/32) to be stored in memory.
 *
-*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
 *
 *       The global icons array size is fixed and depends on the number of icons and size:
 *
@@ -112,20 +112,20 @@
 *
 *   RAYGUI LAYOUT:
 *       raygui currently does not provide an auto-layout mechanism like other libraries,
-*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it.
+*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
 *
 *       TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
 *
 *   CONFIGURATION:
 *       #define RAYGUI_IMPLEMENTATION
-*           Generates the implementation of the library into the included file.
+*           Generates the implementation of the library into the included file
 *           If not defined, the library is in header only mode and can be included in other headers
-*           or source files without problems. But only ONE file should hold the implementation.
+*           or source files without problems. But only ONE file should hold the implementation
 *
 *       #define RAYGUI_STANDALONE
 *           Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
 *           internally in the library and input management and drawing functions must be provided by
-*           the user (check library implementation for further details).
+*           the user (check library implementation for further details)
 *
 *       #define RAYGUI_NO_ICONS
 *           Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
@@ -141,12 +141,17 @@
 *           Draw text bounds rectangles for debug
 *
 *   VERSIONS HISTORY:
-*       4.5-dev (Sep-2024)    Current dev version...
+*       5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
 *                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
 *                         ADDED: GuiValueBoxFloat()
 *                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
 *                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: GuiLoadIconsFromMemory()
 *                         ADDED: Multiple new icons
+*                         REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+*                         REMOVED: GuiSliderPro(), functionality was redundant
+*                         REVIEWED: Controls using text labels to use LABEL properties
+*                         REVIEWED: Replaced sprintf() by snprintf() for more safety
 *                         REVIEWED: GuiTabBar(), close tab with mouse middle button
 *                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
 *                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
@@ -156,6 +161,8 @@
 *                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
 *                         REVIEWED: GuiDrawText(), improved wrap mode drawing
 *                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: GuiProgressBar(), improved borders computing
+*                         REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
 *                         REVIEWED: Functions descriptions, removed wrong return value reference
 *                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
 *
@@ -259,16 +266,16 @@
 *       1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
 *       1.3 (12-Jun-2017) Complete redesign of style system
 *       1.1 (01-Jun-2017) Complete review of the library
-*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
 *
 *   DEPENDENCIES:
-*       raylib 5.0  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+*       raylib 5.6-dev  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
 *
 *   STANDALONE MODE:
 *       By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
-*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs.
+*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
 *
 *       The following functions should be redefined for a custom backend:
 *
@@ -309,7 +316,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -334,7 +341,7 @@
 #define RAYGUI_VERSION_MAJOR 4
 #define RAYGUI_VERSION_MINOR 5
 #define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION  "4.5-dev"
+#define RAYGUI_VERSION  "5.0-dev"
 
 #if !defined(RAYGUI_STANDALONE)
     #include "raylib.h"
@@ -358,24 +365,13 @@
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-// Allow custom memory allocators
-#ifndef RAYGUI_MALLOC
-    #define RAYGUI_MALLOC(sz)       malloc(sz)
-#endif
-#ifndef RAYGUI_CALLOC
-    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
-#endif
-#ifndef RAYGUI_FREE
-    #define RAYGUI_FREE(p)          free(p)
-#endif
-
 // Simple log system to avoid printf() calls if required
 // NOTE: Avoiding those calls, also avoids const strings memory usage
 #define RAYGUI_SUPPORT_LOG_INFO
 #if defined(RAYGUI_SUPPORT_LOG_INFO)
-  #define RAYGUI_LOG(...)           printf(__VA_ARGS__)
+    #define RAYGUI_LOG(...)     printf(__VA_ARGS__)
 #else
-  #define RAYGUI_LOG(...)
+    #define RAYGUI_LOG(...)
 #endif
 
 //----------------------------------------------------------------------------------
@@ -421,14 +417,17 @@
 
     // TODO: Texture2D type is very coupled to raylib, required by Font type
     // It should be redesigned to be provided by user
-    typedef struct Texture2D {
+    typedef struct Texture {
         unsigned int id;        // OpenGL texture id
         int width;              // Texture base width
         int height;             // Texture base height
         int mipmaps;            // Mipmap levels, 1 by default
         int format;             // Data format (PixelFormat type)
-    } Texture2D;
+    } Texture;
 
+    // Texture2D, same as Texture
+    typedef Texture Texture2D;
+
     // Image, pixel data stored in CPU memory (RAM)
     typedef struct Image {
         void *data;             // Image raw data
@@ -527,7 +526,7 @@
     DROPDOWNBOX,
     TEXTBOX,        // Used also for: TEXTBOXMULTI
     VALUEBOX,
-    SPINNER,        // Uses: BUTTON, VALUEBOX
+    CONTROL11,
     LISTVIEW,
     COLORPICKER,
     SCROLLBAR,
@@ -549,12 +548,12 @@
     BORDER_COLOR_DISABLED,      // Control border color in STATE_DISABLED
     BASE_COLOR_DISABLED,        // Control base color in STATE_DISABLED
     TEXT_COLOR_DISABLED,        // Control text color in STATE_DISABLED
-    BORDER_WIDTH,               // Control border size, 0 for no border
+    BORDER_WIDTH = 12,          // Control border size, 0 for no border
     //TEXT_SIZE,                  // Control text size (glyphs max height) -> GLOBAL for all controls
     //TEXT_SPACING,               // Control text spacing between glyphs -> GLOBAL for all controls
-    //TEXT_LINE_SPACING           // Control text spacing between lines -> GLOBAL for all controls
-    TEXT_PADDING,               // Control text padding, not considering border
-    TEXT_ALIGNMENT,             // Control text horizontal alignment inside control text bound (after border and padding)
+    //TEXT_LINE_SPACING,          // Control text spacing between lines -> GLOBAL for all controls
+    TEXT_PADDING = 13,          // Control text padding, not considering border
+    TEXT_ALIGNMENT = 14,        // Control text horizontal alignment inside control text bound (after border and padding)
     //TEXT_WRAP_MODE              // Control text wrap-mode inside text bounds -> GLOBAL for all controls
 } GuiControlProperty;
 
@@ -641,18 +640,22 @@
     TEXT_READONLY = 16,         // TextBox in read-only mode: 0-text editable, 1-text no-editable
 } GuiTextBoxProperty;
 
-// Spinner
+// ValueBox/Spinner
 typedef enum {
-    SPIN_BUTTON_WIDTH = 16,     // Spinner left/right buttons width
-    SPIN_BUTTON_SPACING,        // Spinner buttons separation
-} GuiSpinnerProperty;
+    SPINNER_BUTTON_WIDTH = 16,  // Spinner left/right buttons width
+    SPINNER_BUTTON_SPACING,     // Spinner buttons separation
+} GuiValueBoxProperty;
 
+// Control11
+//typedef enum { } GuiControl11Property;
+
 // ListView
 typedef enum {
     LIST_ITEMS_HEIGHT = 16,     // ListView items height
     LIST_ITEMS_SPACING,         // ListView items separation
     SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
     SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_NORMAL,   // ListView items border enabled in normal state
     LIST_ITEMS_BORDER_WIDTH     // ListView items border width
 } GuiListViewProperty;
 
@@ -717,6 +720,9 @@
 RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
 #endif
 
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text);                // Get text width considering gui style and icon size (if required)
+
 // Controls
 //----------------------------------------------------------------------------------------------------------
 // Container/separator controls, useful for controls organization
@@ -999,33 +1005,33 @@
     ICON_MLAYERS                  = 226,
     ICON_MAPS                     = 227,
     ICON_HOT                      = 228,
-    ICON_229                      = 229,
-    ICON_230                      = 230,
-    ICON_231                      = 231,
-    ICON_232                      = 232,
-    ICON_233                      = 233,
-    ICON_234                      = 234,
-    ICON_235                      = 235,
-    ICON_236                      = 236,
-    ICON_237                      = 237,
-    ICON_238                      = 238,
-    ICON_239                      = 239,
-    ICON_240                      = 240,
-    ICON_241                      = 241,
-    ICON_242                      = 242,
-    ICON_243                      = 243,
-    ICON_244                      = 244,
-    ICON_245                      = 245,
-    ICON_246                      = 246,
-    ICON_247                      = 247,
-    ICON_248                      = 248,
-    ICON_249                      = 249,
+    ICON_LABEL                    = 229,
+    ICON_NAME_ID                  = 230,
+    ICON_SLICING                  = 231,
+    ICON_MANUAL_CONTROL           = 232,
+    ICON_COLLISION                = 233,
+    ICON_CIRCLE_ADD               = 234,
+    ICON_CIRCLE_ADD_FILL          = 235,
+    ICON_CIRCLE_WARNING           = 236,
+    ICON_CIRCLE_WARNING_FILL      = 237,
+    ICON_BOX_MORE                 = 238,
+    ICON_BOX_MORE_FILL            = 239,
+    ICON_BOX_MINUS                = 240,
+    ICON_BOX_MINUS_FILL           = 241,
+    ICON_UNION                    = 242,
+    ICON_INTERSECTION             = 243,
+    ICON_DIFFERENCE               = 244,
+    ICON_SPHERE                   = 245,
+    ICON_CYLINDER                 = 246,
+    ICON_CONE                     = 247,
+    ICON_ELLIPSOID                = 248,
+    ICON_CAPSULE                  = 249,
     ICON_250                      = 250,
     ICON_251                      = 251,
     ICON_252                      = 252,
     ICON_253                      = 253,
     ICON_254                      = 254,
-    ICON_255                      = 255,
+    ICON_255                      = 255
 } GuiIconName;
 #endif
 
@@ -1046,12 +1052,24 @@
 #if defined(RAYGUI_IMPLEMENTATION)
 
 #include <ctype.h>              // required for: isspace() [GuiTextBox()]
-#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
-#include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
 #include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
 #include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
 #include <math.h>               // Required for: roundf() [GuiColorPicker()]
 
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+        #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+    #endif
+#else
+    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+    #define RAYGUI_MALLOC(sz)       malloc(sz)
+    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
+    #define RAYGUI_FREE(p)          free(p)
+#endif
+
 #ifdef __cplusplus
     #define RAYGUI_CLITERAL(name) name
 #else
@@ -1060,7 +1078,7 @@
 
 // Check if two rectangles are equal, used to validate a slider bounds as an id
 #ifndef CHECK_BOUNDS_ID
-    #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
+    #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
 #endif
 
 #if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
@@ -1073,7 +1091,7 @@
 // Icons data is defined by bit array (every bit represents one pixel)
 // Those arrays are stored as unsigned int data arrays, so,
 // every array element defines 32 pixels (bits) of information
-// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels)
+// One icon is defined by 8 int, (8 int*32 bit = 256 bit = 16*16 pixels)
 // NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels)
 #define RAYGUI_ICON_DATA_ELEMENTS   (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32)
 
@@ -1318,27 +1336,27 @@
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
     0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_229
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_230
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_231
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_232
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_233
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_234
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_235
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_236
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_237
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_238
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_239
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_240
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_241
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_242
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_243
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_244
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_245
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_246
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_247
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_248
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_249
+    0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060,      // ICON_LABEL
+    0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000,      // ICON_NAME_ID
+    0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000,      // ICON_SLICING
+    0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0,      // ICON_MANUAL_CONTROL
+    0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe,      // ICON_COLLISION
+    0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_ADD
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_ADD_FILL
+    0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_WARNING
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_WARNING_FILL
+    0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE
+    0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE_FILL
+    0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS
+    0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS_FILL
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0,      // ICON_UNION
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_INTERSECTION
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_DIFFERENCE
+    0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0,      // ICON_SPHERE
+    0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0,      // ICON_CYLINDER
+    0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0,      // ICON_CONE
+    0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000,      // ICON_ELLIPSOID
+    0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000,      // ICON_CAPSULE
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_250
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_251
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_252
@@ -1363,7 +1381,7 @@
 #define RAYGUI_MAX_PROPS_EXTENDED        8      // Maximum number of extended properties
 
 //----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
 //----------------------------------------------------------------------------------
 // Gui control property style color element
 typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@@ -1387,8 +1405,7 @@
 
 static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
 //static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
-static int autoCursorCooldownCounter = 0;       // Cooldown frame counter for automatic cursor movement on key-down
-static int autoCursorDelayCounter = 0;          // Delay frame counter for automatic cursor movement
+static int autoCursorCounter = 0;               // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
 
 //----------------------------------------------------------------------------------
 // Style data array for all gui style properties (allocated on data segment by default)
@@ -1480,11 +1497,10 @@
 #endif      // RAYGUI_STANDALONE
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize);    // Load style from memory (binary only)
 
-static int GetTextWidth(const char *text);                      // Gui get text width using gui font and style
 static Rectangle GetTextBounds(int control, Rectangle bounds);  // Get text bounds considering control bounds
 static const char *GetTextIcon(const char *text, int *iconId);  // Get text icon if provided and move text cursor
 
@@ -1589,6 +1605,10 @@
         #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT        24
     #endif
 
+    #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+        #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT      18
+    #endif
+
     int result = 0;
     //GuiState state = guiState;
 
@@ -1597,9 +1617,10 @@
     Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
     if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
 
+    const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
     Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
-    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
-                                 statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+                                 statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
 
     // Update control
     //--------------------------------------------------------------------
@@ -1653,7 +1674,7 @@
 // Line control
 int GuiLine(Rectangle bounds, const char *text)
 {
-    #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+    #if !defined(RAYGUI_LINE_MARGIN_TEXT)
         #define RAYGUI_LINE_MARGIN_TEXT  12
     #endif
     #if !defined(RAYGUI_LINE_TEXT_PADDING)
@@ -1671,7 +1692,7 @@
     else
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = bounds.height;
         textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
         textBounds.y = bounds.y;
@@ -1711,8 +1732,8 @@
     //--------------------------------------------------------------------
     if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
 
-    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED: (int)LINE_COLOR)),
-                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
     //--------------------------------------------------------------------
 
     return result;
@@ -1722,7 +1743,7 @@
 // NOTE: Using GuiToggle() for the TABS
 int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
 {
-    #define RAYGUI_TABBAR_ITEM_WIDTH    160
+    #define RAYGUI_TABBAR_ITEM_WIDTH    148
 
     int result = -1;
     //GuiState state = guiState;
@@ -1755,12 +1776,12 @@
             if (i == (*active))
             {
                 toggle = true;
-                GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+                GuiToggle(tabBounds, text[i], &toggle);
             }
             else
             {
                 toggle = false;
-                GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+                GuiToggle(tabBounds, text[i], &toggle);
                 if (toggle) *active = i;
             }
 
@@ -2011,7 +2032,7 @@
     bool pressed = false;
 
     // NOTE: We force bounds.width to be all text
-    float textWidth = (float)GetTextWidth(text);
+    float textWidth = (float)GuiGetTextWidth(text);
     if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
 
     // Update control
@@ -2149,8 +2170,10 @@
 
     // Get substrings items from text (items pointers)
     int itemCount = 0;
-    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+    const char **items = NULL;
 
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
     Rectangle slider = {
         0,      // Calculated later depending on the active toggle
         bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
@@ -2196,7 +2219,7 @@
     if (text != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(text);
+        textBounds.width = (float)GuiGetTextWidth(text);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2221,7 +2244,7 @@
 
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2474,7 +2497,7 @@
 int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
 {
     #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
-        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  40        // Frames to wait for autocursor movement
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  20        // Frames to wait for autocursor movement
     #endif
     #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
         #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY      1        // Frames delay for autocursor movement
@@ -2483,15 +2506,15 @@
     int result = 0;
     GuiState state = guiState;
 
-    bool multiline = false;     // TODO: Consider multiline text input
+    bool multiline = false; // TODO: Consider multiline text input
     int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
 
     Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
-    int textLength = (int)strlen(text);     // Get current text length
+    int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
     int thisCursorIndex = textBoxCursorIndex;
     if (thisCursorIndex > textLength) thisCursorIndex = textLength;
-    int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
-    int textIndexOffset = 0;    // Text index offset to start drawing in the box
+    int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
+    int textIndexOffset = 0; // Text index offset to start drawing in the box
 
     // Cursor rectangle
     // NOTE: Position X value should be updated
@@ -2511,15 +2534,6 @@
     mouseCursor.x = -1;
     mouseCursor.width = 1;
 
-    // Auto-cursor movement logic
-    // NOTE: Cursor moves automatically when key down after some time
-    if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
-    else
-    {
-        autoCursorCooldownCounter = 0;      // GLOBAL: Cursor cooldown counter
-        autoCursorDelayCounter = 0;         // GLOBAL: Cursor delay counter
-    }
-
     // Blink-cursor frame counter
     //if (!autoCursorMode) blinkCursorFrameCounter++;
     //else blinkCursorFrameCounter = 0;
@@ -2537,6 +2551,13 @@
 
         if (editMode)
         {
+            // GLOBAL: Auto-cursor movement logic
+            // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+            if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+            else autoCursorCounter = 0;
+
+            bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
             state = STATE_PRESSED;
 
             if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
@@ -2550,7 +2571,7 @@
 
                 textIndexOffset += nextCodepointSize;
 
-                textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
+                textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
             }
 
             int codepoint = GetCharPressed();       // Get Unicode codepoint
@@ -2560,10 +2581,43 @@
             int codepointSize = 0;
             const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
 
-            // Add codepoint to text, at current cursor position
-            // NOTE: Make sure we do not overflow buffer size
-            if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            // Handle text paste action
+            if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
+                const char *pasteText = GetClipboardText();
+                if (pasteText != NULL)
+                {
+                    int pasteLength = 0;
+                    int pasteCodepoint;
+                    int pasteCodepointSize;
+
+                    // Count how many codepoints to copy, stopping at the first unwanted control character
+                    while (true)
+                    {
+                        pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+                        if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+                        if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+                        pasteLength += pasteCodepointSize;
+                    }
+
+                    if (pasteLength > 0)
+                    {
+                        // Move forward data from cursor position
+                        for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+                        // Paste data in at cursor
+                        for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+                        textBoxCursorIndex += pasteLength;
+                        textLength += pasteLength;
+                        text[textLength] = '\0';
+                    }
+                }
+            }
+            else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            {
+                // Adding codepoint to text, at current cursor position
+
                 // Move forward data from cursor position
                 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
 
@@ -2583,115 +2637,187 @@
             // Move cursor to end
             if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
 
-            // Delete codepoint from text, after current cursor position
-            if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+            // Delete related codepoints from text, after current cursor position
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                autoCursorDelayCounter++;
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
 
-                if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
                 {
-                    int nextCodepointSize = 0;
-                    GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
-
-                    // Move backward text from cursor position
-                    for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+                        break;
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
 
-                    textLength -= codepointSize;
-                    if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+                // Check whitespace to delete (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
 
-                    // Make sure text last character is EOL
-                    text[textLength] = '\0';
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
                 }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
             }
 
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, after current cursor position
+
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+                textLength -= nextCodepointSize;
+            }
+
             // Delete related codepoints from text, before current cursor position
-            if ((textLength > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                int i = textBoxCursorIndex - 1;
+                int offset = textBoxCursorIndex;
                 int accCodepointSize = 0;
+                int prevCodepointSize = 0;
+                int prevCodepoint = 0;
 
-                // Move cursor to the end of word if on space already
-                while ((i > 0) && isspace(text[i]))
+                // Check whitespace to delete (ASCII only)
+                while (offset > 0)
                 {
-                    int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + i, &prevCodepointSize);
-                    i -= prevCodepointSize;
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
                     accCodepointSize += prevCodepointSize;
                 }
 
-                // Move cursor to the start of the word
-                while ((i > 0) && !isspace(text[i]))
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
                 {
-                    int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + i, &prevCodepointSize);
-                    i -= prevCodepointSize;
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
                     accCodepointSize += prevCodepointSize;
                 }
 
-                // Move forward text from cursor position
-                for (int j = (textBoxCursorIndex - accCodepointSize); j < textLength; j++) text[j] = text[j + accCodepointSize];
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
 
-                // Prevent cursor index from decrementing past 0
-                if (textBoxCursorIndex > 0)
-                {
-                    textBoxCursorIndex -= accCodepointSize;
-                    textLength -= accCodepointSize;
-                }
+                textLength -= accCodepointSize;
+                textBoxCursorIndex -= accCodepointSize;
+            }
 
-                // Make sure text last character is EOL
-                text[textLength] = '\0';
-            } 
-            else if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
             {
-                autoCursorDelayCounter++;
-
-                if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
-                {
-                    int prevCodepointSize = 0;
+                // Delete single codepoint from text, before current cursor position
 
-                    // Prevent cursor index from decrementing past 0
-                    if (textBoxCursorIndex > 0)
-                    {
-                        GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                int prevCodepointSize = 0;
 
-                        // Move backward text from cursor position
-                        for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
 
-                        textBoxCursorIndex -= codepointSize;
-                        textLength -= codepointSize;
-                    }
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
 
-                    // Make sure text last character is EOL
-                    text[textLength] = '\0';
-                }
+                textLength -= prevCodepointSize;
+                textBoxCursorIndex -= prevCodepointSize;
             }
 
             // Move cursor position with keys
-            if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                autoCursorDelayCounter++;
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int prevCodepointSize = 0;
+                int prevCodepoint = 0;
 
-                if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                // Check whitespace to skip (ASCII only)
+                while (offset > 0)
                 {
-                    int prevCodepointSize = 0;
-                    if (textBoxCursorIndex > 0) GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
 
-                    if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
                 }
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                textBoxCursorIndex = offset;
             }
-            else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
             {
-                autoCursorDelayCounter++;
+                int prevCodepointSize = 0;
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
 
-                if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+            else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
                 {
-                    int nextCodepointSize = 0;
-                    GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
 
-                    if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
                 }
+
+                // Check whitespace to skip (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                textBoxCursorIndex = offset;
             }
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
 
+                textBoxCursorIndex += nextCodepointSize;
+            }
+
             // Move cursor position with mouse
             if (CheckCollisionPointRec(mousePosition, textBounds))     // Mouse hover text
             {
@@ -2701,7 +2827,7 @@
                 float widthToMouseX = 0;
                 int mouseCursorIndex = 0;
 
-                for (int i = textIndexOffset; i < textLength; i++)
+                for (int i = textIndexOffset; i < textLength; i += codepointSize)
                 {
                     codepoint = GetCodepointNext(&text[i], &codepointSize);
                     codepointIndex = GetGlyphIndex(guiFont, codepoint);
@@ -2720,7 +2846,7 @@
                 }
 
                 // Check if mouse cursor is at the last position
-                int textEndWidth = GetTextWidth(text + textIndexOffset);
+                int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
                 if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
                 {
                     mouseCursor.x = textBounds.x + textEndWidth;
@@ -2737,7 +2863,7 @@
             else mouseCursor.x = -1;
 
             // Recalculate cursor position.y depending on textBoxCursorIndex
-            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
             //if (multiline) cursor.y = GetTextLines()
 
             // Finish text editing on ENTER or mouse click outside bounds
@@ -2745,6 +2871,7 @@
                 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
             {
                 textBoxCursorIndex = 0;     // GLOBAL: Reset the shared cursor index
+                autoCursorCounter = 0;      // GLOBAL: Reset counter for repeated keystrokes
                 result = 1;
             }
         }
@@ -2757,6 +2884,7 @@
                 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
                 {
                     textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
+                    autoCursorCounter = 0;             // GLOBAL: Reset counter for repeated keystrokes
                     result = 1;
                 }
             }
@@ -2825,19 +2953,22 @@
 
     int tempValue = *value;
 
-    Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
-                          bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
-    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
-    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle valueBoxBounds = {
+        bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+        bounds.y,
+        bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+        (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
 
     Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-        textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
-        if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
     }
 
     // Update control
@@ -2871,20 +3002,20 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+    result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
 
     // Draw value selector custom buttons
     // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
     int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
     int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
-    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
     GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
 
     GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
     GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
 
     // Draw text label if provided
-    GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     //--------------------------------------------------------------------
 
     *value = tempValue;
@@ -2902,13 +3033,13 @@
     int result = 0;
     GuiState state = guiState;
 
-    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
-    sprintf(textValue, "%i", *value);
+    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+    snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
 
     Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2920,7 +3051,6 @@
     if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
-
         bool valueHasChanged = false;
 
         if (editMode)
@@ -2929,32 +3059,55 @@
 
             int keyCount = (int)strlen(textValue);
 
-            // Only allow keys in range [48..57]
-            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
             {
-                if (GetTextWidth(textValue) < bounds.width)
+                if (textValue[0] == '-')
                 {
-                    int key = GetCharPressed();
-                    if ((key >= 48) && (key <= 57))
+                    for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+                {
+                    if (keyCount == 0)
                     {
-                        textValue[keyCount] = (char)key;
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
                         keyCount++;
-                        valueHasChanged = true;
                     }
+
+                    for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
                 }
             }
 
-            // Delete text
-            if (keyCount > 0)
+            // Add new digit to text value
+            if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
             {
-                if (IsKeyPressed(KEY_BACKSPACE))
+                int key = GetCharPressed();
+
+                // Only allow keys in range [48..57]
+                if ((key >= 48) && (key <= 57))
                 {
-                    keyCount--;
-                    textValue[keyCount] = '\0';
+                    textValue[keyCount] = (char)key;
+                    keyCount++;
                     valueHasChanged = true;
                 }
             }
 
+            // Delete text
+            if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
+            {
+                keyCount--;
+                textValue[keyCount] = '\0';
+                valueHasChanged = true;
+            }
+
             if (valueHasChanged) *value = TextToInteger(textValue);
 
             // NOTE: We are not clamp values until user input finishes
@@ -2992,11 +3145,14 @@
     GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
     GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
 
-    // Draw cursor
+    // Draw cursor rectangle
     if (editMode)
     {
         // NOTE: ValueBox internal text is always centered
-        Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+        Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+            bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+            2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+        if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
         GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
     }
 
@@ -3019,12 +3175,12 @@
     GuiState state = guiState;
 
     //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
-    //sprintf(textValue, "%2.2f", *value);
+    //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
 
-    Rectangle textBounds = {0};
+    Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -3045,10 +3201,37 @@
 
             int keyCount = (int)strlen(textValue);
 
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
             // Only allow keys in range [48..57]
             if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
             {
-                if (GetTextWidth(textValue) < bounds.width)
+                if (GuiGetTextWidth(textValue) < bounds.width)
                 {
                     int key = GetCharPressed();
                     if (((key >= 48) && (key <= 57)) ||
@@ -3103,7 +3286,7 @@
     if (editMode)
     {
         // NOTE: ValueBox internal text is always centered
-        Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+        Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
                             bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
                             bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
         GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
@@ -3120,7 +3303,7 @@
 
 // Slider control with pro parameters
 // NOTE: Other GuiSlider*() controls use this one
-int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
 {
     int result = 0;
     GuiState state = guiState;
@@ -3129,6 +3312,8 @@
     if (value == NULL) value = &temp;
     float oldValue = *value;
 
+    int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
     Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
                          0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
 
@@ -3146,7 +3331,7 @@
                 {
                     state = STATE_PRESSED;
                     // Get equivalent value and slider position from mousePosition.x
-                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
                 }
             }
             else
@@ -3166,7 +3351,7 @@
                 if (!CheckCollisionPointRec(mousePoint, slider))
                 {
                     // Get equivalent value and slider position from mousePosition.x
-                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
                 }
             }
             else state = STATE_FOCUSED;
@@ -3205,44 +3390,45 @@
     if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
     else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
     else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+    else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
 
     // Draw left/right text if provided
     if (textLeft != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textLeft);
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
 
     if (textRight != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textRight);
+        textBounds.width = (float)GuiGetTextWidth(textRight);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
     //--------------------------------------------------------------------
 
     return result;
 }
 
-// Slider control extended, returns selected value and has text
-int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
-{
-    return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
-}
-
 // Slider Bar control extended, returns selected value
 int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
 {
-    return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+    int result = 0;
+    int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+    result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+    return result;
 }
 
 // Progress Bar control extended, shows current progress value
@@ -3257,14 +3443,14 @@
     // Progress bar
     Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
                            bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
-                           bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+                           bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
 
     // Update control
     //--------------------------------------------------------------------
     if (*value > maxValue) *value = maxValue;
 
     // WARNING: Working with floats could lead to rounding issues
-    if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue)? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f);
+    if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
     //--------------------------------------------------------------------
 
     // Draw control
@@ -3282,15 +3468,15 @@
             GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
             GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
         }
-        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
 
-        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
         else
         {
             // Draw borders not yet reached by value
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
         }
 
         // Draw slider internal progress bar (depends on state)
@@ -3301,23 +3487,23 @@
     if (textLeft != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textLeft);
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
 
     if (textRight != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textRight);
+        textBounds.width = (float)GuiGetTextWidth(textRight);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
     //--------------------------------------------------------------------
 
@@ -3467,11 +3653,11 @@
     // Draw visible items
     for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
     {
-        GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+        if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
 
         if (state == STATE_DISABLED)
         {
-            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
 
             GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
         }
@@ -3480,18 +3666,18 @@
             if (((startIndex + i) == itemSelected) && (active != NULL))
             {
                 // Draw item selected
-                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
             }
             else if (((startIndex + i) == itemFocused)) // && (focus != NULL))  // NOTE: We want items focused, despite not returned!
             {
                 // Draw item focused
-                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
             }
             else
             {
-                // Draw item normal
+                // Draw item normal (no rectangle)
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
             }
         }
@@ -3531,22 +3717,22 @@
     return result;
 }
 
-// Color Panel control - Color (RGBA) variant.
+// Color Panel control - Color (RGBA) variant
 int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
 {
     int result = 0;
 
     Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
     Vector3 hsv = ConvertRGBtoHSV(vcolor);
-    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
 
     GuiColorPanelHSV(bounds, text, &hsv);
 
-    // Check if the hsv was changed, only then change the color.
-    // This is required, because the Color->HSV->Color conversion has precision errors.
-    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
-    // Otherwise GuiColorPanel would often modify it's color without user input.
-    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+    // Check if the hsv was changed, only then change the color
+    // This is required, because the Color->HSV->Color conversion has precision errors
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+    // Otherwise GuiColorPanel would often modify it's color without user input
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
     if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
     {
         Vector3 rgb = ConvertHSVtoRGB(hsv);
@@ -3570,7 +3756,10 @@
 
     int result = 0;
     GuiState state = guiState;
-    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+        (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+        (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+        (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
 
     // Update control
     //--------------------------------------------------------------------
@@ -3617,7 +3806,6 @@
 
     // Draw control
     //--------------------------------------------------------------------
-
     // Draw alpha bar: checked background
     if (state != STATE_DISABLED)
     {
@@ -3755,7 +3943,7 @@
     Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
     //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
 
-    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
     Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
 
     GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3768,8 +3956,8 @@
     return result;
 }
 
-// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering.
-// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB.
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
 // NOTE: It's divided in multiple controls:
 //      int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
 //      int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
@@ -3917,7 +4105,7 @@
     buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
     buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
 
-    //int textWidth = GetTextWidth(message) + 2;
+    //int textWidth = GuiGetTextWidth(message) + 2;
 
     Rectangle textBounds = { 0 };
     textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -3981,7 +4169,7 @@
     Rectangle textBounds = { 0 };
     if (message != NULL)
     {
-        int textSize = GetTextWidth(message) + 2;
+        int textSize = GuiGetTextWidth(message) + 2;
 
         textBounds.x = bounds.x + bounds.width/2 - textSize/2;
         textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -4043,7 +4231,7 @@
 // Grid control
 // NOTE: Returns grid mouse-hover selected cell
 // About drawing lines at subpixel spacing, simple put, not easy solution:
-// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
+// REF: https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
 int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell)
 {
     // Grid lines alpha amount
@@ -4221,7 +4409,7 @@
 
             if (fileDataSize > 0)
             {
-                unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char));
+                unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
                 fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
 
                 GuiLoadStyleFromMemory(fileData, fileDataSize);
@@ -4283,8 +4471,6 @@
     GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
     GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
     GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
-    GuiSetStyle(SPINNER, TEXT_PADDING, 0);
-    GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
     GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
     GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
 
@@ -4299,8 +4485,8 @@
     GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
     GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
     GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
-    GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
-    GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
     GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
     GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
     GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
@@ -4310,6 +4496,7 @@
     GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
     GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
     GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
     GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
     GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
     GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
@@ -4322,8 +4509,8 @@
     {
         // Unload previous font texture
         UnloadTexture(guiFont.texture);
-        RL_FREE(guiFont.recs);
-        RL_FREE(guiFont.glyphs);
+        RAYGUI_FREE(guiFont.recs);
+        RAYGUI_FREE(guiFont.glyphs);
         guiFont.recs = NULL;
         guiFont.glyphs = NULL;
 
@@ -4352,7 +4539,7 @@
     if (text != NULL)
     {
         memset(buffer, 0, 1024);
-        sprintf(buffer, "#%03i#", iconId);
+        snprintf(buffer, 1024, "#%03i#", iconId);
 
         for (int i = 5; i < 1024; i++)
         {
@@ -4364,7 +4551,7 @@
     }
     else
     {
-        sprintf(iconBuffer, "#%03i#", iconId);
+        snprintf(iconBuffer, 16, "#%03i#", iconId);
 
         return iconBuffer;
     }
@@ -4430,17 +4617,17 @@
         {
             if (loadIconsName)
             {
-                guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+                guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
                 for (int i = 0; i < iconCount; i++)
                 {
-                    guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+                    guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
                     fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
                 }
             }
             else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
 
             // Read icons data directly over internal icons array
-            fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+            fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
         }
 
         fclose(rgiFile);
@@ -4449,6 +4636,56 @@
     return guiIconsName;
 }
 
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+    char **guiIconsName = NULL;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    short iconCount = 0;
+    short iconSize = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+    memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'I') &&
+        (signature[3] == ' '))
+    {
+        if (loadIconsName)
+        {
+            guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+            for (int i = 0; i < iconCount; i++)
+            {
+                guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+                fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+            }
+        }
+        else
+        {
+            // Skip icon name data if not required
+            fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+        }
+
+        int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+        guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+        memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+    }
+
+    return guiIconsName;
+}
+
 // Draw selected icon using rectangles pixel-by-pixel
 void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
 {
@@ -4476,12 +4713,73 @@
     if (scale >= 1) guiIconScale = scale;
 }
 
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    Vector2 textSize = { 0 };
+    int textIconOffset = 0;
+
+    if ((text != NULL) && (text[0] != '\0'))
+    {
+        if (text[0] == '#')
+        {
+            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+            {
+                if (text[i] == '#')
+                {
+                    textIconOffset = i;
+                    break;
+                }
+            }
+        }
+
+        text += textIconOffset;
+
+        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+        // Custom MeasureText() implementation
+        if ((guiFont.texture.id > 0) && (text != NULL))
+        {
+            // Get size in bytes of text, considering end of line and line break
+            int size = 0;
+            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+            {
+                if ((text[i] != '\0') && (text[i] != '\n')) size++;
+                else break;
+            }
+
+            float scaleFactor = fontSize/(float)guiFont.baseSize;
+            textSize.y = (float)guiFont.baseSize*scaleFactor;
+            float glyphWidth = 0.0f;
+
+            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+            {
+                int codepoint = GetCodepointNext(&text[i], &codepointSize);
+                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+    }
+
+    return (int)textSize.x;
+}
+
 #endif      // !RAYGUI_NO_ICONS
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
-
 // Load style from memory
 // WARNING: Binary files only
 static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
@@ -4567,7 +4865,7 @@
             {
                 // Compressed font atlas image data (DEFLATE), it requires DecompressData()
                 int dataUncompSize = 0;
-                unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+                unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
                 memcpy(compData, fileDataPtr, fontImageCompSize);
                 fileDataPtr += fontImageCompSize;
 
@@ -4581,7 +4879,7 @@
             else
             {
                 // Font atlas image data is not compressed
-                imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+                imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
                 memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
                 fileDataPtr += fontImageUncompSize;
             }
@@ -4609,7 +4907,7 @@
                 if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
                 {
                     // Recs data is compressed, uncompress it
-                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize);
+                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
 
                     memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
                     fileDataPtr += recsDataCompressedSize;
@@ -4651,7 +4949,7 @@
                 if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
                 {
                     // Glyphs data is compressed, uncompress it
-                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize);
+                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
 
                     memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
                     fileDataPtr += glyphsDataCompressedSize;
@@ -4704,68 +5002,6 @@
     }
 }
 
-// Gui get text width considering icon
-static int GetTextWidth(const char *text)
-{
-    #if !defined(ICON_TEXT_PADDING)
-        #define ICON_TEXT_PADDING   4
-    #endif
-
-    Vector2 textSize = { 0 };
-    int textIconOffset = 0;
-
-    if ((text != NULL) && (text[0] != '\0'))
-    {
-        if (text[0] == '#')
-        {
-            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
-            {
-                if (text[i] == '#')
-                {
-                    textIconOffset = i;
-                    break;
-                }
-            }
-        }
-
-        text += textIconOffset;
-
-        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
-        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-
-        // Custom MeasureText() implementation
-        if ((guiFont.texture.id > 0) && (text != NULL))
-        {
-            // Get size in bytes of text, considering end of line and line break
-            int size = 0;
-            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
-            {
-                if ((text[i] != '\0') && (text[i] != '\n')) size++;
-                else break;
-            }
-
-            float scaleFactor = fontSize/(float)guiFont.baseSize;
-            textSize.y = (float)guiFont.baseSize*scaleFactor;
-            float glyphWidth = 0.0f;
-
-            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
-            {
-                int codepoint = GetCodepointNext(&text[i], &codepointSize);
-                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
-
-                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
-                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
-
-                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
-            }
-        }
-
-        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
-    }
-
-    return (int)textSize.x;
-}
-
 // Get text bounds considering control bounds
 static Rectangle GetTextBounds(int control, Rectangle bounds)
 {
@@ -4786,7 +5022,7 @@
         case SLIDER:
         case CHECKBOX:
         case VALUEBOX:
-        case SPINNER:
+        case CONTROL11:
             // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
         default:
         {
@@ -4832,35 +5068,29 @@
 }
 
 // Get text divided into lines (by line-breaks '\n')
-const char **GetTextLines(const char *text, int *count)
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
 {
     #define RAYGUI_MAX_TEXT_LINES   128
 
     static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
     for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL;    // Init NULL pointers to substrings
 
-    int textSize = (int)strlen(text);
+    int textLength = (int)strlen(text);
 
     lines[0] = text;
-    int len = 0;
     *count = 1;
-    //int lineSize = 0;   // Stores current line size, not returned
 
-    for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
+    for (int i = 0, k = 0; (i < textLength) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
     {
         if (text[i] == '\n')
         {
-            //lineSize = len;
             k++;
-            lines[k] = &text[i + 1];     // WARNING: next value is valid?
-            len = 0;
+            lines[k] = &text[i + 1]; // WARNING: next value is valid?
             *count += 1;
         }
-        else len++;
     }
 
-    //lines[*count - 1].size = len;
-
     return lines;
 }
 
@@ -4936,8 +5166,8 @@
         float textBoundsWidthOffset = 0.0f;
 
         // NOTE: We get text size after icon has been processed
-        // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
-        int textSizeX = GetTextWidth(lines[i]);
+        // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+        int textSizeX = GuiGetTextWidth(lines[i]);
 
         // If text requires an icon, add size to measure
         if (iconId >= 0)
@@ -5000,7 +5230,7 @@
         float textOffsetX = 0.0f;
         float glyphWidth = 0;
 
-        int ellipsisWidth = GetTextWidth("...");
+        int ellipsisWidth = GuiGetTextWidth("...");
         bool textOverflow = false;
         for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
         {
@@ -5144,13 +5374,13 @@
 
         if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
 
-        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
 
         int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
         int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
         GuiSetStyle(LABEL, TEXT_PADDING, 0);
         GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
-        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
         GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
         GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
     }
@@ -5204,7 +5434,7 @@
             buffer[i] = '\0';   // Set an end of string at this point
 
             counter++;
-            if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
         }
     }
 
@@ -5526,10 +5756,10 @@
 {
     Color color;
 
-    color.r = (unsigned char)(hexValue >> 24) & 0xFF;
-    color.g = (unsigned char)(hexValue >> 16) & 0xFF;
-    color.b = (unsigned char)(hexValue >> 8) & 0xFF;
-    color.a = (unsigned char)hexValue & 0xFF;
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
 
     return color;
 }
@@ -5562,7 +5792,7 @@
 
     va_list args;
     va_start(args, text);
-    vsprintf(buffer, text, args);
+    vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
     va_end(args);
 
     return buffer;
@@ -5636,7 +5866,7 @@
         text++;
     }
 
-    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
+    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
 
     return value*sign;
 }
@@ -5731,7 +5961,7 @@
     }
     else if (0xe0 == (0xf0 & ptr[0]))
     {
-        // 3 byte UTF-8 codepoint */
+        // 3 byte UTF-8 codepoint
         if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
         codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
         *codepointSize = 3;
diff --git a/raygui/styles/amber/style_amber.h b/raygui/styles/amber/style_amber.h
--- a/raygui/styles/amber/style_amber.h
+++ b/raygui/styles/amber/style_amber.h
@@ -7,11 +7,11 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
-#define AMBER_STYLE_PROPS_COUNT  16
+#define AMBER_STYLE_PROPS_COUNT  18
 
 // Custom style name: Amber
 static const GuiStyleProp amberStyleProps[AMBER_STYLE_PROPS_COUNT] = {
@@ -23,7 +23,7 @@
     { 0, 5, (int)0xffffffff },    // DEFAULT_TEXT_COLOR_FOCUSED 
     { 0, 6, (int)0xf1cf9dff },    // DEFAULT_BORDER_COLOR_PRESSED 
     { 0, 7, (int)0xf39333ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, (int)0x282020ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 8, (int)0x191410ff },    // DEFAULT_TEXT_COLOR_PRESSED 
     { 0, 9, (int)0x6a6a6aff },    // DEFAULT_BORDER_COLOR_DISABLED 
     { 0, 10, (int)0x818181ff },    // DEFAULT_BASE_COLOR_DISABLED 
     { 0, 11, (int)0x606060ff },    // DEFAULT_TEXT_COLOR_DISABLED 
@@ -31,6 +31,8 @@
     { 0, 18, (int)0xef922aff },    // DEFAULT_LINE_COLOR 
     { 0, 19, (int)0x333333ff },    // DEFAULT_BACKGROUND_COLOR 
     { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 1, 8, (int)0xe7e0d4ff },    // LABEL_TEXT_COLOR_PRESSED 
+    { 4, 8, (int)0xf1cf9dff },    // SLIDER_TEXT_COLOR_PRESSED 
 };
 
 // WARNING: This style uses a custom font: "hello-world.ttf" (size: 16, spacing: 1)
diff --git a/raygui/styles/ashes/style_ashes.h b/raygui/styles/ashes/style_ashes.h
--- a/raygui/styles/ashes/style_ashes.h
+++ b/raygui/styles/ashes/style_ashes.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,133 +15,120 @@
 
 // Custom style name: Ashes
 static const GuiStyleProp ashesStyleProps[ASHES_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0xf0f0f0ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x868686ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xe6e6e6ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0x929999ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xeaeaeaff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0x98a1a8ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0x3f3f3fff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xf6f6f6ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x414141ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x8b8b8bff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x777777ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x959595ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 18, 0x9dadb1ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x6b6b6bff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0xf0f0f0ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x868686ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xe6e6e6ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0x929999ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xeaeaeaff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0x98a1a8ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x3f3f3fff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xf6f6f6ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x414141ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x8b8b8bff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x777777ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x959595ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 18, (int)0x9dadb1ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x6b6b6bff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
 // WARNING: This style uses a custom font: "v5loxical.ttf" (size: 16, spacing: 1)
 
-#define ASHES_STYLE_FONT_ATLAS_COMP_SIZE 2042
+#define ASHES_STYLE_FONT_ATLAS_COMP_SIZE 1800
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char ashesFontData[ASHES_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0xd1, 0xb2, 0xab, 0x36, 0x0c, 0x05, 0x50, 0xff, 0xff, 0x4f, 0xab, 0x0f, 0x9d, 0x4e, 0xdb, 0x69, 0x13, 0x90, 0x2c,
-    0x83, 0x21, 0xeb, 0xae, 0xb7, 0x9c, 0xdc, 0x40, 0x0c, 0x1b, 0x63, 0x88, 0x45, 0x0c, 0x00, 0x00, 0x00, 0xe0, 0xe7, 0xc5,
-    0xff, 0xbe, 0x12, 0x1f, 0xdf, 0x19, 0xa7, 0x3f, 0xe7, 0xdf, 0xaf, 0xc7, 0x87, 0xbf, 0xe6, 0x3e, 0x2f, 0x4e, 0x2f, 0xb7,
-    0xf2, 0x97, 0xf8, 0xdf, 0xf5, 0x8b, 0xe9, 0xa5, 0xc6, 0xe9, 0x6f, 0xf3, 0xed, 0xbd, 0xdf, 0x96, 0xf9, 0xe9, 0x93, 0x22,
-    0xb1, 0x8c, 0xec, 0xfb, 0x3f, 0x7f, 0xce, 0xd5, 0x2d, 0x1e, 0x5f, 0xfe, 0xf2, 0xfd, 0xaf, 0xd9, 0xd7, 0xe3, 0xe4, 0xab,
-    0xdf, 0xfe, 0x12, 0x07, 0x7f, 0x39, 0x9b, 0xbb, 0x78, 0x40, 0xfe, 0xe3, 0xcb, 0xe7, 0x7d, 0xff, 0xc4, 0x98, 0x38, 0x26,
-    0x9c, 0x5b, 0xee, 0x95, 0xed, 0xf9, 0x69, 0x4f, 0xcf, 0x6d, 0xeb, 0xef, 0x6d, 0x9c, 0xcb, 0x5c, 0x34, 0xac, 0xff, 0x6c,
-    0xab, 0x45, 0xc3, 0xbb, 0xe3, 0xc4, 0xde, 0x92, 0xd9, 0x9b, 0xb3, 0x69, 0xab, 0xbd, 0x7f, 0x34, 0x6c, 0xaf, 0x6b, 0xf3,
-    0xff, 0xcf, 0x7f, 0x23, 0x75, 0x14, 0x1c, 0x4d, 0xf9, 0xcf, 0xf7, 0xb9, 0xdf, 0x8e, 0xbe, 0x71, 0xd1, 0xd9, 0xd4, 0xb9,
-    0x3d, 0xb4, 0xfe, 0x7a, 0xdf, 0xa7, 0x5f, 0x9d, 0xff, 0x68, 0xfc, 0x1f, 0x47, 0x6b, 0x17, 0xf2, 0x3f, 0xbd, 0x15, 0xa2,
-    0xd8, 0xdb, 0x56, 0x7b, 0xa2, 0xd9, 0xad, 0x1f, 0x8b, 0x7b, 0xfa, 0x55, 0x09, 0xad, 0x1d, 0xcd, 0x22, 0xf9, 0x7f, 0x72,
-    0x7d, 0x6c, 0x4c, 0x6f, 0xbd, 0xf3, 0xfb, 0x44, 0x7e, 0xfd, 0x8e, 0xf7, 0x00, 0xf9, 0xbf, 0x22, 0xff, 0x95, 0xe4, 0xc6,
-    0x92, 0xa3, 0x7f, 0xf5, 0xca, 0xc0, 0x8a, 0xb6, 0xcc, 0xe5, 0x3f, 0x1a, 0x5b, 0x20, 0xdb, 0x6f, 0xe7, 0xd7, 0xff, 0xec,
-    0x48, 0x3d, 0x6e, 0xec, 0xff, 0xbf, 0x1d, 0x1d, 0x33, 0x47, 0xcd, 0x6c, 0x3a, 0x73, 0xcb, 0xfd, 0x7b, 0x5d, 0xe7, 0xaf,
-    0x88, 0xac, 0xc8, 0x7f, 0xf5, 0xfc, 0xbf, 0x72, 0x86, 0x3a, 0x36, 0xc9, 0xf3, 0xf5, 0xf9, 0x8f, 0xf6, 0x16, 0x58, 0x99,
-    0xff, 0xb3, 0x6d, 0x1f, 0x0b, 0xc7, 0xff, 0xb9, 0xab, 0xab, 0xfb, 0xf7, 0xff, 0xf7, 0x5f, 0xff, 0x1b, 0xe5, 0xeb, 0x46,
-    0xd9, 0x6b, 0x80, 0x71, 0xea, 0xec, 0xe1, 0x29, 0xf9, 0xef, 0x38, 0xfe, 0x55, 0x47, 0x4a, 0x1d, 0x57, 0xb8, 0xb2, 0xf9,
-    0x8f, 0x86, 0xb6, 0x8f, 0xa5, 0x47, 0xff, 0x4a, 0xaf, 0x24, 0xff, 0xbd, 0xe3, 0xff, 0x38, 0x71, 0x06, 0x18, 0xaf, 0xe8,
-    0xff, 0xf3, 0xa3, 0x9c, 0x68, 0xb8, 0x07, 0x57, 0xb9, 0xbb, 0x9a, 0xed, 0x31, 0x2b, 0xf7, 0x24, 0x77, 0x68, 0x77, 0xfd,
-    0xff, 0x75, 0xdb, 0xa0, 0xb6, 0x2f, 0xfc, 0xf5, 0xff, 0x9e, 0xdf, 0xff, 0xef, 0xb4, 0x35, 0x90, 0xff, 0xab, 0xf7, 0xdb,
-    0xb8, 0xb5, 0x7f, 0x95, 0x7f, 0xbf, 0x60, 0xeb, 0xca, 0xff, 0xda, 0xdf, 0xff, 0xc4, 0x41, 0x6f, 0x38, 0x1e, 0xd9, 0xff,
-    0x5f, 0xb3, 0x36, 0x21, 0x79, 0xe0, 0x58, 0x2f, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0xfc, 0xd0, 0xec, 0x99, 0xcc, 0xec, 0xe5, 0x6a, 0x15, 0xe0, 0x8e, 0xf7, 0x57, 0x97, 0x10, 0x6d, 0xf5, 0x1f, 0xfb,
-    0xaa, 0x49, 0x7e, 0x9e, 0x4d, 0x9a, 0x99, 0xf7, 0x9c, 0xab, 0x6f, 0x9e, 0xab, 0x24, 0x17, 0xc9, 0x9a, 0x6d, 0x91, 0xac,
-    0x5b, 0x9c, 0xdd, 0x8e, 0xf3, 0xb3, 0xec, 0xe7, 0xfe, 0xff, 0xdf, 0xb5, 0x2a, 0xfa, 0xf6, 0xe5, 0xd1, 0xd4, 0x32, 0xd7,
-    0xe5, 0x7f, 0xb4, 0xd7, 0x54, 0xcd, 0x57, 0xa8, 0xca, 0xd7, 0xc7, 0x1f, 0x9b, 0xe5, 0x3f, 0xb3, 0x3f, 0x66, 0xeb, 0xfe,
-    0x44, 0x61, 0x56, 0x64, 0x4c, 0xcf, 0xb7, 0x8c, 0x85, 0xfb, 0x5d, 0x57, 0x05, 0xc9, 0x31, 0x99, 0xff, 0x3b, 0xf7, 0xfd,
-    0x4a, 0x6d, 0x46, 0xf9, 0x7f, 0x43, 0xfe, 0x2b, 0xd5, 0x3d, 0x3b, 0xe6, 0x45, 0x77, 0xd5, 0xc7, 0x8d, 0xf4, 0x79, 0x51,
-    0x66, 0x7f, 0x88, 0xa9, 0xca, 0xea, 0x91, 0x3e, 0x0e, 0xef, 0x98, 0x7f, 0xfd, 0x7f, 0xa5, 0x36, 0xde, 0xdd, 0xf9, 0x9f,
-    0x7b, 0x22, 0xc0, 0xea, 0xea, 0x07, 0x6b, 0xfb, 0xa7, 0xde, 0xfe, 0x3f, 0x26, 0xf2, 0x9f, 0x7d, 0x86, 0xd9, 0x5e, 0xf9,
-    0x9f, 0x3f, 0xff, 0xaf, 0x57, 0x3b, 0xef, 0x1a, 0x05, 0x8f, 0x86, 0xe3, 0x7d, 0xf5, 0x49, 0x82, 0x5d, 0xf9, 0x1f, 0x85,
-    0xa7, 0xc0, 0xcd, 0x9e, 0x8f, 0x3e, 0x3b, 0xff, 0xb9, 0x2b, 0x11, 0xeb, 0xf2, 0xff, 0xec, 0xfe, 0x7f, 0xbc, 0xe0, 0xfc,
-    0xbf, 0x63, 0x7b, 0x8f, 0x4d, 0xcf, 0xff, 0xbb, 0x46, 0xd1, 0xef, 0xcb, 0x7f, 0xf6, 0x7b, 0x76, 0x7c, 0xfe, 0x5b, 0xcf,
-    0xff, 0xe5, 0xff, 0xed, 0xf9, 0xbf, 0xeb, 0x49, 0x66, 0x7b, 0xe4, 0x3f, 0x9a, 0xaa, 0xef, 0xca, 0xff, 0x5b, 0xf3, 0xbf,
-    0xeb, 0xf5, 0xbf, 0xe7, 0xe6, 0x7f, 0x94, 0xc6, 0x66, 0x73, 0xef, 0x7e, 0x42, 0xff, 0x3f, 0x0a, 0xd7, 0xac, 0xf2, 0x23,
-    0xd9, 0xeb, 0xc6, 0xff, 0xf2, 0x7f, 0xee, 0xee, 0xd9, 0xdd, 0xf9, 0x5f, 0x73, 0xff, 0xab, 0xef, 0xbc, 0xa0, 0xe3, 0xdd,
-    0x7b, 0x8f, 0xff, 0x57, 0xff, 0x16, 0x67, 0xed, 0xf2, 0x7b, 0xae, 0xb4, 0x45, 0xf9, 0xba, 0x7d, 0xcf, 0x6f, 0x49, 0xf2,
-    0xd7, 0xdf, 0xb2, 0x47, 0xce, 0x38, 0x78, 0x7f, 0x34, 0xf5, 0x73, 0x95, 0xea, 0xf0, 0x3b, 0xe4, 0x3f, 0xfb, 0xd4, 0x9f,
-    0xec, 0xbb, 0xf7, 0xc8, 0xff, 0xfa, 0x96, 0xbd, 0x3a, 0xff, 0x4f, 0xfc, 0xb5, 0xe2, 0xef, 0xb5, 0x42, 0x2c, 0x7e, 0xff,
-    0x6f, 0xef, 0x0b, 0x7b, 0x3d, 0x31, 0xcb, 0x76, 0xe5, 0xca, 0xbe, 0x15, 0xad, 0x05, 0x8e, 0xac, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x9b, 0xfb, 0x70, 0x34, 0x7f, 0x32, 0x9a, 0xaa, 0x20, 0x64, 0xeb, 0xc0,
-    0x8d, 0xd3, 0xb5, 0xf0, 0xa3, 0x54, 0x79, 0x76, 0xbe, 0xba, 0xc2, 0xf7, 0x19, 0xcd, 0x9f, 0xbf, 0x69, 0x14, 0x66, 0x09,
-    0x67, 0x9f, 0x6d, 0x50, 0xa9, 0xad, 0x50, 0x5f, 0x56, 0x14, 0xab, 0x48, 0xce, 0xb7, 0x41, 0x14, 0x9e, 0xef, 0x70, 0xf4,
-    0x5d, 0xf2, 0x5b, 0x21, 0xf3, 0x5a, 0x65, 0x6e, 0xf9, 0x38, 0xf1, 0xdc, 0x81, 0x38, 0x9c, 0xdf, 0xd0, 0x9b, 0xff, 0xd5,
-    0x75, 0xb0, 0x2b, 0xb5, 0x3c, 0xfe, 0xfb, 0xda, 0x7d, 0x75, 0x27, 0xbe, 0x1d, 0x77, 0x2a, 0x55, 0x28, 0xb2, 0xc7, 0xe1,
-    0xfc, 0x5e, 0x38, 0xb7, 0xac, 0x91, 0xda, 0x02, 0x95, 0x7a, 0xe6, 0x9d, 0x6d, 0x1a, 0x85, 0x9a, 0x17, 0xe7, 0x7b, 0x9f,
-    0x28, 0xb6, 0xfd, 0x4c, 0xcd, 0x9d, 0x33, 0x33, 0x8a, 0xa3, 0xb0, 0xe7, 0x75, 0xd5, 0xc1, 0x8d, 0x96, 0x4c, 0x77, 0x54,
-    0xc1, 0xca, 0xd5, 0xee, 0xcd, 0xce, 0x24, 0x8d, 0xc9, 0xfd, 0x38, 0xbf, 0x7f, 0x47, 0x4b, 0x35, 0xae, 0xe3, 0x65, 0xc5,
-    0x97, 0x65, 0x65, 0xf2, 0x5f, 0xef, 0x4b, 0xd7, 0xe7, 0x3f, 0xbf, 0x06, 0x99, 0x0a, 0x52, 0x73, 0xbd, 0xe3, 0x7c, 0xfe,
-    0x77, 0xed, 0xff, 0xef, 0xce, 0x7f, 0xbe, 0x06, 0xcc, 0xd1, 0x79, 0x77, 0x65, 0x2f, 0x8a, 0x52, 0x26, 0xb3, 0xfb, 0x60,
-    0xbd, 0xff, 0x8f, 0xd2, 0x39, 0x55, 0xe7, 0xb6, 0xbd, 0x22, 0xff, 0x63, 0x61, 0xfe, 0xef, 0xee, 0xff, 0xf7, 0xcc, 0x7f,
-    0x5f, 0x2d, 0xdf, 0xb9, 0xfc, 0xe7, 0x53, 0x3e, 0x8a, 0x6b, 0xde, 0x77, 0xfe, 0x1f, 0xe9, 0xf1, 0xd3, 0xcc, 0xb2, 0xa2,
-    0xe5, 0x5c, 0x6b, 0x1c, 0x3c, 0x9f, 0xf0, 0xf9, 0xf9, 0x1f, 0xc5, 0x33, 0xe0, 0xa3, 0xa7, 0x61, 0xe4, 0xdb, 0x67, 0x76,
-    0x84, 0xbb, 0xbe, 0x0a, 0xf2, 0xf5, 0xcf, 0xc1, 0x88, 0x86, 0xb3, 0xfc, 0xa3, 0xca, 0x85, 0xb1, 0x41, 0xfe, 0xeb, 0xd7,
-    0x87, 0xbb, 0xfa, 0xff, 0xee, 0xeb, 0xd6, 0x3d, 0xf9, 0x1f, 0xa5, 0x71, 0x4c, 0x5f, 0x3b, 0x8f, 0xa9, 0xfe, 0x7f, 0xbc,
-    0x2c, 0xff, 0xd7, 0x3f, 0x07, 0x23, 0x5a, 0x46, 0xf9, 0x51, 0x7c, 0xde, 0x50, 0xef, 0xf9, 0xff, 0xb7, 0xe7, 0xe2, 0x46,
-    0xf9, 0xd9, 0x70, 0x6b, 0xf3, 0x7f, 0xee, 0x6a, 0xf5, 0xca, 0xfc, 0x0f, 0xf9, 0x3f, 0x79, 0x87, 0xeb, 0xe8, 0x5e, 0xc5,
-    0x48, 0xdf, 0x75, 0x99, 0xbd, 0x77, 0x74, 0xe5, 0xf9, 0xff, 0x75, 0xfd, 0xff, 0x58, 0x72, 0xfd, 0x7f, 0x45, 0x3f, 0xfb,
-    0xa4, 0xfe, 0xff, 0xfb, 0x9e, 0xfd, 0x9c, 0xfc, 0x9f, 0x49, 0xdc, 0x78, 0x44, 0x45, 0xd1, 0xae, 0x4a, 0xd0, 0xd7, 0x5e,
-    0xff, 0xeb, 0xbe, 0x9f, 0xd1, 0x73, 0xfe, 0x9f, 0xef, 0x49, 0xe7, 0x46, 0xb9, 0x9d, 0xcb, 0xba, 0xee, 0xfc, 0xff, 0x1d,
-    0xf9, 0xef, 0x59, 0xbb, 0xfb, 0x2b, 0x20, 0xee, 0x99, 0xff, 0x51, 0xfc, 0x7d, 0xcd, 0xbd, 0xe7, 0xff, 0x57, 0xe6, 0x6c,
-    0x8f, 0x7d, 0xe6, 0xbd, 0xf9, 0x3f, 0xfa, 0xfd, 0xcf, 0x13, 0xb7, 0xdb, 0xec, 0xd1, 0x27, 0x16, 0xbd, 0xf7, 0xae, 0xb3,
-    0xa3, 0x98, 0xba, 0x56, 0x21, 0xff, 0x9d, 0xf9, 0x7f, 0x53, 0xc2, 0x9e, 0x92, 0xff, 0x55, 0x47, 0x8c, 0xa7, 0x3c, 0x19,
-    0x42, 0xfe, 0xaf, 0xcd, 0xff, 0xaf, 0x24, 0xec, 0xbd, 0xf9, 0x7f, 0xdf, 0xd1, 0x2c, 0x3b, 0x86, 0x8e, 0x0b, 0xc7, 0xdd,
-    0x71, 0xe3, 0x18, 0xbf, 0xba, 0x5e, 0xd7, 0xac, 0xf3, 0xae, 0x2d, 0xf3, 0x84, 0xb5, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x67, 0xb9, 0x44, 0xcb, 0x6c, 0xd9, 0x51, 0xaa, 0xc3, 0x9e, 0x9f, 0x0b, 0x99, 0xad,
-    0x35, 0x3f, 0x5a, 0xea, 0x14, 0x57, 0xaa, 0xc5, 0x1e, 0xcf, 0x37, 0x8d, 0x64, 0x5b, 0xde, 0xb5, 0xad, 0xa2, 0xbc, 0xf4,
-    0x95, 0xdb, 0xe4, 0xf3, 0xac, 0xa0, 0xfc, 0x93, 0x16, 0x2a, 0xd5, 0x33, 0xae, 0xcb, 0xc1, 0xea, 0xd9, 0xfc, 0xf9, 0xba,
-    0xfc, 0xf3, 0x55, 0x4f, 0xfb, 0x9e, 0x14, 0xd0, 0xfd, 0x2d, 0x32, 0x75, 0x5e, 0xcf, 0xb5, 0x42, 0x34, 0xb5, 0xe5, 0x5d,
-    0xdb, 0xea, 0x68, 0x8d, 0x62, 0xf9, 0xda, 0x76, 0xd5, 0xeb, 0x8e, 0x52, 0x3f, 0x78, 0x47, 0x0e, 0x2a, 0xfd, 0xd1, 0xfd,
-    0xb3, 0x5c, 0xe7, 0xe7, 0xf7, 0xd6, 0xf3, 0xff, 0xd4, 0xf3, 0xb1, 0x67, 0x7c, 0xa7, 0xef, 0xb5, 0xb4, 0x76, 0xfa, 0x0e,
-    0xd5, 0xfc, 0x77, 0x54, 0x97, 0xe9, 0x9c, 0xe7, 0x1e, 0x5f, 0x6a, 0x2e, 0x47, 0xeb, 0xf9, 0xff, 0x4e, 0xaf, 0x57, 0xf3,
-    0x9f, 0x1f, 0x2f, 0xcc, 0x8e, 0x2f, 0xe6, 0x9f, 0x50, 0x94, 0xaf, 0xde, 0x79, 0xdf, 0xeb, 0xdd, 0xcf, 0x48, 0x5a, 0xfd,
-    0x7a, 0x25, 0xff, 0xbb, 0xf6, 0x0d, 0x3d, 0xf9, 0x8f, 0x0d, 0xb7, 0x52, 0x7e, 0xf4, 0x36, 0x7f, 0x9e, 0x59, 0x3d, 0xbe,
-    0x74, 0xe7, 0x7f, 0xc7, 0xb6, 0xcf, 0x8f, 0xff, 0x9f, 0x97, 0xff, 0x28, 0xf4, 0xc3, 0xf7, 0xf5, 0x83, 0x99, 0xa7, 0x58,
-    0xd6, 0xc7, 0xf8, 0xfb, 0xef, 0x83, 0xf2, 0x7f, 0x77, 0xdb, 0x8f, 0x1f, 0xe8, 0xff, 0xf7, 0xd9, 0xef, 0xba, 0xc7, 0xc1,
-    0xf2, 0x2f, 0xff, 0xf2, 0x1f, 0x27, 0xc6, 0x63, 0x7b, 0xbc, 0x7e, 0x5d, 0xfe, 0x73, 0x95, 0xf9, 0xd7, 0xbf, 0x5e, 0xcb,
-    0xed, 0xda, 0x6d, 0x24, 0xff, 0xfb, 0xe5, 0x7f, 0xe6, 0x89, 0x61, 0x4f, 0x68, 0xf3, 0xde, 0xfb, 0x7f, 0xcf, 0x1a, 0x6b,
-    0xea, 0xff, 0xe5, 0xbf, 0x7e, 0xff, 0xff, 0x1d, 0xf9, 0xef, 0xbe, 0x97, 0xf7, 0x94, 0xeb, 0x7f, 0xe3, 0xe0, 0xb9, 0x40,
-    0x71, 0xcb, 0x73, 0xc4, 0xd6, 0xe4, 0xff, 0x19, 0xdb, 0xe4, 0x78, 0xaf, 0x7a, 0x52, 0xfe, 0x9f, 0xdf, 0xe6, 0x9d, 0xf7,
-    0x25, 0xdf, 0xf1, 0xbd, 0x9f, 0x99, 0xff, 0x37, 0x6c, 0x93, 0xa3, 0x11, 0xd3, 0x9e, 0xfd, 0xff, 0xf3, 0x73, 0x10, 0x8b,
-    0x7f, 0x83, 0xb2, 0x6f, 0xfe, 0xc7, 0x6b, 0xfa, 0xff, 0xe7, 0x6f, 0x93, 0xb8, 0xf1, 0x5b, 0xf4, 0xe6, 0x7f, 0xb7, 0xfb,
-    0x7f, 0x77, 0xff, 0x06, 0xed, 0x69, 0xe7, 0x3d, 0xcf, 0xfb, 0xfd, 0x4f, 0x6d, 0x4c, 0xfd, 0xa4, 0x7d, 0xf1, 0xae, 0x3b,
-    0xe7, 0x2a, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0xd6, 0x55, 0xdf, 0x3c, 0x8a,
-    0x33, 0xab, 0xce, 0xbd, 0x56, 0xa9, 0xe6, 0x1e, 0xcb, 0xbe, 0x6b, 0xa5, 0x36, 0x4b, 0xb4, 0xcd, 0x5d, 0xea, 0xda, 0x32,
-    0x9f, 0x97, 0x1b, 0xc5, 0x2a, 0x2c, 0xbc, 0xeb, 0x08, 0xb0, 0xaa, 0x42, 0xfe, 0x68, 0xa8, 0x92, 0x5f, 0xab, 0xe6, 0x78,
-    0x4f, 0x2d, 0xf7, 0xb9, 0x99, 0xb2, 0xeb, 0xb6, 0x4c, 0x6f, 0x9d, 0x7c, 0xde, 0x75, 0x04, 0x58, 0xf9, 0x1c, 0x91, 0x68,
-    0xfc, 0xcc, 0xd8, 0xea, 0xbb, 0x8e, 0xaf, 0x33, 0xcf, 0x46, 0xa2, 0xf2, 0xc4, 0xfa, 0xb5, 0x8d, 0xd6, 0x96, 0x85, 0x3b,
-    0x8e, 0x59, 0xcf, 0xde, 0x4b, 0xf7, 0xeb, 0x51, 0xe5, 0xff, 0xd7, 0x92, 0xb4, 0x7a, 0x8e, 0xf8, 0x6c, 0x5d, 0x8e, 0xfc,
-    0x59, 0xea, 0x7e, 0x35, 0x1b, 0xa2, 0x30, 0xa7, 0x7f, 0x78, 0x4e, 0x06, 0x37, 0xf5, 0x3f, 0x9d, 0x75, 0x3b, 0xae, 0xcf,
-    0xff, 0x5e, 0xaf, 0xd7, 0xfa, 0x7f, 0xf9, 0x47, 0xfe, 0xe5, 0x5f, 0xfe, 0x91, 0xff, 0xf7, 0xe6, 0x3f, 0xe4, 0x9f, 0x5b,
-    0x47, 0xff, 0x2b, 0x9f, 0xdb, 0x21, 0xff, 0x73, 0x77, 0xff, 0xe4, 0x9f, 0x3b, 0xee, 0xfe, 0xf7, 0x55, 0xc8, 0x95, 0xff,
-    0xda, 0xaf, 0xaf, 0xe4, 0x1f, 0xf9, 0x7f, 0x7b, 0xff, 0x2f, 0xff, 0xb8, 0xff, 0x77, 0x4d, 0xfe, 0xd5, 0xc9, 0xce, 0xfd,
-    0x4a, 0x3a, 0xf7, 0x24, 0x44, 0xb8, 0xfe, 0xba, 0x85, 0xbd, 0xb1, 0xff, 0xb7, 0x99, 0x47, 0x2d, 0xab, 0xc5, 0xe1, 0x17,
-    0xe6, 0x7f, 0xc9, 0x3f, 0xfc, 0xda, 0xc8, 0xaf, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xe3, 0xcf, 0x7f, 0xda, 0x01, 0xe4, 0x1f, 0xf8, 0xb9, 0xfc, 0xff,
-    0x01 };
+    0x9d, 0x51, 0xb6, 0xa4, 0x36, 0x0c, 0x44, 0xbd, 0xff, 0x4d, 0x57, 0xbe, 0x72, 0x32, 0xc9, 0x49, 0x83, 0x25, 0x97, 0xb0,
+    0x0c, 0x77, 0xee, 0x5f, 0xbf, 0x1e, 0x1a, 0x5c, 0xc8, 0x06, 0xbb, 0x24, 0x6b, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfd,
+    0xef, 0x27, 0xfa, 0xf9, 0x4d, 0x4d, 0x1f, 0xe7, 0xdf, 0x9f, 0xeb, 0xc7, 0x5f, 0x63, 0xc7, 0xd3, 0xf4, 0xef, 0x66, 0xfe,
+    0xa2, 0xff, 0x3d, 0x3f, 0x2d, 0xff, 0xaa, 0xa6, 0xaf, 0xe6, 0xea, 0xbb, 0x57, 0xbf, 0xa9, 0xa6, 0xfa, 0xeb, 0xe2, 0x78,
+    0xd7, 0x47, 0xd4, 0xc2, 0x3d, 0x31, 0xf7, 0xbb, 0xf3, 0x9a, 0xae, 0xb7, 0xe7, 0xaf, 0x7b, 0x6b, 0xfe, 0xbb, 0xf7, 0x6d,
+    0x5c, 0xab, 0xff, 0x9f, 0xff, 0xc6, 0x8f, 0xbf, 0xe6, 0x34, 0xae, 0x89, 0xf4, 0xeb, 0x33, 0x95, 0x49, 0xed, 0xf9, 0xbb,
+    0x56, 0x4b, 0xfd, 0x50, 0xe6, 0xe8, 0xf9, 0x33, 0x1e, 0x41, 0xb5, 0x74, 0x73, 0x17, 0x2a, 0x7c, 0xc4, 0xd5, 0xfe, 0x3c,
+    0x13, 0xff, 0x5d, 0xf4, 0xcf, 0xdd, 0xcd, 0x6a, 0xa0, 0x7f, 0x46, 0x39, 0x19, 0xce, 0x54, 0xa6, 0xf1, 0xbf, 0x42, 0xfd,
+    0xa8, 0xfe, 0x32, 0xb6, 0x80, 0x53, 0xff, 0x6c, 0xff, 0x9f, 0xe9, 0xff, 0x46, 0x13, 0x3d, 0x9f, 0xd7, 0x5f, 0x8f, 0xb7,
+    0x80, 0x23, 0xfe, 0xc7, 0x6d, 0x6f, 0xab, 0x9b, 0xa7, 0x1a, 0xbd, 0x40, 0x7f, 0xc7, 0xfd, 0x9f, 0x1d, 0x29, 0xfb, 0x8e,
+    0xff, 0x9a, 0xe8, 0xeb, 0xf4, 0x8a, 0xf8, 0x8f, 0x8f, 0x72, 0xf3, 0xef, 0x9b, 0x77, 0x23, 0xa5, 0xb6, 0x5f, 0x7b, 0xee,
+    0x09, 0xe4, 0xef, 0xff, 0x77, 0x7e, 0xfc, 0x77, 0x19, 0x83, 0x76, 0x9d, 0xb7, 0xb6, 0xc6, 0x17, 0xfa, 0x7f, 0xe1, 0xfa,
+    0x45, 0xcb, 0x73, 0x97, 0xa1, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xca, 0x59,
+    0x10, 0x77, 0x22, 0x68, 0xd9, 0xe3, 0xe6, 0xf1, 0xdf, 0x2b, 0xdc, 0x02, 0x19, 0xdf, 0xfd, 0xaf, 0xf3, 0x8a, 0x7d, 0x7f,
+    0x84, 0x3d, 0x75, 0x4a, 0xae, 0x83, 0x29, 0xed, 0xea, 0x5a, 0x77, 0xa8, 0x57, 0xac, 0x20, 0xe7, 0xbd, 0x76, 0x11, 0x8f,
+    0xae, 0x4c, 0x3e, 0x2d, 0x99, 0x73, 0x54, 0xdc, 0xfa, 0x8f, 0xed, 0xfa, 0xab, 0xd4, 0x0f, 0xa2, 0x1b, 0xbf, 0xd9, 0x17,
+    0xf4, 0xcf, 0x3a, 0x77, 0x23, 0xfd, 0xbc, 0xec, 0xde, 0xc9, 0xf8, 0xf9, 0xdd, 0xe7, 0x03, 0x7c, 0x51, 0xff, 0x11, 0x8e,
+    0xdb, 0x78, 0x0f, 0x3b, 0x3b, 0x52, 0x6b, 0x63, 0xfc, 0x5f, 0x8d, 0xdb, 0x0a, 0x44, 0x4d, 0x54, 0x9d, 0xd8, 0xef, 0xfe,
+    0x73, 0xae, 0xeb, 0x4f, 0x44, 0x4f, 0xe9, 0x3f, 0x16, 0x7d, 0x9c, 0xb2, 0xf5, 0x18, 0xd9, 0x71, 0xae, 0x5f, 0xfc, 0xbb,
+    0x9f, 0xff, 0xea, 0xf4, 0x97, 0xc1, 0x8d, 0xa5, 0x52, 0x2f, 0x68, 0x26, 0x47, 0xe5, 0x3d, 0xfa, 0x67, 0xde, 0x70, 0x3c,
+    0xc7, 0x39, 0xc1, 0x87, 0xfa, 0x05, 0xfd, 0x01, 0xfd, 0xf1, 0xa1, 0x67, 0x9e, 0xdb, 0xea, 0xe6, 0x7f, 0x14, 0xee, 0xaf,
+    0xd1, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xeb, 0xdb, 0xd9, 0x2a,
+    0xe0, 0x8e, 0xef, 0x67, 0x7f, 0x41, 0x36, 0xc7, 0xa7, 0xcf, 0x3f, 0xfa, 0x7b, 0xfd, 0xd8, 0x91, 0xeb, 0x50, 0xa7, 0xff,
+    0xb0, 0xbb, 0x68, 0xe3, 0x9e, 0xb4, 0x78, 0x7d, 0xfc, 0xd1, 0x4c, 0xff, 0x11, 0x68, 0x77, 0x15, 0xe6, 0x4c, 0xa0, 0x7f,
+    0x7f, 0xfd, 0x7d, 0x95, 0x2f, 0xdf, 0xae, 0xff, 0x38, 0x4a, 0xff, 0xb5, 0x1d, 0x01, 0x5c, 0xfa, 0xe7, 0xfd, 0xed, 0xae,
+    0x51, 0x70, 0x3d, 0xc7, 0x60, 0xa4, 0x77, 0x12, 0x72, 0xe9, 0x3f, 0x12, 0x79, 0x7f, 0x0e, 0x6f, 0xfc, 0x1b, 0xe2, 0x7f,
+    0x18, 0xe2, 0x7f, 0x34, 0xed, 0xff, 0x87, 0xc9, 0x0f, 0x8a, 0xfe, 0xdf, 0xd6, 0xdf, 0x53, 0xfb, 0xf8, 0xbd, 0xfa, 0x77,
+    0x7d, 0xfe, 0x43, 0xff, 0x13, 0xf5, 0x97, 0xed, 0xca, 0x7d, 0xfa, 0xaf, 0x66, 0x4d, 0x7a, 0x9e, 0xb4, 0x94, 0x7e, 0x6e,
+    0x57, 0x38, 0x57, 0x2b, 0x9e, 0x7d, 0xea, 0xc8, 0x14, 0xbc, 0xab, 0x10, 0x11, 0x9b, 0x4d, 0x8a, 0x56, 0xd7, 0xa8, 0xd3,
+    0xff, 0xcc, 0x8a, 0x25, 0xcc, 0xd0, 0x56, 0x64, 0xbb, 0xc0, 0x3b, 0xee, 0xf9, 0x2f, 0x46, 0x08, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xad, 0x84, 0xc7, 0x56, 0x23, 0x5d, 0x6b, 0xb9, 0xc3, 0xb2, 0xbb, 0x80,
+    0x0c, 0xeb, 0xeb, 0x6b, 0xff, 0x5f, 0x7f, 0xac, 0x0c, 0xbb, 0x72, 0x1c, 0x1c, 0x9f, 0xcf, 0x3a, 0x1b, 0x32, 0xeb, 0x8b,
+    0x55, 0x8e, 0x06, 0x25, 0x7c, 0xd1, 0x15, 0xeb, 0xe2, 0x0a, 0xaf, 0xc1, 0xef, 0xf2, 0x44, 0x2b, 0x78, 0x45, 0xb2, 0xac,
+    0x51, 0x3a, 0x9c, 0xc4, 0x5a, 0x76, 0x0a, 0xc7, 0x7d, 0xc2, 0x2a, 0xb8, 0x0f, 0xcf, 0xd0, 0xdf, 0x3b, 0x86, 0x54, 0x45,
+    0xae, 0x37, 0xfe, 0xb5, 0xa0, 0x7f, 0x74, 0x37, 0x1b, 0xf4, 0xf7, 0xe8, 0x1f, 0x7b, 0x12, 0xa9, 0xd3, 0x9f, 0xf8, 0xdf,
+    0x17, 0xff, 0xab, 0x1e, 0x3e, 0xf4, 0xff, 0x86, 0xfe, 0x32, 0xd5, 0xe1, 0x46, 0xff, 0x75, 0x8f, 0x5a, 0xcc, 0x01, 0xeb,
+    0x72, 0x0a, 0xf7, 0x89, 0xff, 0x61, 0xf3, 0x44, 0xfb, 0x2a, 0x88, 0x77, 0xe8, 0x17, 0x1c, 0xdf, 0xee, 0x3d, 0xfe, 0x3f,
+    0xe9, 0x00, 0xee, 0xec, 0xf7, 0xac, 0xf4, 0xae, 0x76, 0xd1, 0xbf, 0x5e, 0x81, 0x73, 0xf5, 0x3f, 0x37, 0x47, 0xa0, 0x53,
+    0xab, 0xa2, 0x7f, 0xff, 0xac, 0x0b, 0xa0, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8,
+    0x3c, 0xd3, 0x9d, 0xf3, 0x4f, 0xc8, 0xe4, 0x82, 0x88, 0xfa, 0x00, 0xc7, 0x74, 0x8d, 0x7c, 0xa5, 0x9c, 0xc7, 0xab, 0xbe,
+    0x93, 0xbb, 0x56, 0x96, 0x71, 0x45, 0x42, 0x97, 0xad, 0x54, 0xeb, 0xcf, 0xda, 0x55, 0x1f, 0x3d, 0x53, 0xe3, 0x71, 0x25,
+    0x0b, 0xc2, 0xed, 0x3b, 0x90, 0xcd, 0x19, 0xa3, 0xe0, 0xfa, 0xa5, 0xcb, 0x87, 0x9f, 0xd3, 0x3f, 0xee, 0x1a, 0xab, 0xfb,
+    0xd4, 0x91, 0x8b, 0x94, 0xcd, 0x1b, 0x91, 0x6d, 0x2d, 0x5a, 0x97, 0xf9, 0x4f, 0xbb, 0xfa, 0xff, 0xea, 0xfa, 0xf8, 0x4f,
+    0xea, 0x1f, 0xf7, 0x00, 0x5d, 0x8f, 0x5a, 0x6b, 0x3a, 0x5d, 0x1d, 0x4f, 0xcb, 0x57, 0xd9, 0x4f, 0x7f, 0x5f, 0x8d, 0x6f,
+    0x6f, 0xcb, 0xdc, 0xab, 0xbc, 0x16, 0xd9, 0x91, 0xfe, 0x5f, 0xe1, 0xfe, 0xdf, 0x93, 0x59, 0x51, 0xef, 0x82, 0x7e, 0x7e,
+    0x7f, 0x04, 0x19, 0x7a, 0xf9, 0xbb, 0x2c, 0x58, 0xa1, 0x7f, 0x59, 0x1e, 0x48, 0x07, 0xfd, 0xaf, 0x9f, 0xc7, 0x65, 0x75,
+    0x24, 0xd5, 0x8e, 0xff, 0xf9, 0x7a, 0xda, 0xae, 0x3a, 0xe3, 0x8e, 0x7d, 0x53, 0x6a, 0xfa, 0xff, 0xda, 0xf8, 0x1f, 0x45,
+    0xcf, 0xff, 0x9d, 0xbd, 0x8f, 0x2e, 0x27, 0xf8, 0xb3, 0xcf, 0x7f, 0x9e, 0xf7, 0x99, 0x7c, 0x66, 0x44, 0xec, 0xfd, 0xbf,
+    0xb3, 0x03, 0xb2, 0xa7, 0xfe, 0xf1, 0x3d, 0x25, 0x73, 0xfd, 0xe6, 0xb7, 0x1d, 0xa9, 0xeb, 0x7b, 0x58, 0x56, 0xf6, 0x6d,
+    0x78, 0x83, 0x4f, 0xbd, 0x63, 0xde, 0x97, 0x19, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x10, 0xf3, 0xc8, 0xac, 0xae, 0x38, 0x2b, 0x51, 0x01, 0xdc, 0xb7, 0x6e, 0x73, 0xe7, 0x2f, 0xd1, 0x42, 0x2d, 0xb7, 0x4c,
+    0x1d, 0xe8, 0x2b, 0x77, 0x8c, 0x82, 0xde, 0x19, 0xdf, 0xba, 0x94, 0x16, 0xd5, 0xcf, 0xfa, 0xaa, 0x64, 0x73, 0xd1, 0x64,
+    0xdc, 0x6d, 0x0a, 0xd6, 0x88, 0x9f, 0xcb, 0x36, 0xf1, 0xf9, 0x42, 0xee, 0xbd, 0xf3, 0x75, 0xeb, 0xa6, 0x0a, 0xc6, 0x81,
+    0x42, 0x8e, 0xb3, 0xdf, 0x6d, 0xb5, 0x1e, 0x95, 0xeb, 0xfa, 0xe7, 0xe3, 0x5f, 0xb6, 0xba, 0xf6, 0x59, 0xfd, 0x73, 0x11,
+    0xb2, 0x1e, 0xff, 0x4a, 0x65, 0xd0, 0x54, 0x79, 0x76, 0x56, 0xf5, 0x1f, 0x66, 0xfd, 0x9f, 0x8b, 0x7f, 0x6d, 0x18, 0xff,
+    0xff, 0x1c, 0xa1, 0x64, 0xfb, 0xdd, 0x35, 0x9f, 0xe4, 0x1e, 0xfd, 0xb3, 0xde, 0x3f, 0xdd, 0xb8, 0x63, 0xf5, 0xa8, 0xb7,
+    0x25, 0xdb, 0x83, 0xa8, 0xa1, 0x2f, 0xe6, 0x2e, 0xef, 0xac, 0x87, 0xc3, 0x71, 0xfd, 0x09, 0x7c, 0xa5, 0x87, 0x5c, 0xbd,
+    0x6b, 0x9c, 0xfa, 0xbb, 0xbc, 0xab, 0xb3, 0xb1, 0x87, 0xfe, 0xde, 0xf7, 0xc7, 0x7e, 0xf1, 0x7f, 0xed, 0xbb, 0xed, 0xad,
+    0x7f, 0xec, 0xfd, 0x4f, 0x9b, 0x5b, 0xb9, 0xa3, 0x3f, 0x59, 0xa9, 0x27, 0xd0, 0x3e, 0xfa, 0xef, 0x3a, 0xd6, 0xdb, 0xe7,
+    0xb6, 0xba, 0xe8, 0x7f, 0x37, 0xff, 0x73, 0x5a, 0xc6, 0xc6, 0x49, 0xfa, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x71, 0x8d, 0x74, 0x76, 0x95, 0x51, 0x41, 0x27, 0xf1, 0x75, 0xa5, 0xb4, 0xeb, 0x7c,
+    0x82, 0xaa, 0xab, 0x58, 0xdd, 0x1f, 0x3e, 0x5a, 0x1b, 0x55, 0x41, 0xb7, 0xa3, 0x4b, 0xab, 0xbc, 0x13, 0x53, 0x26, 0xef,
+    0xab, 0xaf, 0xf2, 0xbf, 0xfb, 0x2a, 0x66, 0x75, 0x55, 0xc0, 0xd5, 0xee, 0x69, 0x4b, 0xd7, 0x55, 0xf6, 0xf1, 0x66, 0x0d,
+    0xa3, 0xfe, 0xa7, 0xf6, 0xbc, 0xfb, 0x3d, 0x48, 0x6a, 0xf4, 0x79, 0x56, 0xff, 0xf8, 0x78, 0xb1, 0x3a, 0xbe, 0xac, 0xef,
+    0x38, 0xa2, 0xcb, 0x2c, 0x98, 0xca, 0xb6, 0xf7, 0xf7, 0xcf, 0xbe, 0xcf, 0x65, 0xcb, 0x19, 0x74, 0xd6, 0x05, 0xf7, 0xeb,
+    0xbf, 0xaf, 0x8d, 0x7b, 0xeb, 0xbf, 0x53, 0x67, 0xf4, 0x47, 0xff, 0xaf, 0xe9, 0x1f, 0xab, 0xb4, 0x5f, 0xff, 0x79, 0x4e,
+    0xb7, 0xda, 0xd1, 0x9f, 0xf8, 0x27, 0xfe, 0xd1, 0xff, 0xc9, 0xcf, 0x63, 0x11, 0x7a, 0xaa, 0xfe, 0x2a, 0x9d, 0x33, 0x39,
+    0x51, 0x7f, 0x5f, 0xde, 0x6d, 0x7f, 0xfd, 0x77, 0x69, 0x72, 0xb6, 0xfe, 0xe3, 0x35, 0xf1, 0x8f, 0xfe, 0xbe, 0x7a, 0x05,
+    0xe7, 0xcd, 0xff, 0xe4, 0xb3, 0xd6, 0x9d, 0xf3, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
+    0x5e, 0xff, 0xbf, 0xc2, 0xf3, 0xf6, 0x19, 0x27, 0x7a, 0xa5, 0x3f, 0x7f, 0x7e, 0x96, 0x5d, 0xa9, 0x19, 0xfa, 0xbd, 0x79,
+    0x10, 0xb3, 0xd7, 0x91, 0x5d, 0x4d, 0x89, 0xd7, 0xac, 0x8e, 0xfa, 0x70, 0xea, 0xfd, 0xf9, 0x91, 0xbd, 0x0a, 0x1c, 0xfb,
+    0x0e, 0x54, 0xe7, 0x41, 0x64, 0xf2, 0x51, 0x9e, 0xf2, 0x92, 0x5f, 0x57, 0x54, 0xed, 0xbc, 0xe2, 0x94, 0xe9, 0x3f, 0xb4,
+    0xd1, 0x7f, 0x1f, 0xd5, 0x3f, 0xb7, 0x03, 0x80, 0xc2, 0x3d, 0xa5, 0xc2, 0xeb, 0x96, 0x3d, 0x56, 0x93, 0xcf, 0xa9, 0x91,
+    0xaa, 0x54, 0xfc, 0xfb, 0x22, 0x22, 0x33, 0xfe, 0x9f, 0xa1, 0xbf, 0x8e, 0xca, 0x83, 0x89, 0xfa, 0xdd, 0x54, 0xac, 0x7f,
+    0xd6, 0xb7, 0x70, 0x46, 0xfc, 0xf7, 0xf1, 0x9d, 0xe6, 0x47, 0x2a, 0xa1, 0xff, 0xad, 0xbe, 0xda, 0xe2, 0x20, 0x8a, 0x3b,
+    0x5a, 0xd1, 0x3f, 0xf2, 0x16, 0xb5, 0xa2, 0x7f, 0x3f, 0x1f, 0x5c, 0xf6, 0xfd, 0xef, 0x9b, 0xfa, 0xaf, 0xc6, 0xff, 0x69,
+    0x3e, 0xc8, 0xd8, 0x5e, 0x8b, 0x4f, 0xe9, 0xaf, 0x03, 0xf4, 0x57, 0xab, 0x11, 0xca, 0xe9, 0x83, 0xd5, 0x03, 0xcf, 0xff,
+    0xf1, 0x99, 0xc4, 0x8e, 0xf1, 0x7f, 0xbe, 0xfe, 0x4f, 0xec, 0x90, 0xe8, 0xd9, 0xcf, 0xed, 0x0c, 0xfd, 0xbb, 0xbd, 0xff,
+    0x79, 0xe7, 0x92, 0x9c, 0xf3, 0x3f, 0x67, 0x55, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xf8, 0x72, 0x0e, 0xc0, 0xaa, 0x03, 0x5f, 0xcb, 0x2b, 0x55, 0x0a, 0x38, 0xf8, 0xd5, 0xc8, 0xbf, 0x3f, 0x26, 0x6b, 0x2c,
+    0x57, 0x9c, 0xad, 0xa7, 0x5a, 0x7a, 0x55, 0x85, 0xfc, 0x61, 0xa9, 0x92, 0x1f, 0x73, 0x1f, 0x3f, 0xed, 0xdf, 0x5f, 0x5b,
+    0x11, 0xdf, 0x9d, 0x0f, 0x51, 0xe1, 0x25, 0x7e, 0x3e, 0x1f, 0x61, 0xa7, 0x7f, 0xff, 0xae, 0x5a, 0xb9, 0x36, 0x65, 0x1b,
+    0xb0, 0x1a, 0xda, 0x3d, 0xdb, 0xa4, 0x53, 0xfe, 0x9f, 0x67, 0xa5, 0x7d, 0x35, 0x4f, 0x49, 0x8b, 0x3d, 0xea, 0x2e, 0x87,
+    0x81, 0x8e, 0xf5, 0x43, 0x7b, 0x8f, 0xfc, 0x8c, 0xfe, 0xe7, 0xf8, 0xb4, 0xd0, 0x1f, 0xfd, 0xd1, 0xff, 0xcb, 0xfa, 0xeb,
+    0x00, 0xfd, 0x3d, 0xfb, 0x36, 0xa0, 0x7f, 0x27, 0x3f, 0x74, 0x34, 0xfe, 0x87, 0x25, 0xc3, 0x1c, 0xfd, 0xbb, 0xf8, 0xa1,
+    0xd1, 0xbf, 0x4f, 0x9e, 0x46, 0x6f, 0xfd, 0xcf, 0x7b, 0xff, 0x1b, 0xc5, 0xb5, 0xd1, 0xdf, 0xe3, 0x87, 0x67, 0x0e, 0x88,
+    0xf9, 0x27, 0x40, 0x7f, 0xf8, 0xa2, 0xf6, 0xa8, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7, 0x7a, 0x16,
+    0x01, 0xfd, 0x01, 0x00, 0x80, 0xfe, 0x1f, 0xd0, 0x1f, 0xd0, 0x1f, 0xd0, 0x1f, 0xd0, 0x1f, 0xd0, 0x1f, 0xd0, 0x1f, 0xb2,
+    0x15, 0x0d, 0x68, 0x07, 0x7a, 0x01, 0x40, 0x7f, 0xc0, 0x39, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xbb, 0x01, 0xfd, 0x61, 0xab, 0xfe, 0x7f, 0x01 };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle ashesFontRecs[189] = {
@@ -166,180 +153,180 @@
     { 215, 4, 4 , 8 },
     { 227, 4, 4 , 8 },
     { 239, 4, 6 , 8 },
-    { 253, 4, 4 , 8 },
-    { 265, 4, 4 , 8 },
-    { 277, 4, 4 , 8 },
-    { 289, 4, 4 , 8 },
-    { 301, 4, 4 , 8 },
-    { 313, 4, 1 , 5 },
-    { 322, 4, 2 , 6 },
-    { 332, 4, 4 , 7 },
-    { 344, 4, 4 , 4 },
-    { 356, 4, 4 , 7 },
-    { 368, 4, 4 , 10 },
-    { 380, 4, 8 , 7 },
-    { 396, 4, 4 , 10 },
-    { 408, 4, 4 , 10 },
-    { 420, 4, 4 , 10 },
-    { 432, 4, 4 , 10 },
-    { 444, 4, 4 , 10 },
-    { 456, 4, 5 , 10 },
-    { 469, 4, 4 , 10 },
-    { 481, 4, 4 , 10 },
-    { 493, 4, 1 , 10 },
-    { 4, 28, 3 , 10 },
-    { 15, 28, 4 , 10 },
-    { 27, 28, 4 , 10 },
-    { 39, 28, 7 , 10 },
-    { 54, 28, 4 , 10 },
-    { 66, 28, 4 , 10 },
-    { 78, 28, 4 , 10 },
-    { 90, 28, 5 , 11 },
-    { 103, 28, 4 , 10 },
-    { 115, 28, 4 , 10 },
-    { 127, 28, 5 , 10 },
-    { 140, 28, 4 , 10 },
-    { 152, 28, 4 , 10 },
-    { 164, 28, 7 , 10 },
-    { 179, 28, 4 , 10 },
-    { 191, 28, 4 , 10 },
-    { 203, 28, 4 , 10 },
-    { 215, 28, 2 , 12 },
-    { 225, 28, 5 , 10 },
-    { 238, 28, 2 , 12 },
-    { 248, 28, 5 , 3 },
-    { 261, 28, 5 , 1 },
-    { 274, 28, 2 , 2 },
-    { 284, 28, 4 , 8 },
-    { 296, 28, 4 , 10 },
-    { 308, 28, 3 , 8 },
-    { 319, 28, 4 , 10 },
-    { 331, 28, 4 , 8 },
-    { 343, 28, 3 , 10 },
-    { 354, 28, 5 , 11 },
-    { 367, 28, 4 , 10 },
-    { 379, 28, 1 , 10 },
-    { 388, 28, 3 , 13 },
-    { 399, 28, 4 , 10 },
-    { 411, 28, 2 , 10 },
-    { 421, 28, 7 , 8 },
-    { 436, 28, 4 , 8 },
-    { 448, 28, 4 , 8 },
-    { 460, 28, 4 , 11 },
-    { 472, 28, 4 , 11 },
-    { 484, 28, 3 , 8 },
-    { 495, 28, 4 , 8 },
+    { 4, 28, 4 , 8 },
+    { 16, 28, 4 , 8 },
+    { 28, 28, 4 , 8 },
+    { 40, 28, 4 , 8 },
+    { 52, 28, 4 , 8 },
+    { 64, 28, 1 , 5 },
+    { 73, 28, 2 , 6 },
+    { 83, 28, 4 , 7 },
+    { 95, 28, 4 , 4 },
+    { 107, 28, 4 , 7 },
+    { 119, 28, 4 , 10 },
+    { 131, 28, 8 , 7 },
+    { 147, 28, 4 , 10 },
+    { 159, 28, 4 , 10 },
+    { 171, 28, 4 , 10 },
+    { 183, 28, 4 , 10 },
+    { 195, 28, 4 , 10 },
+    { 207, 28, 5 , 10 },
+    { 220, 28, 4 , 10 },
+    { 232, 28, 4 , 10 },
+    { 244, 28, 1 , 10 },
     { 4, 52, 3 , 10 },
-    { 15, 52, 4 , 8 },
-    { 27, 52, 5 , 8 },
-    { 40, 52, 7 , 8 },
-    { 55, 52, 4 , 8 },
-    { 67, 52, 4 , 11 },
-    { 79, 52, 4 , 8 },
-    { 91, 52, 4 , 12 },
-    { 103, 52, 1 , 10 },
-    { 112, 52, 4 , 12 },
-    { 124, 52, 4 , 2 },
-    { 136, 52, 1 , 10 },
-    { 145, 52, 4 , 12 },
-    { 157, 52, 5 , 10 },
-    { 170, 52, 5 , 10 },
-    { 183, 52, 5 , 10 },
-    { 196, 52, 6 , 13 },
-    { 210, 52, 4 , 10 },
-    { 222, 52, 6 , 13 },
-    { 236, 52, 7 , 9 },
-    { 251, 52, 3 , 7 },
-    { 262, 52, 6 , 5 },
-    { 276, 52, 6 , 13 },
-    { 290, 52, 7 , 9 },
-    { 305, 52, 4 , 1 },
-    { 317, 52, 3 , 5 },
-    { 328, 52, 5 , 7 },
-    { 341, 52, 3 , 5 },
-    { 352, 52, 6 , 13 },
-    { 366, 52, 6 , 13 },
-    { 380, 52, 4 , 11 },
-    { 392, 52, 6 , 10 },
-    { 406, 52, 3 , 3 },
-    { 417, 52, 6 , 13 },
-    { 431, 52, 2 , 5 },
-    { 441, 52, 3 , 5 },
-    { 452, 52, 6 , 5 },
-    { 466, 52, 7 , 10 },
-    { 481, 52, 7 , 8 },
-    { 496, 52, 6 , 13 },
-    { 4, 76, 4 , 10 },
-    { 16, 76, 4 , 13 },
-    { 28, 76, 4 , 13 },
-    { 40, 76, 4 , 13 },
-    { 52, 76, 4 , 13 },
-    { 64, 76, 4 , 13 },
-    { 76, 76, 4 , 13 },
-    { 88, 76, 7 , 10 },
-    { 103, 76, 4 , 13 },
-    { 115, 76, 4 , 13 },
-    { 127, 76, 4 , 13 },
-    { 139, 76, 4 , 13 },
-    { 151, 76, 4 , 13 },
-    { 163, 76, 2 , 13 },
-    { 173, 76, 2 , 13 },
-    { 183, 76, 3 , 13 },
-    { 194, 76, 3 , 13 },
-    { 205, 76, 5 , 10 },
-    { 218, 76, 4 , 13 },
-    { 230, 76, 4 , 13 },
-    { 242, 76, 4 , 13 },
-    { 254, 76, 4 , 13 },
-    { 266, 76, 4 , 13 },
-    { 278, 76, 4 , 13 },
-    { 290, 76, 5 , 5 },
-    { 303, 76, 6 , 12 },
-    { 317, 76, 4 , 13 },
-    { 329, 76, 4 , 13 },
-    { 341, 76, 4 , 13 },
-    { 353, 76, 4 , 13 },
-    { 365, 76, 4 , 13 },
-    { 377, 76, 6 , 13 },
-    { 391, 76, 5 , 12 },
-    { 404, 76, 4 , 12 },
-    { 416, 76, 4 , 12 },
-    { 428, 76, 4 , 12 },
-    { 440, 76, 4 , 12 },
-    { 452, 76, 4 , 12 },
-    { 464, 76, 4 , 12 },
-    { 476, 76, 7 , 8 },
-    { 491, 76, 3 , 11 },
-    { 4, 100, 4 , 12 },
-    { 16, 100, 4 , 12 },
-    { 28, 100, 4 , 12 },
-    { 40, 100, 4 , 12 },
-    { 52, 100, 3 , 12 },
-    { 63, 100, 3 , 12 },
-    { 74, 100, 3 , 12 },
-    { 85, 100, 3 , 12 },
-    { 96, 100, 6 , 13 },
-    { 110, 100, 4 , 12 },
-    { 122, 100, 4 , 12 },
-    { 134, 100, 4 , 12 },
-    { 146, 100, 4 , 12 },
-    { 158, 100, 4 , 12 },
-    { 170, 100, 4 , 12 },
-    { 182, 100, 5 , 5 },
-    { 195, 100, 6 , 10 },
-    { 209, 100, 4 , 12 },
-    { 221, 100, 4 , 12 },
-    { 233, 100, 4 , 12 },
-    { 245, 100, 4 , 12 },
-    { 257, 100, 4 , 15 },
-    { 269, 100, 6 , 13 },
-    { 283, 100, 4 , 15 },
+    { 15, 52, 4 , 10 },
+    { 27, 52, 4 , 10 },
+    { 39, 52, 7 , 10 },
+    { 54, 52, 4 , 10 },
+    { 66, 52, 4 , 10 },
+    { 78, 52, 4 , 10 },
+    { 90, 52, 5 , 11 },
+    { 103, 52, 4 , 10 },
+    { 115, 52, 4 , 10 },
+    { 127, 52, 5 , 10 },
+    { 140, 52, 4 , 10 },
+    { 152, 52, 4 , 10 },
+    { 164, 52, 7 , 10 },
+    { 179, 52, 4 , 10 },
+    { 191, 52, 4 , 10 },
+    { 203, 52, 4 , 10 },
+    { 215, 52, 2 , 12 },
+    { 225, 52, 5 , 10 },
+    { 238, 52, 2 , 12 },
+    { 4, 76, 5 , 3 },
+    { 17, 76, 5 , 1 },
+    { 30, 76, 2 , 2 },
+    { 40, 76, 4 , 8 },
+    { 52, 76, 4 , 10 },
+    { 64, 76, 3 , 8 },
+    { 75, 76, 4 , 10 },
+    { 87, 76, 4 , 8 },
+    { 99, 76, 3 , 10 },
+    { 110, 76, 5 , 11 },
+    { 123, 76, 4 , 10 },
+    { 135, 76, 1 , 10 },
+    { 144, 76, 3 , 13 },
+    { 155, 76, 4 , 10 },
+    { 167, 76, 2 , 10 },
+    { 177, 76, 7 , 8 },
+    { 192, 76, 4 , 8 },
+    { 204, 76, 4 , 8 },
+    { 216, 76, 4 , 11 },
+    { 228, 76, 4 , 11 },
+    { 240, 76, 3 , 8 },
+    { 4, 100, 4 , 8 },
+    { 16, 100, 3 , 10 },
+    { 27, 100, 4 , 8 },
+    { 39, 100, 5 , 8 },
+    { 52, 100, 7 , 8 },
+    { 67, 100, 4 , 8 },
+    { 79, 100, 4 , 11 },
+    { 91, 100, 4 , 8 },
+    { 103, 100, 4 , 12 },
+    { 115, 100, 1 , 10 },
+    { 124, 100, 4 , 12 },
+    { 136, 100, 4 , 2 },
+    { 148, 100, 1 , 10 },
+    { 157, 100, 4 , 12 },
+    { 169, 100, 5 , 10 },
+    { 182, 100, 5 , 10 },
+    { 195, 100, 5 , 10 },
+    { 208, 100, 0 , 0 },
+    { 216, 100, 4 , 10 },
+    { 228, 100, 0 , 0 },
+    { 236, 100, 7 , 9 },
+    { 4, 124, 3 , 7 },
+    { 15, 124, 6 , 5 },
+    { 29, 124, 0 , 0 },
+    { 37, 124, 7 , 9 },
+    { 52, 124, 4 , 1 },
+    { 64, 124, 3 , 5 },
+    { 75, 124, 5 , 7 },
+    { 88, 124, 3 , 5 },
+    { 99, 124, 0 , 0 },
+    { 107, 124, 0 , 0 },
+    { 115, 124, 4 , 11 },
+    { 127, 124, 6 , 10 },
+    { 141, 124, 3 , 3 },
+    { 152, 124, 0 , 0 },
+    { 160, 124, 2 , 5 },
+    { 170, 124, 3 , 5 },
+    { 181, 124, 6 , 5 },
+    { 195, 124, 7 , 10 },
+    { 210, 124, 7 , 8 },
+    { 225, 124, 0 , 0 },
+    { 233, 124, 4 , 10 },
+    { 4, 148, 4 , 13 },
+    { 16, 148, 4 , 13 },
+    { 28, 148, 4 , 13 },
+    { 40, 148, 4 , 13 },
+    { 52, 148, 4 , 13 },
+    { 64, 148, 4 , 13 },
+    { 76, 148, 7 , 10 },
+    { 91, 148, 4 , 13 },
+    { 103, 148, 4 , 13 },
+    { 115, 148, 4 , 13 },
+    { 127, 148, 4 , 13 },
+    { 139, 148, 4 , 13 },
+    { 151, 148, 2 , 13 },
+    { 161, 148, 2 , 13 },
+    { 171, 148, 3 , 13 },
+    { 182, 148, 3 , 13 },
+    { 193, 148, 5 , 10 },
+    { 206, 148, 4 , 13 },
+    { 218, 148, 4 , 13 },
+    { 230, 148, 4 , 13 },
+    { 242, 148, 4 , 13 },
+    { 4, 172, 4 , 13 },
+    { 16, 172, 4 , 13 },
+    { 28, 172, 5 , 5 },
+    { 41, 172, 6 , 12 },
+    { 55, 172, 4 , 13 },
+    { 67, 172, 4 , 13 },
+    { 79, 172, 4 , 13 },
+    { 91, 172, 4 , 13 },
+    { 103, 172, 4 , 13 },
+    { 115, 172, 0 , 0 },
+    { 123, 172, 5 , 12 },
+    { 136, 172, 4 , 12 },
+    { 148, 172, 4 , 12 },
+    { 160, 172, 4 , 12 },
+    { 172, 172, 4 , 12 },
+    { 184, 172, 4 , 12 },
+    { 196, 172, 4 , 12 },
+    { 208, 172, 7 , 8 },
+    { 223, 172, 3 , 11 },
+    { 234, 172, 4 , 12 },
+    { 4, 196, 4 , 12 },
+    { 16, 196, 4 , 12 },
+    { 28, 196, 4 , 12 },
+    { 40, 196, 3 , 12 },
+    { 51, 196, 3 , 12 },
+    { 62, 196, 3 , 12 },
+    { 73, 196, 3 , 12 },
+    { 84, 196, 0 , 0 },
+    { 92, 196, 4 , 12 },
+    { 104, 196, 4 , 12 },
+    { 116, 196, 4 , 12 },
+    { 128, 196, 4 , 12 },
+    { 140, 196, 4 , 12 },
+    { 152, 196, 4 , 12 },
+    { 164, 196, 5 , 5 },
+    { 177, 196, 6 , 10 },
+    { 191, 196, 4 , 12 },
+    { 203, 196, 4 , 12 },
+    { 215, 196, 4 , 12 },
+    { 227, 196, 4 , 12 },
+    { 239, 196, 4 , 15 },
+    { 4, 220, 0 , 0 },
+    { 12, 220, 4 , 15 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo ashesFontGlyphs[189] = {
-    { 32, 0, 13, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 0, 3, 4, { 0 }},
     { 34, 0, 1, 5, { 0 }},
     { 35, 0, 4, 8, { 0 }},
@@ -439,30 +426,30 @@
     { 163, 0, 3, 7, { 0 }},
     { 8364, 0, 3, 7, { 0 }},
     { 165, 0, 3, 7, { 0 }},
-    { 352, 1, 0, 8, { 0 }},
+    { 352, 0, 0, 0, { 0 }},
     { 167, 0, 3, 6, { 0 }},
-    { 353, 1, 0, 8, { 0 }},
+    { 353, 0, 0, 0, { 0 }},
     { 169, 0, 1, 9, { 0 }},
     { 170, 0, 1, 5, { 0 }},
     { 171, 0, 6, 8, { 0 }},
-    { 172, 1, 0, 8, { 0 }},
+    { 172, 0, 0, 0, { 0 }},
     { 174, 0, 1, 9, { 0 }},
     { 175, 0, 0, 6, { 0 }},
     { 176, 0, 1, 5, { 0 }},
     { 177, 0, 5, 7, { 0 }},
     { 178, 0, 1, 5, { 0 }},
-    { 179, 1, 0, 8, { 0 }},
-    { 381, 1, 0, 8, { 0 }},
+    { 179, 0, 0, 0, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
     { 181, 0, 5, 6, { 0 }},
     { 182, 0, 3, 8, { 0 }},
     { 183, 0, 7, 5, { 0 }},
-    { 382, 1, 0, 8, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
     { 185, 0, 1, 4, { 0 }},
     { 186, 0, 1, 5, { 0 }},
     { 187, 0, 6, 8, { 0 }},
     { 338, 0, 3, 9, { 0 }},
     { 339, 0, 5, 9, { 0 }},
-    { 376, 1, 0, 8, { 0 }},
+    { 376, 0, 0, 0, { 0 }},
     { 191, 0, 4, 6, { 0 }},
     { 192, 0, 0, 6, { 0 }},
     { 193, 0, 0, 6, { 0 }},
@@ -494,7 +481,7 @@
     { 219, 0, 0, 6, { 0 }},
     { 220, 0, 0, 6, { 0 }},
     { 221, 0, 0, 6, { 0 }},
-    { 222, 1, 0, 8, { 0 }},
+    { 222, 0, 0, 0, { 0 }},
     { 223, 0, 3, 7, { 0 }},
     { 224, 0, 1, 6, { 0 }},
     { 225, 0, 1, 6, { 0 }},
@@ -512,7 +499,7 @@
     { 237, 0, 1, 5, { 0 }},
     { 238, 0, 1, 5, { 0 }},
     { 239, 0, 1, 5, { 0 }},
-    { 240, 1, 0, 8, { 0 }},
+    { 240, 0, 0, 0, { 0 }},
     { 241, 0, 1, 6, { 0 }},
     { 242, 0, 1, 6, { 0 }},
     { 243, 0, 1, 6, { 0 }},
@@ -526,7 +513,7 @@
     { 251, 0, 1, 6, { 0 }},
     { 252, 0, 1, 6, { 0 }},
     { 253, 0, 1, 6, { 0 }},
-    { 254, 1, 0, 8, { 0 }},
+    { 254, 0, 0, 0, { 0 }},
     { 255, 0, 1, 6, { 0 }},
 };
 
@@ -544,7 +531,7 @@
     // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
     int ashesFontDataSize = 0;
     unsigned char *data = DecompressData(ashesFontData, ASHES_STYLE_FONT_ATLAS_COMP_SIZE, &ashesFontDataSize);
-    Image imFont = { data, 512, 256, 1, 2 };
+    Image imFont = { data, 256, 256, 1, 2 };
 
     Font font = { 0 };
     font.baseSize = 16;
@@ -568,7 +555,7 @@
 
     // Setup a white rectangle on the font to be used on shapes drawing,
     // it makes possible to draw shapes and text (full UI) in a single draw call
-    Rectangle fontWhiteRec = { 510, 254, 1, 1 };
+    Rectangle fontWhiteRec = { 254, 254, 1, 1 };
     SetShapesTexture(font.texture, fontWhiteRec);
 
     //-----------------------------------------------------------------
diff --git a/raygui/styles/bluish/style_bluish.h b/raygui/styles/bluish/style_bluish.h
--- a/raygui/styles/bluish/style_bluish.h
+++ b/raygui/styles/bluish/style_bluish.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,174 +15,165 @@
 
 // Custom style name: Bluish
 static const GuiStyleProp bluishStyleProps[BLUISH_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x5ca6a6ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0xb4e8f3ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0x447e77ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0x5f8792ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xcdeff7ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0x4c6c74ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0x3b5b5fff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xeaffffff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x275057ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x96aaacff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0xc8d7d9ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x8c9c9eff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 18, 0x84adb7ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0xe8eef1ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 0, (int)0x5ca6a6ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0xb4e8f3ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x447e77ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0x5f8792ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xcdeff7ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0x4c6c74ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x3b5b5fff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xeaffffff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x275057ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x96aaacff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0xc8d7d9ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x8c9c9eff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 18, (int)0x84adb7ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0xe8eef1ff },    // DEFAULT_BACKGROUND_COLOR 
 };
 
 // WARNING: This style uses a custom font: "homespun.ttf" (size: 10, spacing: 1)
 
-#define BLUISH_STYLE_FONT_ATLAS_COMP_SIZE 2914
+#define BLUISH_STYLE_FONT_ATLAS_COMP_SIZE 2730
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char bluishFontData[BLUISH_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0x9d, 0xb1, 0x8e, 0x24, 0x49, 0x11, 0x86, 0x2b, 0xf3, 0x00, 0x09, 0x13, 0x01, 0x12, 0xe0, 0x20, 0x81, 0x90, 0xf0, 0x10,
-    0x12, 0x2e, 0x16, 0x3c, 0x02, 0x2f, 0x02, 0x0e, 0xef, 0x81, 0x07, 0x12, 0x06, 0x12, 0xce, 0x99, 0x38, 0x48, 0x20, 0xe1,
-    0x81, 0x83, 0xc0, 0xc3, 0xc5, 0xe5, 0x2c, 0x8c, 0xc3, 0x81, 0x93, 0x26, 0x33, 0xd0, 0x4e, 0xcf, 0x2e, 0xe2, 0xae, 0xfe,
-    0xc8, 0x8a, 0xac, 0xa8, 0xea, 0xea, 0x9b, 0x6f, 0x3f, 0xed, 0x1a, 0x95, 0x5d, 0xd5, 0x99, 0x19, 0x59, 0x35, 0xb3, 0x9d,
-    0x5f, 0x47, 0xd8, 0x02, 0x00, 0x00, 0x00, 0xf0, 0x09, 0xaa, 0x38, 0x56, 0xe5, 0xab, 0x6b, 0xe0, 0x4a, 0xb7, 0xe3, 0xd5,
-    0x69, 0x8f, 0x5d, 0xad, 0x06, 0xae, 0xe0, 0xb5, 0xf8, 0x6d, 0x1f, 0xa7, 0xbc, 0x90, 0x31, 0xb3, 0xf5, 0xdd, 0x7c, 0xac,
-    0xcf, 0x52, 0xe4, 0xf5, 0x35, 0x34, 0x8a, 0x75, 0x4c, 0x1c, 0xeb, 0xab, 0xe3, 0x35, 0x2b, 0xa2, 0xa5, 0x9a, 0x89, 0xfe,
-    0xd8, 0xf3, 0x59, 0xeb, 0x57, 0xf4, 0xce, 0x5a, 0x7b, 0x8f, 0xb6, 0x72, 0x8d, 0x26, 0x57, 0xc5, 0x9b, 0x77, 0x54, 0x33,
-    0xd4, 0x45, 0x3c, 0x6d, 0xf5, 0xb5, 0x37, 0xca, 0xa6, 0x57, 0x7b, 0xef, 0x60, 0xd6, 0xac, 0xbd, 0xcc, 0xc7, 0xc7, 0x47,
-    0x61, 0xa1, 0xd7, 0xeb, 0x96, 0xfd, 0xf1, 0xef, 0xcf, 0xfd, 0x29, 0x2b, 0xf7, 0xc1, 0x9b, 0x68, 0x75, 0x11, 0x2f, 0xd5,
-    0x9f, 0xdb, 0xcc, 0x95, 0xd5, 0x2b, 0x7a, 0x67, 0x2d, 0x9b, 0x67, 0x7b, 0xfd, 0xfc, 0x5b, 0x4f, 0xd7, 0xc7, 0x51, 0x9e,
-    0xdb, 0xd6, 0xe6, 0xae, 0x25, 0x3d, 0x57, 0xd5, 0x78, 0x17, 0xf7, 0xde, 0x5a, 0x9f, 0x85, 0x1e, 0x6a, 0xd9, 0x1f, 0x7f,
-    0xbd, 0xa2, 0xed, 0xe5, 0xaf, 0x1a, 0x59, 0x13, 0xfd, 0x29, 0xa2, 0xc5, 0x3f, 0x6b, 0x4f, 0xfc, 0xcb, 0x4b, 0x4f, 0xab,
-    0xb8, 0x9f, 0x4d, 0xdc, 0xd1, 0x47, 0x46, 0x3f, 0x1e, 0xff, 0xf2, 0xb2, 0x56, 0x8b, 0xf8, 0xa9, 0x74, 0x6e, 0xfc, 0xcb,
-    0xcb, 0xf3, 0xbf, 0x8a, 0xde, 0x17, 0x19, 0x7d, 0xfd, 0xb4, 0x8d, 0xfc, 0x5c, 0x8d, 0xdd, 0xff, 0xb7, 0x79, 0x56, 0x6b,
-    0xeb, 0xb8, 0xd8, 0xfb, 0xe3, 0x8d, 0xc5, 0xdf, 0xe4, 0x4f, 0x9e, 0xb7, 0xab, 0x78, 0xef, 0x28, 0x7a, 0xd2, 0xfd, 0xdf,
-    0x65, 0x7f, 0xca, 0xcb, 0xf3, 0x7f, 0x7d, 0xcc, 0x91, 0xbb, 0x30, 0x12, 0xff, 0x51, 0x8c, 0x8f, 0xbc, 0xf7, 0xf5, 0xb5,
-    0xe3, 0xf1, 0xbf, 0x5d, 0x71, 0xed, 0x5d, 0x8e, 0x5e, 0xc5, 0x4b, 0xe8, 0xf7, 0xbf, 0xe2, 0xfc, 0xce, 0xf3, 0x76, 0xed,
-    0x44, 0xc6, 0x97, 0x15, 0xe7, 0xf3, 0xb1, 0xe1, 0x08, 0x22, 0xf1, 0x2f, 0x72, 0x56, 0xcb, 0xe9, 0x3f, 0xff, 0xbd, 0xf8,
-    0x7b, 0x3d, 0xf5, 0xe2, 0x1f, 0x1b, 0x85, 0x05, 0x7f, 0x07, 0xbf, 0x5e, 0xfc, 0xed, 0xdd, 0xff, 0x87, 0xb6, 0x8d, 0x4d,
-    0xbd, 0xde, 0xbb, 0xd2, 0x7d, 0x46, 0x3d, 0xd3, 0x9f, 0xab, 0x8d, 0x02, 0xf6, 0xf0, 0x34, 0x11, 0xc7, 0x27, 0x62, 0x0f,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x66, 0xf9, 0x64, 0x19, 0x26, 0xd9, 0x2d, 0xfb, 0xac, 0x20, 0x6d, 0xe7,
-    0x78, 0x46, 0x92, 0xdf, 0xcb, 0xb3, 0xe6, 0x63, 0x39, 0xc9, 0xf9, 0xb1, 0x09, 0x23, 0xe5, 0xac, 0x96, 0xb6, 0xd9, 0xc2,
-    0x58, 0xdf, 0xcd, 0xd5, 0x76, 0x4e, 0x17, 0x73, 0x35, 0xea, 0xe5, 0x79, 0xf3, 0x51, 0x27, 0x7a, 0x37, 0xf3, 0x69, 0xb9,
-    0x0d, 0x76, 0xe7, 0x5a, 0xa8, 0xa5, 0x0f, 0xf7, 0xfa, 0x62, 0xe6, 0xcb, 0xbe, 0x7d, 0x55, 0xcf, 0xce, 0x53, 0xfe, 0x87,
-    0xd7, 0x4b, 0xe5, 0x55, 0xd8, 0xf4, 0xa8, 0x5b, 0xea, 0x39, 0x6d, 0x2a, 0xfe, 0x7a, 0x54, 0x7a, 0x6f, 0xde, 0xb7, 0x1e,
-    0xf5, 0xd5, 0x62, 0x2d, 0xdb, 0xed, 0x03, 0x15, 0x63, 0xed, 0x48, 0x44, 0xdd, 0x19, 0x73, 0xbc, 0x8a, 0xd1, 0xd8, 0x5a,
-    0x68, 0x6f, 0xd2, 0x06, 0x73, 0xb8, 0xdf, 0x96, 0xd9, 0x66, 0x67, 0x74, 0x67, 0xbc, 0xbe, 0x61, 0xd2, 0x43, 0xe7, 0xf4,
-    0x04, 0x5f, 0x25, 0xba, 0x7a, 0x3c, 0x0f, 0xa7, 0x3b, 0xf1, 0xaf, 0xc1, 0xb1, 0xdd, 0x5a, 0x6a, 0x68, 0x2f, 0xd8, 0x06,
-    0x73, 0xb8, 0xdf, 0x96, 0xd9, 0x36, 0x7f, 0x65, 0xfa, 0x89, 0x9d, 0x75, 0xce, 0x71, 0x2b, 0xc0, 0xc2, 0xd7, 0xb1, 0x77,
-    0x2b, 0x35, 0xf3, 0xf9, 0x5f, 0x26, 0x7e, 0x22, 0x97, 0xdd, 0xae, 0xc6, 0xf6, 0x27, 0xf6, 0x9c, 0x61, 0x92, 0x75, 0xce,
-    0xb5, 0xdc, 0x8d, 0x32, 0xf9, 0xc4, 0x3e, 0xa7, 0xe5, 0x88, 0xf8, 0xcf, 0x1a, 0x26, 0x3d, 0xed, 0x9c, 0xe5, 0x74, 0xa7,
-    0xc7, 0xe4, 0xfd, 0xef, 0xcd, 0x47, 0xe6, 0x1c, 0x7a, 0x1e, 0x90, 0xdf, 0x83, 0xd7, 0x40, 0xe3, 0x33, 0x1b, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xff, 0xe3, 0xdb, 0x37, 0x31, 0x43, 0xc5, 0xb7, 0x76, 0xa2, 0x39, 0x7a,
-    0xea, 0xe9, 0xd6, 0x4d, 0x76, 0xcb, 0xcc, 0x5c, 0x8c, 0x5a, 0x62, 0x67, 0x8e, 0x3f, 0xef, 0xd6, 0x4e, 0x89, 0xfa, 0x54,
-    0xbb, 0x3d, 0x9f, 0x95, 0xb3, 0xfb, 0x67, 0xc3, 0x9e, 0xdd, 0xfe, 0xad, 0x17, 0xf4, 0x93, 0x3c, 0x73, 0xe9, 0x6d, 0xcf,
-    0x33, 0x77, 0x0b, 0x6f, 0x2d, 0x6d, 0x75, 0x2e, 0x66, 0xf7, 0xd0, 0x66, 0x3c, 0x14, 0x7f, 0x37, 0x2c, 0x2f, 0xfe, 0xb3,
-    0x63, 0x3e, 0xb7, 0x25, 0x9a, 0x47, 0xe4, 0x7a, 0xf1, 0xf7, 0x2c, 0x9f, 0xac, 0xf8, 0xef, 0xf3, 0x60, 0xf2, 0xe2, 0x3f,
-    0xe3, 0x70, 0xe9, 0xbe, 0x7b, 0x1e, 0xd4, 0x99, 0xf1, 0xd7, 0x79, 0xb4, 0xb6, 0xc4, 0x3f, 0x6e, 0xf9, 0xc4, 0xe3, 0xbf,
-    0xcf, 0x83, 0xc9, 0x8b, 0xff, 0x8c, 0xc3, 0xd1, 0x9d, 0x6c, 0x51, 0x3d, 0x9c, 0x7b, 0xc4, 0x06, 0x8e, 0x61, 0x9b, 0x88,
-    0x7f, 0x91, 0x7d, 0xdc, 0xeb, 0x7f, 0x46, 0xee, 0xff, 0x72, 0x98, 0x07, 0x93, 0xfb, 0xfc, 0xcf, 0x9a, 0x87, 0x32, 0xb0,
-    0x0f, 0xcb, 0x84, 0x2d, 0xd2, 0x93, 0xe7, 0x62, 0x9b, 0xef, 0x90, 0x13, 0xff, 0x99, 0x6b, 0x6d, 0x3b, 0x27, 0x1a, 0xff,
-    0x32, 0x11, 0xff, 0xcc, 0xbe, 0xfb, 0xf1, 0x2f, 0x8e, 0x2d, 0x32, 0x3e, 0x27, 0x3b, 0xfe, 0xb3, 0x56, 0x4b, 0x49, 0xbc,
-    0x96, 0x25, 0xc7, 0x3f, 0xee, 0xc9, 0xe4, 0xf6, 0xdd, 0x8f, 0xff, 0x7c, 0xef, 0x6e, 0x7f, 0xea, 0x01, 0x0e, 0x3d, 0x90,
-    0x71, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xff, 0xb3, 0x38, 0x15, 0xa9, 0x7c, 0xcb, 0x68, 0xbb,
-    0x2d, 0xf4, 0xd6, 0xa0, 0x58, 0xf7, 0x27, 0x72, 0x32, 0xfd, 0x8c, 0x6b, 0x77, 0x45, 0xdf, 0x47, 0xcd, 0xd6, 0x4c, 0xdf,
-    0x8e, 0xae, 0x71, 0xb6, 0xcd, 0xf2, 0xf1, 0xbc, 0x97, 0xd8, 0x77, 0x50, 0x55, 0x6e, 0x9a, 0x2a, 0x32, 0xdd, 0x44, 0xbf,
-    0xbb, 0xef, 0xe5, 0xaa, 0xd0, 0xd7, 0xc9, 0xcc, 0x62, 0x61, 0x72, 0xbd, 0xda, 0x44, 0xdf, 0x9a, 0x38, 0xaa, 0xae, 0x51,
-    0xa7, 0xbf, 0x29, 0x3d, 0x9f, 0xcb, 0x61, 0x6b, 0x4e, 0x25, 0xbd, 0x63, 0x78, 0xdb, 0xeb, 0x8e, 0xae, 0x80, 0x2e, 0xef,
-    0xb1, 0xf5, 0xbe, 0x56, 0xe9, 0xb7, 0x2c, 0xb2, 0x9a, 0x59, 0x3c, 0x57, 0x8d, 0x89, 0xaa, 0x61, 0xfe, 0xfe, 0x91, 0xea,
-    0x9b, 0xaa, 0x96, 0xb4, 0x88, 0x6b, 0xb4, 0xe9, 0xef, 0x84, 0xdb, 0xa0, 0x8e, 0x54, 0xe4, 0xca, 0xde, 0x0a, 0xf0, 0x2a,
-    0xb9, 0xb5, 0x84, 0x5a, 0x25, 0xf1, 0x5c, 0x35, 0x9e, 0x79, 0xe0, 0x5f, 0x2d, 0x9a, 0x9b, 0x45, 0xaf, 0x99, 0xd8, 0x93,
-    0x21, 0x2b, 0xc3, 0xcc, 0xb6, 0x27, 0x67, 0x71, 0x46, 0xb4, 0x84, 0x57, 0xc0, 0xb1, 0x95, 0x3c, 0x8e, 0x33, 0x0f, 0xb6,
-    0x8e, 0x22, 0x7b, 0x67, 0x79, 0x7b, 0xfc, 0x97, 0x5d, 0x39, 0x41, 0xca, 0xb4, 0x2b, 0x13, 0x5b, 0x01, 0x47, 0xc7, 0xff,
-    0x28, 0xf3, 0x20, 0x2b, 0x03, 0x47, 0xbf, 0x68, 0xfc, 0x47, 0xd9, 0x66, 0xe2, 0xb1, 0x8c, 0x1b, 0xa3, 0xcb, 0x5d, 0x9e,
-    0x0c, 0xd9, 0xe7, 0x2c, 0xe1, 0xff, 0xc9, 0x5c, 0x23, 0xfe, 0x73, 0x19, 0x6a, 0x4a, 0x38, 0x97, 0xd0, 0xbd, 0xe2, 0x1f,
-    0xaf, 0x23, 0x35, 0x77, 0x4e, 0xbe, 0x7b, 0x79, 0x46, 0xfc, 0xe1, 0x71, 0x32, 0x1f, 0x35, 0xfc, 0x24, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x52, 0xfe, 0x8f, 0x88, 0xbd, 0x72, 0xff, 0x96, 0x3a, 0xd8, 0xd5, 0x2a, 0xd3, 0x2d,
-    0x25, 0xed, 0x9a, 0x9e, 0x63, 0xb4, 0xb7, 0xe6, 0xd9, 0xdb, 0x6b, 0x97, 0x69, 0xfb, 0xe7, 0x7f, 0x7b, 0xd0, 0xb7, 0x7f,
-    0x6b, 0xd2, 0xce, 0x7a, 0x7e, 0xcb, 0xf1, 0xdf, 0xe6, 0x3f, 0x22, 0xdf, 0x80, 0x97, 0x21, 0xa2, 0x6e, 0x76, 0x2b, 0x9a,
-    0xdc, 0x53, 0xed, 0x32, 0x67, 0x40, 0x9b, 0xd8, 0x83, 0xb8, 0x7f, 0x96, 0x9d, 0x16, 0xac, 0x43, 0x55, 0x4f, 0xcc, 0xb1,
-    0xd1, 0x07, 0x3e, 0x4c, 0xcc, 0xa1, 0xd1, 0x39, 0x14, 0x96, 0xd0, 0x6b, 0x55, 0x4d, 0xa2, 0xf2, 0x90, 0xf1, 0x5f, 0x82,
-    0x6e, 0x5f, 0xdf, 0x90, 0x2f, 0x23, 0x2b, 0xc7, 0x46, 0x99, 0x30, 0x7c, 0x8a, 0x53, 0x7f, 0x2c, 0x16, 0x7f, 0x55, 0xad,
-    0x2e, 0xfe, 0x6c, 0x3c, 0x23, 0xcb, 0x46, 0x7e, 0x05, 0x9b, 0xbc, 0x6c, 0x1e, 0xf9, 0x39, 0x36, 0xbc, 0x9d, 0x72, 0xed,
-    0x55, 0xc6, 0xe2, 0x1f, 0xcd, 0xed, 0x70, 0x46, 0xfc, 0x67, 0x6b, 0xdb, 0xe4, 0x66, 0xe6, 0x28, 0x17, 0xc9, 0xb1, 0x11,
-    0xcd, 0x10, 0xb2, 0xff, 0xf9, 0x9f, 0xed, 0x23, 0xe4, 0xed, 0x54, 0x9f, 0x93, 0x67, 0x63, 0xc6, 0xd3, 0x39, 0x6a, 0x67,
-    0x3e, 0xbe, 0x3b, 0x1f, 0x71, 0x2b, 0xfb, 0x43, 0xc5, 0xff, 0xca, 0x6e, 0xcf, 0x95, 0x38, 0xb2, 0xe6, 0x19, 0xf6, 0xcf,
-    0xa3, 0xae, 0x69, 0xe6, 0xe0, 0x75, 0x5b, 0x5e, 0xcc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x93, 0x01,
-    0x68, 0x91, 0x39, 0x6d, 0x3e, 0x79, 0xfc, 0x8b, 0x56, 0xed, 0x5b, 0xf6, 0x35, 0xfb, 0x8e, 0x7d, 0xd7, 0xbe, 0xe2, 0x5c,
-    0xb1, 0xa6, 0xe5, 0x1b, 0x1a, 0x9d, 0x53, 0x83, 0x75, 0xca, 0x4a, 0xd8, 0xd7, 0xd9, 0x57, 0x8b, 0x2c, 0x36, 0xf3, 0xd1,
-    0x6b, 0x8d, 0xe2, 0xe1, 0xfb, 0x5f, 0x9e, 0x45, 0xb1, 0x9e, 0xb7, 0xe7, 0xe7, 0xf6, 0x64, 0x1f, 0xda, 0x3f, 0xed, 0x1f,
-    0xf6, 0x37, 0xfb, 0xd0, 0x3e, 0x2b, 0x3f, 0xc3, 0x5c, 0xf7, 0x52, 0xde, 0x8c, 0xe1, 0x29, 0x58, 0x4d, 0xcb, 0x3f, 0x47,
-    0x55, 0x08, 0x53, 0xd7, 0x2b, 0xcf, 0xd6, 0x44, 0x0b, 0xf9, 0x3a, 0xe3, 0x77, 0x52, 0xdf, 0xf1, 0x6e, 0xe1, 0x0a, 0x01,
-    0x7e, 0x55, 0xb1, 0x78, 0x3c, 0xe6, 0x32, 0xa7, 0xdc, 0xf6, 0xd7, 0xd6, 0xc6, 0xf4, 0x7b, 0xfb, 0x8f, 0xfd, 0xc0, 0xaa,
-    0xfd, 0xd0, 0xbe, 0x6a, 0x7f, 0xb5, 0xcf, 0xc9, 0xbc, 0x36, 0xeb, 0xe3, 0x6b, 0xd2, 0x87, 0xd1, 0xb9, 0x60, 0xbc, 0x73,
-    0xca, 0xcb, 0x2e, 0x60, 0x5d, 0xbd, 0x5f, 0xd6, 0xad, 0xac, 0xe2, 0xe6, 0xaa, 0xd1, 0xde, 0x50, 0x19, 0xe6, 0x39, 0x5a,
-    0x9b, 0x8b, 0x2e, 0x72, 0x0c, 0x75, 0x71, 0xe7, 0xa9, 0x7e, 0xab, 0xe3, 0xa3, 0x78, 0x8c, 0xf7, 0x1b, 0xd6, 0xb3, 0x20,
-    0x99, 0x18, 0xe9, 0xaf, 0xed, 0xcf, 0xf6, 0x0d, 0xfb, 0xb6, 0xfd, 0xcb, 0xfe, 0x6d, 0x1f, 0xd9, 0x67, 0x56, 0xef, 0x22,
-    0xe5, 0xe6, 0xe8, 0x27, 0xa5, 0x97, 0x0d, 0xa9, 0x38, 0x39, 0x9c, 0xba, 0x8c, 0xa7, 0xbe, 0xf7, 0xba, 0x5c, 0x9d, 0xde,
-    0x3c, 0xf5, 0xe1, 0xca, 0x51, 0x7b, 0xc3, 0x25, 0x34, 0x47, 0xaa, 0x72, 0xa0, 0xba, 0xff, 0x47, 0xf1, 0x18, 0xc7, 0xbf,
-    0x08, 0x43, 0x65, 0x7d, 0x8e, 0x7e, 0x61, 0x7f, 0xb7, 0xaf, 0xdb, 0x97, 0xec, 0x0b, 0xf6, 0x79, 0x7b, 0x5f, 0xc4, 0x4b,
-    0xad, 0xed, 0x51, 0x06, 0xa5, 0xb8, 0x01, 0x7a, 0x8b, 0xc7, 0x7a, 0xfc, 0x6b, 0x9a, 0x1d, 0x60, 0xef, 0x9e, 0x0b, 0x73,
-    0x75, 0xb6, 0x22, 0xcf, 0x7f, 0xf5, 0x13, 0x59, 0x3d, 0x53, 0x47, 0xf1, 0x98, 0x75, 0x28, 0x4c, 0xdc, 0x75, 0x7f, 0xb0,
-    0x9f, 0xd9, 0x4f, 0xed, 0x7b, 0xf6, 0x2b, 0xfb, 0xc8, 0x9a, 0x7d, 0x33, 0xb8, 0xb6, 0x4b, 0xb8, 0xde, 0xa0, 0x4d, 0xde,
-    0xff, 0x5d, 0xda, 0x77, 0xd7, 0x8e, 0xbf, 0xea, 0xb7, 0x3a, 0x3e, 0x8a, 0xc7, 0xac, 0x43, 0xa1, 0x5c, 0xc9, 0xf7, 0xed,
-    0xc7, 0xf6, 0x81, 0xfd, 0xd2, 0x7e, 0x64, 0xdf, 0xb7, 0xdf, 0xd8, 0x97, 0x27, 0xc6, 0x66, 0xa1, 0xdf, 0xbd, 0xbc, 0xdf,
-    0xca, 0xca, 0x20, 0xef, 0x54, 0x6e, 0x1e, 0xa1, 0x33, 0xde, 0x29, 0xce, 0x96, 0x78, 0xc4, 0x6d, 0x2c, 0xbd, 0x32, 0x7e,
-    0x62, 0xbf, 0xb5, 0x3f, 0xd9, 0x07, 0xf6, 0x3b, 0xfb, 0xa3, 0xfd, 0xc5, 0xde, 0x73, 0x4c, 0xba, 0x68, 0xcd, 0xbc, 0x59,
-    0xbf, 0xce, 0xe4, 0xef, 0x2a, 0xd9, 0xae, 0x51, 0xd6, 0x3b, 0x59, 0xa2, 0xbd, 0x34, 0x8a, 0xc7, 0xb5, 0xfc, 0x96, 0x99,
-    0xac, 0x7b, 0x8f, 0xed, 0x79, 0xc1, 0x96, 0x58, 0xce, 0x59, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0,
-    0xc5, 0xfe, 0xf1, 0xdd, 0x9a, 0x65, 0x53, 0x76, 0x89, 0xb1, 0x15, 0x14, 0x71, 0x5a, 0x8a, 0xcc, 0x61, 0x51, 0x37, 0xd4,
-    0xda, 0x2a, 0xa1, 0x91, 0xce, 0x1c, 0x1f, 0xf9, 0x3b, 0xfa, 0x78, 0x9e, 0x15, 0x35, 0xe3, 0x23, 0x45, 0x3f, 0x75, 0x6d,
-    0x6e, 0xe5, 0x9e, 0x88, 0x85, 0xa2, 0x6b, 0x89, 0x99, 0x70, 0x34, 0x9a, 0xf0, 0x04, 0x9a, 0x3c, 0xcb, 0x5e, 0x76, 0x1e,
-    0x4d, 0x66, 0xc4, 0x79, 0x92, 0xce, 0x8d, 0x3a, 0xfe, 0x9e, 0x8c, 0xf2, 0x93, 0xcc, 0xeb, 0xb3, 0x7e, 0x96, 0x3d, 0x1f,
-    0xef, 0xa1, 0xbe, 0x99, 0x5b, 0x85, 0xcd, 0x9c, 0xb6, 0xb6, 0xe9, 0x13, 0xf3, 0x51, 0xfc, 0xb7, 0x7b, 0x12, 0x63, 0x0b,
-    0xc5, 0xe4, 0x0e, 0xfe, 0xba, 0xa3, 0xa5, 0xb3, 0xed, 0xf8, 0x6d, 0xca, 0x9e, 0xf1, 0x73, 0x69, 0x69, 0x83, 0x23, 0xba,
-    0x62, 0x54, 0x2c, 0x9b, 0x34, 0x99, 0x7c, 0x2b, 0xa6, 0x3a, 0xee, 0x83, 0x97, 0x91, 0xa8, 0x9d, 0x1a, 0xff, 0xb1, 0x85,
-    0x12, 0x89, 0x7f, 0x77, 0x33, 0x24, 0x68, 0xbb, 0x64, 0x94, 0x11, 0x47, 0xbb, 0x58, 0x65, 0xea, 0xe9, 0x9b, 0xd3, 0xe2,
-    0xdf, 0xe5, 0x4d, 0xee, 0x7d, 0x7b, 0xf3, 0x10, 0xcf, 0xff, 0xb2, 0x66, 0x71, 0x6c, 0x8f, 0xff, 0xd8, 0x42, 0x31, 0x51,
-    0x03, 0x6f, 0xce, 0xc4, 0x8a, 0x65, 0x77, 0xf2, 0x57, 0xba, 0xce, 0xec, 0x74, 0x56, 0x8e, 0xbb, 0x99, 0x8c, 0x59, 0x9e,
-    0x65, 0xb3, 0x6c, 0xcc, 0x01, 0x66, 0x03, 0x5b, 0x63, 0x7b, 0xfc, 0xc7, 0x16, 0x4a, 0xc4, 0x69, 0x19, 0x9b, 0x38, 0x25,
-    0xb8, 0xab, 0xee, 0xf9, 0x45, 0x3a, 0x8f, 0xd0, 0x4c, 0x25, 0xc8, 0xb3, 0x5a, 0xbc, 0x79, 0x28, 0x1b, 0x23, 0xe7, 0xe7,
-    0xd0, 0x89, 0xc4, 0x7f, 0x9f, 0x85, 0x12, 0x33, 0x08, 0xe2, 0xfe, 0xe6, 0xd8, 0x22, 0xea, 0x77, 0xcf, 0x71, 0x19, 0x7f,
-    0x66, 0x74, 0xb9, 0x36, 0xb6, 0x1a, 0x13, 0xfe, 0x2b, 0x23, 0xf3, 0x7c, 0x96, 0x85, 0x32, 0x6b, 0xce, 0x94, 0x09, 0xbf,
-    0xe4, 0xdc, 0xec, 0x87, 0xf1, 0x96, 0x3e, 0x88, 0x1d, 0xf9, 0x46, 0xb6, 0x3e, 0xe9, 0xe2, 0xbf, 0x1d, 0xdf, 0xbb, 0x65,
-    0x64, 0xf5, 0x61, 0xd2, 0xec, 0xb7, 0xc8, 0x1e, 0x37, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x19,
-    0x2e, 0x4b, 0xb0, 0x86, 0x95, 0x6e, 0xd3, 0x47, 0x6b, 0x28, 0x6b, 0x8e, 0xe7, 0xdd, 0xdc, 0xdf, 0xef, 0x29, 0xee, 0x3e,
-    0x5d, 0x5e, 0x9f, 0xbd, 0x4f, 0x80, 0xf3, 0x0c, 0x17, 0xcf, 0x7d, 0xa8, 0xa1, 0x77, 0xa9, 0xce, 0x3b, 0x3c, 0x89, 0x3d,
-    0xaf, 0xb8, 0x77, 0x73, 0x7f, 0xbf, 0x47, 0x19, 0x1c, 0x55, 0x7a, 0x1f, 0x33, 0x7d, 0x1e, 0xad, 0x80, 0x2c, 0xc3, 0xc5,
-    0xf3, 0xce, 0x62, 0xef, 0xd2, 0x44, 0x8b, 0xbe, 0xbe, 0xfe, 0x7c, 0xbf, 0x39, 0x19, 0x84, 0xee, 0xed, 0xf7, 0xe8, 0x6a,
-    0x54, 0xca, 0xfb, 0xa8, 0x72, 0x05, 0xdc, 0x72, 0x5a, 0xd5, 0xe0, 0xd3, 0xff, 0x1c, 0x8f, 0x25, 0x9a, 0xd3, 0x4c, 0x1d,
-    0xbf, 0x39, 0x6f, 0x3a, 0x5b, 0x51, 0x2c, 0xe7, 0xd0, 0xfd, 0xfd, 0x9e, 0xee, 0xe6, 0x9b, 0x3a, 0x7e, 0xaf, 0x60, 0x94,
-    0x8f, 0xa7, 0x4f, 0x54, 0x64, 0x5c, 0xcf, 0x77, 0x64, 0xc2, 0xff, 0x88, 0x1d, 0x5f, 0x64, 0x46, 0x27, 0x73, 0x76, 0xc3,
-    0xaf, 0xb0, 0x57, 0x1f, 0x8b, 0xcc, 0xcd, 0x72, 0xb0, 0x13, 0xe2, 0x5f, 0x9c, 0x7a, 0x68, 0x79, 0x2d, 0xcb, 0xa0, 0xee,
-    0x5a, 0xd6, 0xf1, 0xeb, 0xee, 0xd5, 0x47, 0xe3, 0x6f, 0x27, 0xc5, 0xdf, 0x77, 0xdf, 0xb2, 0x5a, 0x88, 0x7f, 0x34, 0xfe,
-    0xfd, 0xa4, 0xe7, 0xbf, 0x76, 0xdf, 0x32, 0x5b, 0xce, 0x88, 0xbf, 0xb9, 0x35, 0xf5, 0x1e, 0x2d, 0xfe, 0xe5, 0x14, 0x57,
-    0x20, 0xbb, 0x8a, 0xa7, 0x9f, 0x4d, 0xee, 0xc8, 0xf8, 0xcf, 0x59, 0x1c, 0xe5, 0xce, 0x2d, 0x71, 0x5b, 0x25, 0xf3, 0x5d,
-    0xf2, 0xab, 0x78, 0xfa, 0x2d, 0xc7, 0x1d, 0xdf, 0xf3, 0x1d, 0x07, 0xbb, 0x63, 0x4b, 0xdc, 0xcb, 0xcb, 0x7c, 0x17, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x2d, 0xfe, 0x4f, 0xd4, 0xe5, 0x99, 0x31, 0x53, 0xaa, 0xeb, 0xe7, 0x5c, 0x35,
-    0xc7, 0x8e, 0x97, 0x4f, 0x48, 0xb7, 0xdc, 0xdb, 0xa9, 0xaa, 0xa1, 0xe8, 0xc7, 0x5d, 0x1e, 0xed, 0x99, 0xd8, 0xc4, 0x0e,
-    0xb8, 0xef, 0x06, 0xb5, 0x09, 0x07, 0xa7, 0x3a, 0x0e, 0x4e, 0x0b, 0xe5, 0xd8, 0xe8, 0x4e, 0x3e, 0x21, 0x73, 0x72, 0x17,
-    0x64, 0x8e, 0x48, 0xf7, 0xbb, 0x89, 0x55, 0xd1, 0xc2, 0x9f, 0x33, 0x3e, 0x85, 0xf2, 0xe5, 0x68, 0xcf, 0x64, 0x76, 0x07,
-    0x7c, 0xce, 0xc1, 0x69, 0x21, 0x07, 0xc7, 0xab, 0x22, 0x55, 0x07, 0x55, 0xba, 0xf4, 0x6e, 0x4b, 0xae, 0x53, 0x15, 0xeb,
-    0xb7, 0x9e, 0x81, 0x8c, 0xfc, 0x6f, 0xd9, 0x3b, 0x60, 0x45, 0x98, 0x6e, 0x33, 0x4f, 0xd8, 0x99, 0x16, 0x9d, 0xff, 0xc1,
-    0xab, 0xde, 0x37, 0x53, 0xc9, 0x28, 0xb7, 0xdf, 0x5e, 0x85, 0xbe, 0x12, 0xca, 0x64, 0x75, 0xdf, 0x6f, 0x38, 0x77, 0x51,
-    0x39, 0x27, 0xb7, 0x22, 0x90, 0x6e, 0x29, 0x32, 0x27, 0x4a, 0x71, 0x6b, 0x52, 0xf5, 0x70, 0x7d, 0x9c, 0x6c, 0x77, 0xa8,
-    0x38, 0x96, 0x60, 0xd9, 0x91, 0xfb, 0x69, 0xdf, 0xbd, 0xdc, 0x27, 0x72, 0xec, 0xf4, 0x89, 0xb8, 0x64, 0xb5, 0xd8, 0x54,
-    0x7d, 0xa9, 0x71, 0x7e, 0x94, 0xe3, 0xb3, 0x06, 0x75, 0xc7, 0xb7, 0xb9, 0x47, 0xfc, 0xc7, 0xf3, 0x15, 0x7f, 0x32, 0x9c,
-    0x73, 0x1f, 0x45, 0x57, 0xe0, 0x5c, 0x8e, 0x8d, 0x6c, 0xa7, 0xb2, 0x0c, 0xec, 0x8d, 0xb3, 0xe3, 0x5f, 0x06, 0xe7, 0xe8,
-    0xac, 0x5d, 0x76, 0x57, 0x07, 0x23, 0xfe, 0xbc, 0xde, 0x93, 0x69, 0x26, 0xaf, 0xc5, 0xaf, 0xd0, 0xb7, 0x37, 0xfe, 0x67,
-    0xd8, 0x2c, 0x57, 0x30, 0x50, 0x72, 0x33, 0x03, 0x9d, 0x37, 0xa2, 0xe8, 0x4a, 0xb3, 0x8b, 0xf6, 0xf5, 0xd3, 0x95, 0x19,
-    0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xd1, 0xf3, 0xff, 0xd4, 0x70, 0xcb, 0x95, 0xb3, 0xec, 0xe4, 0xe5,
-    0x1f, 0xca, 0x76, 0x78, 0xe6, 0x5c, 0xaa, 0xb9, 0x4c, 0x3f, 0xca, 0x47, 0xa9, 0x81, 0xfc, 0x2b, 0x37, 0xeb, 0x44, 0x55,
-    0xaa, 0xaa, 0x27, 0xf8, 0x30, 0x33, 0x86, 0x8f, 0x3a, 0xea, 0xed, 0xfc, 0x9c, 0xe3, 0xf0, 0xe8, 0xab, 0xd9, 0xc0, 0xd9,
-    0xc9, 0xc9, 0xef, 0xde, 0x9c, 0xb9, 0x99, 0x59, 0x31, 0xed, 0x92, 0x86, 0xcf, 0x8c, 0x5b, 0x91, 0xeb, 0xf0, 0xe8, 0x19,
-    0x6d, 0xd2, 0x2f, 0xf0, 0xab, 0x2f, 0x66, 0x7d, 0x5a, 0x1d, 0xdd, 0x91, 0x6a, 0x61, 0x8b, 0xa2, 0xdc, 0xdd, 0x94, 0xe9,
-    0xae, 0xc3, 0xd3, 0x43, 0x11, 0x9b, 0x69, 0x19, 0xed, 0xa5, 0x96, 0x44, 0x97, 0x2e, 0x6b, 0xb7, 0xd2, 0x82, 0x4f, 0xcc,
-    0x6b, 0x67, 0xd9, 0x29, 0x4e, 0xf6, 0x93, 0x47, 0xac, 0xfc, 0x72, 0xaf, 0xf8, 0x3f, 0xe6, 0x8c, 0xf9, 0x39, 0x29, 0x1e,
-    0x2d, 0xca, 0x36, 0xe9, 0xab, 0xe4, 0xc4, 0xff, 0xda, 0xf3, 0x92, 0x97, 0x65, 0xe3, 0x9c, 0x9e, 0xcd, 0x19, 0x61, 0x99,
-    0xfb, 0xd5, 0xd1, 0xf8, 0x97, 0xe4, 0xd1, 0x9f, 0x61, 0xf8, 0xcc, 0x64, 0xd9, 0x38, 0xcb, 0x3d, 0x9a, 0xb5, 0x2c, 0x8e,
-    0xbe, 0xff, 0x33, 0xef, 0x98, 0x7b, 0x1b, 0x3e, 0x57, 0x76, 0x78, 0xe2, 0x2e, 0x9d, 0xf6, 0xd5, 0xe1, 0xd1, 0xd0, 0xb1,
-    0x7c, 0x3d, 0x95, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x9d, 0xdb, 0x92, 0xdb, 0x38, 0x0c, 0x44, 0x45, 0xee, 0xff, 0xff, 0xf1, 0x90, 0xdc, 0xca, 0xc8, 0x33, 0xa9, 0x4d, 0xd4,
+    0x10, 0x01, 0x41, 0x17, 0xef, 0x9c, 0x9c, 0x4a, 0x1e, 0x44, 0x8b, 0x26, 0x09, 0x4a, 0x76, 0x84, 0x76, 0x63, 0x2c, 0x00,
+    0x00, 0x00, 0x00, 0x7f, 0x51, 0xc5, 0xb1, 0x2a, 0x5f, 0x5d, 0x1d, 0x3d, 0xad, 0xc7, 0xab, 0xd1, 0xee, 0xeb, 0xad, 0x3a,
+    0x7a, 0xb0, 0x5a, 0xec, 0xb6, 0x3f, 0x29, 0x2f, 0x32, 0x56, 0xb6, 0x7e, 0xaf, 0xc7, 0xf6, 0x2a, 0x79, 0x5e, 0x5f, 0x5d,
+    0xb3, 0xd8, 0x66, 0x88, 0x63, 0x7d, 0x73, 0xbe, 0x63, 0x14, 0xd1, 0x52, 0xc7, 0x10, 0xe3, 0x19, 0x9f, 0x67, 0x6d, 0xf7,
+    0x68, 0x9d, 0xb5, 0xf5, 0x1e, 0x6d, 0xa3, 0x8f, 0x26, 0x77, 0xc5, 0xaf, 0x77, 0x54, 0x2b, 0xd4, 0x45, 0x3c, 0xc7, 0xe6,
+    0x6b, 0x57, 0xca, 0xd4, 0xab, 0xad, 0x77, 0x18, 0xa3, 0x8d, 0xf6, 0x5a, 0x8f, 0x3f, 0x67, 0x31, 0x5c, 0xaf, 0xd7, 0x2d,
+    0xc7, 0xe3, 0xdf, 0x3f, 0xc7, 0x53, 0x36, 0xae, 0x83, 0x5f, 0xd1, 0xea, 0x22, 0x5e, 0x6a, 0x3c, 0xeb, 0xca, 0x95, 0xcd,
+    0x1e, 0xad, 0xb3, 0x96, 0xe9, 0xd5, 0xde, 0x3e, 0x7f, 0x1d, 0xe9, 0xf6, 0x3c, 0xca, 0x67, 0xdb, 0xd6, 0xda, 0xb5, 0xa4,
+    0xfb, 0xaa, 0x9a, 0xef, 0x62, 0x5e, 0x5b, 0xdb, 0xab, 0xd0, 0x5d, 0x2d, 0xc7, 0xe3, 0xaf, 0x77, 0xf4, 0x78, 0xfd, 0x55,
+    0x33, 0x6b, 0x62, 0x3c, 0x45, 0xb4, 0xd8, 0x67, 0x1d, 0x89, 0x7f, 0x79, 0x8d, 0xb4, 0x8a, 0xeb, 0x79, 0x88, 0x2b, 0xfa,
+    0xcc, 0xe8, 0xfb, 0xe3, 0x5f, 0x5e, 0x7b, 0xb5, 0x88, 0x4f, 0xa5, 0x6b, 0xe3, 0x5f, 0x5e, 0xf7, 0xff, 0x2a, 0x46, 0x5f,
+    0x64, 0xf4, 0xf5, 0xdd, 0xd6, 0xf3, 0xb9, 0xea, 0xbb, 0xfe, 0xd7, 0x75, 0x56, 0x7b, 0xeb, 0xbc, 0xd8, 0xdb, 0xf3, 0xf5,
+    0xc5, 0x7f, 0xc8, 0x4f, 0x9e, 0xaf, 0x5d, 0x7c, 0x74, 0x16, 0x3d, 0xe9, 0xfa, 0xef, 0x72, 0x3c, 0xe5, 0x75, 0xff, 0xdf,
+    0x9e, 0xb3, 0xe7, 0x2a, 0xf4, 0xc4, 0x7f, 0x2f, 0xc6, 0x67, 0x5e, 0xfb, 0xba, 0x6f, 0x7f, 0xfc, 0xd7, 0x1e, 0xb7, 0xde,
+    0xe5, 0xec, 0x5d, 0xbc, 0xb8, 0xbe, 0xff, 0x15, 0xe3, 0x3b, 0xcf, 0xd7, 0xde, 0xf1, 0xcc, 0x2f, 0x2b, 0xce, 0xd7, 0x33,
+    0x76, 0x67, 0xe0, 0x89, 0x7f, 0x91, 0xab, 0x5a, 0x2e, 0xff, 0xfc, 0xb7, 0xe2, 0x6f, 0x8d, 0xd4, 0x8a, 0xbf, 0x6f, 0x16,
+    0xc3, 0xf9, 0x1d, 0xfc, 0x79, 0xf1, 0x1f, 0xdf, 0xff, 0x1f, 0x9a, 0x9b, 0x9b, 0x7a, 0xbd, 0xd5, 0xd3, 0x3d, 0xb3, 0x8e,
+    0x8c, 0xe7, 0x69, 0xb3, 0x80, 0x23, 0x7c, 0x04, 0xe2, 0xf8, 0x41, 0xec, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xd2, 0x54, 0x3e, 0x59, 0x0a, 0x93, 0xec, 0x96, 0x63, 0xaa, 0x20, 0xad, 0xce, 0xb1, 0x14, 0x49, 0xf6, 0x28, 0xaf, 0x5a,
+    0x8f, 0xe5, 0x22, 0xcd, 0xcf, 0x08, 0x28, 0x52, 0xae, 0x6a, 0x69, 0xd3, 0x2a, 0x8c, 0xed, 0x6c, 0xae, 0x56, 0xe7, 0x74,
+    0xb1, 0x56, 0x7b, 0xa3, 0xbc, 0x6e, 0x3d, 0x6a, 0x60, 0x74, 0x91, 0xa7, 0xe5, 0x63, 0x27, 0x3b, 0xd7, 0x5c, 0x2d, 0x7d,
+    0x37, 0xd7, 0xe7, 0x53, 0xbe, 0x1c, 0xcb, 0xab, 0x5a, 0xea, 0x3c, 0xa5, 0xff, 0xb0, 0x46, 0xa9, 0x74, 0x15, 0x23, 0x3c,
+    0xeb, 0x96, 0x7a, 0x4e, 0x0b, 0xc5, 0x5f, 0xcf, 0x4a, 0xe7, 0xe6, 0x6d, 0xd5, 0xa3, 0xee, 0xcd, 0xd7, 0x32, 0xaf, 0x3e,
+    0x50, 0x31, 0xd6, 0x1a, 0x09, 0xaf, 0x76, 0x66, 0x18, 0xba, 0x8a, 0xbd, 0xb9, 0x35, 0x57, 0x6e, 0x72, 0xec, 0xac, 0xe1,
+    0x71, 0xb5, 0xcc, 0x9c, 0x3a, 0xa3, 0x1b, 0xf3, 0xb5, 0x15, 0x26, 0xdd, 0x75, 0x4e, 0x4f, 0xd0, 0xab, 0x78, 0x77, 0x8f,
+    0xa5, 0xc3, 0xe9, 0x46, 0xfc, 0xab, 0x73, 0x6e, 0x6b, 0x4b, 0x75, 0xe5, 0x82, 0xc7, 0xce, 0x1a, 0x1e, 0x57, 0xcb, 0xcc,
+    0xad, 0x5f, 0x09, 0xdf, 0xb1, 0xb3, 0xce, 0x39, 0x6f, 0x07, 0x0c, 0x77, 0x3f, 0xe3, 0x7b, 0xa7, 0x66, 0xde, 0xff, 0x4b,
+    0xe0, 0x13, 0xb9, 0x1c, 0xd6, 0x6a, 0xcc, 0xdf, 0xb1, 0x63, 0x0a, 0x93, 0xac, 0x73, 0x9e, 0xa5, 0xdd, 0x28, 0xc1, 0x3b,
+    0xf6, 0x35, 0x2d, 0x67, 0xc4, 0x3f, 0xaa, 0x30, 0xe9, 0x69, 0xe7, 0x2c, 0x97, 0x6b, 0x7a, 0x86, 0xbc, 0xfe, 0xad, 0xf5,
+    0xc8, 0x5c, 0x43, 0x4b, 0x07, 0x64, 0x8f, 0xe0, 0x27, 0xd0, 0x78, 0x66, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0xe0, 0xd0, 0xff, 0xd8, 0xea, 0x1b, 0x9f, 0x42, 0xc5, 0x56, 0xed, 0x78, 0x3d, 0x7a, 0xea, 0xe5, 0xaa, 0x9b, 0xec,
+    0x96, 0xc8, 0x5a, 0xec, 0xb5, 0xf8, 0xce, 0xdc, 0x7f, 0xde, 0xad, 0x35, 0x25, 0xea, 0xa9, 0x76, 0xfb, 0x3c, 0x2b, 0x27,
+    0xfb, 0x37, 0x76, 0x47, 0xb6, 0xfe, 0x5b, 0x1f, 0xa8, 0x4f, 0xb2, 0x94, 0x4b, 0x5f, 0x23, 0xcf, 0xcc, 0x16, 0xae, 0x2d,
+    0x6d, 0x73, 0x2d, 0xa2, 0x39, 0xb4, 0x88, 0x0e, 0xc5, 0xce, 0x86, 0xe5, 0xc5, 0x3f, 0x3a, 0xe7, 0x6b, 0x5b, 0xbc, 0x3e,
+    0x22, 0xcf, 0x8b, 0xbf, 0xa5, 0xf2, 0xc9, 0x8a, 0xff, 0x31, 0x1d, 0x4c, 0x5e, 0xfc, 0x23, 0x1a, 0x2e, 0x3d, 0x76, 0x4b,
+    0x07, 0x75, 0x65, 0xfc, 0xb5, 0x8f, 0xd6, 0x4c, 0xfc, 0xfd, 0x2a, 0x1f, 0x7f, 0xfc, 0x8f, 0xe9, 0x60, 0xf2, 0xe2, 0x1f,
+    0xd1, 0x70, 0x74, 0xc3, 0x2d, 0xaa, 0xbb, 0xbd, 0x47, 0xc6, 0x8e, 0xc6, 0xb0, 0x05, 0xe2, 0x5f, 0xe4, 0x18, 0x8f, 0xea,
+    0x3f, 0x3d, 0xd7, 0x7f, 0x39, 0x4d, 0x07, 0x93, 0x7b, 0xff, 0xcf, 0x5a, 0x87, 0xb2, 0xa3, 0x3e, 0x2c, 0x01, 0xb5, 0x48,
+    0x4f, 0x5e, 0x8b, 0x39, 0xbd, 0x43, 0x4e, 0xfc, 0x23, 0x7d, 0xcd, 0x9d, 0xe3, 0x8d, 0x7f, 0x09, 0xc4, 0x3f, 0x73, 0xec,
+    0x76, 0xfc, 0x8b, 0xa1, 0x16, 0xd9, 0x3f, 0x27, 0x3b, 0xfe, 0x51, 0x55, 0x4b, 0x49, 0xec, 0x6b, 0x24, 0xc7, 0xdf, 0xaf,
+    0x93, 0xc9, 0x1d, 0xbb, 0x1d, 0xff, 0xf8, 0xe8, 0xd6, 0x3f, 0xf5, 0x04, 0x0d, 0x3d, 0xe0, 0x38, 0x03, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x2c, 0x46, 0x45, 0x2a, 0x5b, 0x65, 0x34, 0xaf, 0x16, 0xfa, 0x52, 0x50, 0x6c,
+    0xeb, 0x27, 0x72, 0x9c, 0x7e, 0xf6, 0x6b, 0x77, 0x79, 0xdf, 0x47, 0xad, 0x56, 0x64, 0x6c, 0x67, 0xd7, 0x38, 0x9b, 0x53,
+    0xf9, 0x58, 0xba, 0x17, 0xdf, 0x6f, 0x50, 0x95, 0x37, 0x4d, 0x15, 0x4e, 0x37, 0xde, 0xdf, 0xee, 0x5b, 0x5e, 0x15, 0xba,
+    0x9f, 0x4c, 0x17, 0x8b, 0x21, 0xf7, 0xeb, 0x08, 0x8c, 0xad, 0x89, 0xa3, 0xaa, 0x8f, 0x1a, 0xfe, 0xa5, 0x74, 0xdc, 0xcb,
+    0x61, 0xd6, 0x53, 0x49, 0x67, 0x0c, 0xd7, 0x5c, 0xb7, 0x77, 0x07, 0x74, 0x79, 0x8d, 0x6d, 0x8f, 0xb5, 0x4a, 0x7d, 0xcb,
+    0x22, 0xab, 0x99, 0xf9, 0xbd, 0x6a, 0x86, 0xa8, 0x1a, 0x66, 0xe7, 0x8f, 0xd4, 0xd8, 0x54, 0xb5, 0xa4, 0x45, 0xf4, 0xd1,
+    0xc2, 0xbf, 0x09, 0x1f, 0x3b, 0x75, 0xa4, 0x3c, 0x3d, 0x5b, 0x3b, 0xc0, 0xaa, 0xe4, 0xd6, 0x12, 0x6a, 0x95, 0xf8, 0xbd,
+    0x6a, 0x2c, 0xe5, 0x81, 0xdd, 0x9b, 0xd7, 0x9b, 0x45, 0xef, 0x19, 0xdf, 0x9d, 0x21, 0xcb, 0x61, 0x66, 0xee, 0xce, 0x59,
+    0x8c, 0x19, 0x2d, 0xee, 0x1d, 0x70, 0x6e, 0x25, 0x8f, 0xf3, 0x94, 0x07, 0xb3, 0xb3, 0xc8, 0xce, 0x2c, 0xcf, 0xc7, 0x7f,
+    0x39, 0xe4, 0x09, 0x52, 0xc2, 0x5a, 0x19, 0xdf, 0x0e, 0x38, 0x3b, 0xfe, 0x67, 0x29, 0x0f, 0xb2, 0x1c, 0x38, 0xfa, 0x43,
+    0xe3, 0xbf, 0xe7, 0x36, 0xe3, 0x8f, 0xa5, 0x5f, 0x31, 0xba, 0xdc, 0x72, 0x67, 0xc8, 0x3e, 0x67, 0x71, 0xff, 0x4f, 0xe6,
+    0x19, 0xf1, 0x8f, 0x39, 0xd4, 0x14, 0xb7, 0x97, 0xd0, 0x5d, 0xf1, 0xf7, 0xd7, 0x91, 0x8a, 0x9d, 0x93, 0xaf, 0xbd, 0xbc,
+    0x22, 0xfe, 0xf0, 0x3e, 0xce, 0x47, 0x0d, 0x7d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x29, 0xff,
+    0xc1, 0xa3, 0x5e, 0xb9, 0xbf, 0xa5, 0xee, 0x64, 0xb5, 0x4a, 0xb8, 0xa5, 0xa4, 0xf5, 0x69, 0x69, 0x8c, 0x8e, 0xd6, 0x3c,
+    0xfb, 0xea, 0xbb, 0x84, 0xd5, 0x3f, 0xbf, 0x73, 0xd0, 0xeb, 0xbf, 0x35, 0x29, 0xb3, 0x9e, 0xdf, 0x72, 0xfe, 0xaf, 0xf9,
+    0xcf, 0xf0, 0x1b, 0xb0, 0x1c, 0x22, 0xea, 0xb4, 0xb6, 0xa2, 0xc9, 0x9c, 0x6a, 0x97, 0x9e, 0x01, 0x2d, 0x90, 0x83, 0xb8,
+    0xdf, 0x65, 0xa7, 0x39, 0xeb, 0x50, 0xd5, 0x0b, 0x3d, 0x36, 0xfa, 0x8e, 0x1e, 0xc6, 0xa7, 0xa1, 0xd1, 0x1e, 0x0a, 0x8b,
+    0xeb, 0xb5, 0xaa, 0x26, 0x51, 0x79, 0xcb, 0xf8, 0x2f, 0x4e, 0x6d, 0x5f, 0x9f, 0xf0, 0xcb, 0xc8, 0xf2, 0xd8, 0x28, 0x01,
+    0x85, 0x4f, 0x31, 0xea, 0x8f, 0xf9, 0xe2, 0xaf, 0xaa, 0xd5, 0xf9, 0xef, 0x8d, 0x57, 0xb8, 0x6c, 0xe4, 0x57, 0xb0, 0xc9,
+    0x73, 0xf3, 0xc8, 0xf7, 0xd8, 0xb0, 0x32, 0xe5, 0x5a, 0x57, 0xe9, 0x8b, 0xbf, 0xd7, 0xdb, 0xe1, 0x8a, 0xf8, 0x47, 0x6b,
+    0xdb, 0xe4, 0x3a, 0x73, 0x94, 0x87, 0x78, 0x6c, 0x78, 0x1d, 0x42, 0x8e, 0xdf, 0xff, 0xb3, 0xf5, 0x08, 0x79, 0x99, 0xea,
+    0x6b, 0x7c, 0x36, 0x22, 0x3a, 0x9d, 0xb3, 0x32, 0xf3, 0xfe, 0xec, 0xbc, 0x47, 0x5b, 0xd9, 0xdf, 0x2a, 0xfe, 0x4f, 0xd6,
+    0xf6, 0x3c, 0x89, 0x33, 0x6b, 0x9e, 0xa1, 0xfe, 0x79, 0xd7, 0x3d, 0xcd, 0x1a, 0xfc, 0x6c, 0x95, 0x17, 0x6b, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe7, 0x38, 0x00, 0x2d, 0xd2, 0xd3, 0x66, 0xbe, 0x87, 0x9a, 0xe6, 0x2f, 0xb4, 0x77,
+    0x4e, 0x75, 0xd6, 0x25, 0x2b, 0x6e, 0x7d, 0xce, 0xb1, 0xda, 0x63, 0xbe, 0x95, 0x8e, 0xf4, 0x35, 0xb7, 0x5e, 0x33, 0xfa,
+    0x2f, 0x4b, 0x45, 0xd1, 0xa6, 0x9e, 0xae, 0x0f, 0xe3, 0x79, 0x76, 0xfb, 0x9c, 0xc3, 0x87, 0xb3, 0x7a, 0x96, 0x7d, 0x8e,
+    0xaa, 0x08, 0xa6, 0xfa, 0x2b, 0x9f, 0x2a, 0x89, 0xe6, 0xd2, 0xe7, 0xec, 0xbf, 0x93, 0xfa, 0x4d, 0x77, 0x73, 0x57, 0x04,
+    0xb0, 0xab, 0x88, 0x95, 0xc3, 0x1e, 0x3e, 0x31, 0xe7, 0x94, 0x35, 0xbf, 0xd6, 0xa6, 0xde, 0xbf, 0x1a, 0x2e, 0x32, 0x4d,
+    0xea, 0x5f, 0xb4, 0xf7, 0x8b, 0x75, 0x4e, 0x79, 0x65, 0xfd, 0xea, 0xe6, 0xf5, 0xb2, 0xad, 0xc2, 0x2a, 0xa6, 0x37, 0x8d,
+    0xd6, 0x09, 0x95, 0x5d, 0x5f, 0xa3, 0xad, 0xb5, 0xe8, 0xc2, 0x53, 0xa8, 0x8b, 0x2b, 0x4d, 0x8d, 0x5b, 0x1d, 0xcf, 0x8d,
+    0xbf, 0x76, 0x41, 0x1a, 0x93, 0x33, 0xff, 0xad, 0x20, 0x2c, 0x8e, 0x3b, 0xa5, 0xe5, 0x7e, 0x54, 0x0c, 0xcf, 0xa6, 0x2e,
+    0xe3, 0xa9, 0xaf, 0xbd, 0x2e, 0x77, 0xa7, 0xb5, 0x2e, 0x7d, 0x77, 0xe7, 0xa8, 0x5c, 0x70, 0x71, 0xad, 0x91, 0xaa, 0x14,
+    0x78, 0xcd, 0xf5, 0xaf, 0x34, 0x14, 0x7d, 0x5a, 0x75, 0x62, 0xed, 0xed, 0x3d, 0xc7, 0x24, 0xbf, 0xe2, 0x73, 0x8d, 0xc7,
+    0x76, 0xfc, 0x6b, 0x9a, 0x1a, 0x60, 0x7c, 0xaf, 0x68, 0x0f, 0x7e, 0xb2, 0xce, 0xdf, 0xff, 0xd5, 0x27, 0x70, 0x99, 0xdc,
+    0xb5, 0xc7, 0xe2, 0x5f, 0x4c, 0xcf, 0x96, 0x72, 0xf0, 0xfa, 0xb7, 0xd4, 0x9b, 0xb6, 0xcb, 0x9a, 0xff, 0xfa, 0xef, 0x52,
+    0x6d, 0xf7, 0xec, 0xf8, 0xab, 0x71, 0xcf, 0xce, 0xe7, 0x58, 0xfc, 0x6d, 0x85, 0xd2, 0x6c, 0xaf, 0xf6, 0xdc, 0x86, 0xeb,
+    0xbb, 0x97, 0xf5, 0xad, 0xac, 0x7c, 0xbf, 0xa6, 0x5e, 0xe0, 0x1b, 0x74, 0xc5, 0x3b, 0xdd, 0xad, 0x83, 0xfa, 0x70, 0xd7,
+    0xa9, 0xd2, 0xca, 0x39, 0x6f, 0x8d, 0xbc, 0xa8, 0x9e, 0x6e, 0x4c, 0x7f, 0x37, 0x39, 0xaa, 0x2d, 0xca, 0x7a, 0xa7, 0x71,
+    0xa2, 0x5a, 0xe9, 0xc9, 0x3a, 0xa8, 0x98, 0xcb, 0xde, 0x7b, 0xeb, 0xba, 0xd0, 0x8c, 0xcc, 0xc4, 0x32, 0xa6, 0x9a, 0x04,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xfa, 0xc7, 0xaa, 0x5c, 0x35, 0x77, 0xfe, 0xbc, 0x72, 0xa5, 0xc8,
+    0x5e, 0xeb, 0x44, 0x05, 0xad, 0xe2, 0xd6, 0xbe, 0x54, 0xb7, 0xe6, 0xc9, 0x56, 0xe9, 0xe8, 0xe3, 0x79, 0xda, 0xa7, 0x88,
+    0xea, 0x68, 0x09, 0x3f, 0xa3, 0x5e, 0xff, 0x34, 0xb3, 0x3e, 0x8f, 0xfd, 0xec, 0x71, 0x38, 0x54, 0x30, 0x5d, 0xd6, 0x7b,
+    0x5a, 0xcf, 0x68, 0x32, 0x1f, 0x3c, 0x44, 0x46, 0xa8, 0x99, 0x39, 0x8c, 0x26, 0x8f, 0xff, 0x23, 0xa3, 0xfc, 0x21, 0xdd,
+    0x7a, 0xb6, 0xcf, 0x1a, 0x9f, 0xc7, 0xbb, 0x6b, 0x6c, 0xc3, 0xac, 0xad, 0x36, 0x8c, 0x36, 0xa5, 0x08, 0xd0, 0x33, 0x9a,
+    0x89, 0x7f, 0x39, 0x90, 0x37, 0x1d, 0x32, 0x4f, 0xbf, 0xad, 0xc4, 0xd2, 0x1e, 0x3a, 0x76, 0x9b, 0xd2, 0xc8, 0x2c, 0xa6,
+    0x43, 0x96, 0xde, 0xb7, 0xde, 0x1d, 0xa3, 0x62, 0xd9, 0xa4, 0x5e, 0xc9, 0xd6, 0xbe, 0x54, 0x43, 0xe1, 0x60, 0xf9, 0x0c,
+    0x79, 0x47, 0xfe, 0xa4, 0xf8, 0x77, 0xd3, 0xf7, 0x40, 0x6b, 0x48, 0xf6, 0x7c, 0x6e, 0xb4, 0xe2, 0xaa, 0x84, 0xee, 0xbe,
+    0x39, 0x2d, 0xf6, 0x55, 0xde, 0x64, 0x86, 0xdb, 0x5a, 0x07, 0xfb, 0x53, 0x3c, 0xa2, 0x49, 0xe8, 0xa7, 0xc4, 0xbf, 0x06,
+    0xf5, 0x56, 0x3e, 0xcf, 0x26, 0x3b, 0x8b, 0xa8, 0xfd, 0x9a, 0xae, 0x72, 0xae, 0x5b, 0x02, 0xb9, 0x4f, 0x4b, 0x4b, 0xb3,
+    0x98, 0x15, 0xeb, 0xa2, 0x31, 0xec, 0x87, 0xe2, 0x7f, 0x44, 0xb9, 0xf2, 0xf7, 0x1d, 0xa3, 0x38, 0x73, 0xe7, 0x96, 0x8a,
+    0x48, 0xaf, 0x56, 0xa4, 0xbe, 0xe3, 0x55, 0x2d, 0xd6, 0x3a, 0xd8, 0xfd, 0x1d, 0xd1, 0x25, 0xdd, 0xed, 0xa2, 0xf3, 0x75,
+    0x8d, 0x77, 0xb7, 0x8b, 0x8e, 0xad, 0x15, 0xea, 0xb7, 0x3b, 0x57, 0xfa, 0xef, 0x19, 0x5d, 0x46, 0x59, 0x5f, 0x05, 0xfd,
+    0xe0, 0x15, 0x7c, 0x7f, 0xfc, 0x63, 0xaa, 0x8f, 0x12, 0x50, 0x91, 0x5c, 0xeb, 0x69, 0xe8, 0x6f, 0xe9, 0xc6, 0x3a, 0x58,
+    0xae, 0x51, 0x3f, 0x51, 0x49, 0x11, 0xaf, 0xd5, 0xf7, 0xcc, 0xf8, 0xef, 0x69, 0xf5, 0x2c, 0x2f, 0xd1, 0xfb, 0x9d, 0xd0,
+    0x9e, 0xa4, 0x2f, 0xfa, 0x3f, 0xc6, 0xff, 0x1d, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x64, 0x07,
+    0x20, 0x5b, 0x6b, 0x52, 0x53, 0x74, 0x35, 0xfa, 0x68, 0x75, 0x7a, 0x12, 0x55, 0xf3, 0xac, 0x7b, 0xf5, 0x3e, 0xc5, 0x7c,
+    0x3a, 0x7b, 0xfe, 0x98, 0xed, 0xe8, 0x7b, 0x15, 0x2f, 0x96, 0x16, 0xa2, 0xba, 0xde, 0xa5, 0x1a, 0xef, 0xf0, 0x21, 0x9e,
+    0x74, 0xfa, 0x75, 0x38, 0xf7, 0xeb, 0x7d, 0x94, 0xa2, 0xa3, 0x1a, 0x1e, 0x2f, 0x99, 0x63, 0xb6, 0x15, 0x5c, 0x5e, 0xc5,
+    0x8b, 0xa5, 0x36, 0xf0, 0xa9, 0x53, 0x9a, 0x68, 0xd1, 0xfd, 0xeb, 0x27, 0xdd, 0xcd, 0xf0, 0x0d, 0xba, 0x5b, 0xef, 0xa3,
+    0x6b, 0x4e, 0x29, 0x1d, 0x48, 0x95, 0x3b, 0x60, 0x75, 0xb2, 0xaa, 0xae, 0x31, 0x2f, 0x01, 0xe5, 0x48, 0x44, 0xd7, 0xe2,
+    0x75, 0x32, 0x53, 0xc7, 0x57, 0x0d, 0x9c, 0xf6, 0x24, 0xf2, 0x39, 0x0d, 0xdd, 0xaf, 0xf7, 0xe9, 0xa6, 0xcb, 0x54, 0x56,
+    0xee, 0x20, 0xe2, 0x03, 0xb7, 0xe7, 0xcf, 0xe3, 0xab, 0xd5, 0xa7, 0x3c, 0x83, 0xbc, 0xc7, 0x17, 0xe9, 0xe3, 0x34, 0x8c,
+    0xec, 0xf8, 0x13, 0x72, 0xf7, 0xbe, 0xac, 0xce, 0xaa, 0x7a, 0x18, 0x37, 0x66, 0x95, 0xb4, 0x72, 0x24, 0xd2, 0xb2, 0xec,
+    0x54, 0x57, 0xcb, 0x3a, 0xfe, 0xdc, 0xdc, 0xbd, 0x37, 0xfe, 0xe3, 0xf6, 0xf8, 0xdb, 0x2a, 0x39, 0x5f, 0x0b, 0xf1, 0xf7,
+    0xc6, 0xbf, 0x27, 0xde, 0xff, 0x63, 0xf1, 0xd7, 0x2a, 0x39, 0x7f, 0xcb, 0x15, 0xf1, 0x1f, 0x66, 0xe5, 0xbc, 0x77, 0x8b,
+    0x7f, 0xb9, 0x59, 0x55, 0x90, 0x5b, 0xc5, 0xf3, 0xfc, 0xf8, 0xc7, 0x54, 0x1d, 0xe5, 0xe6, 0x16, 0xbf, 0x6e, 0xe3, 0x9a,
+    0xf7, 0xcf, 0xaf, 0xe2, 0x79, 0xee, 0xf1, 0x68, 0xfc, 0xf3, 0xe7, 0x38, 0x52, 0x7c, 0xab, 0xda, 0x45, 0x71, 0x01, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x3f, 0x35, 0x49, 0x99, 0x12, 0x75, 0xa8, 0xb9, 0xd7, 0x73, 0xc7, 0xf2, 0x17,
+    0xd2, 0x2d, 0xd9, 0x33, 0xf2, 0x6a, 0xaa, 0x72, 0x7c, 0x80, 0x2c, 0xcd, 0x40, 0x95, 0x1a, 0x1c, 0x7f, 0x06, 0xdc, 0xd6,
+    0x06, 0xb5, 0x80, 0x06, 0xa7, 0x1a, 0x1a, 0x9c, 0xe6, 0xf2, 0xdc, 0xe8, 0x86, 0xbf, 0x90, 0xe5, 0xac, 0x90, 0x39, 0x23,
+    0x3d, 0xee, 0x26, 0x76, 0x45, 0x13, 0x7a, 0xaa, 0x9a, 0x74, 0xfd, 0x6b, 0x9d, 0x49, 0x34, 0x03, 0x1e, 0xd3, 0xe0, 0x34,
+    0x97, 0x06, 0xc7, 0xaa, 0x1d, 0x55, 0x77, 0x6a, 0x73, 0xe9, 0x9c, 0x4a, 0xae, 0x8b, 0x90, 0x6f, 0xdc, 0x7a, 0x05, 0xe6,
+    0xf5, 0x54, 0xd7, 0xfd, 0x16, 0xb6, 0x08, 0xa5, 0x5b, 0xe4, 0x0e, 0x1b, 0x69, 0xd1, 0x7e, 0x10, 0x56, 0xcd, 0xbe, 0x48,
+    0xfd, 0xa2, 0xdc, 0x71, 0x5b, 0x75, 0xf8, 0x8a, 0xc3, 0xd9, 0x2a, 0x33, 0x6b, 0x16, 0x73, 0xaf, 0xd9, 0xae, 0x97, 0x93,
+    0x5b, 0x07, 0x48, 0xb7, 0x14, 0xe9, 0x91, 0x52, 0xcc, 0x4a, 0x54, 0xdd, 0x5d, 0x05, 0x27, 0x5b, 0x3b, 0x54, 0x0c, 0x95,
+    0xe0, 0xfc, 0x7d, 0x29, 0x33, 0x6b, 0x16, 0x73, 0xaf, 0x59, 0x8c, 0xd5, 0x3f, 0xbf, 0x65, 0x84, 0xaa, 0x4a, 0xd9, 0x99,
+    0xb3, 0x7e, 0x89, 0x8b, 0x50, 0x37, 0xf4, 0x36, 0x77, 0xc4, 0x7f, 0x7f, 0xbd, 0xfc, 0x77, 0x86, 0x6b, 0xae, 0x23, 0xef,
+    0x0e, 0x8c, 0x7a, 0x6e, 0x44, 0x94, 0x93, 0x96, 0xc7, 0x99, 0xcf, 0x61, 0xed, 0xec, 0xf8, 0x97, 0x9d, 0xfb, 0xbf, 0x76,
+    0xf1, 0x1a, 0xb7, 0x6a, 0x30, 0xfc, 0xf7, 0xeb, 0x3c, 0xdf, 0xbb, 0x23, 0x2d, 0x76, 0x1d, 0xbe, 0xf3, 0xe2, 0x9f, 0xa9,
+    0x26, 0xb9, 0x56, 0x67, 0x32, 0x92, 0x94, 0x4e, 0x4f, 0x98, 0x91, 0x77, 0xa7, 0xe5, 0xc5, 0xff, 0xa7, 0xa8, 0x49, 0x50,
+    0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xcf, 0xd5, 0xff, 0xf8, 0x3c, 0x78, 0x9e, 0xec, 0xb3, 0x93, 0xe7,
+    0x40, 0x94, 0xad, 0xe2, 0x89, 0xa9, 0xa9, 0x96, 0x03, 0x95, 0xbd, 0xe6, 0xf5, 0x3f, 0x4d, 0xaa, 0x5b, 0x54, 0xed, 0xaa,
+    0x7a, 0x81, 0x22, 0x26, 0xa2, 0xf1, 0x51, 0x47, 0xad, 0xdc, 0xcf, 0x35, 0x2a, 0x1e, 0xdd, 0xdb, 0xd8, 0xd1, 0x66, 0xb5,
+    0x93, 0x9f, 0x58, 0x16, 0x63, 0xf6, 0x55, 0xee, 0x98, 0xf6, 0x48, 0x8d, 0x4f, 0x44, 0x5d, 0x91, 0xab, 0xe2, 0xd1, 0x2b,
+    0xda, 0xa4, 0xc2, 0xc0, 0xae, 0xbf, 0x58, 0x4e, 0x7f, 0x2e, 0xae, 0xc7, 0xdb, 0x6f, 0x54, 0xf2, 0x44, 0xb4, 0x32, 0xdd,
+    0x54, 0xf1, 0x74, 0x57, 0xc4, 0x22, 0x2d, 0x7b, 0xd9, 0xd4, 0x92, 0xa8, 0xa6, 0x3b, 0x37, 0xfe, 0x11, 0x85, 0xc5, 0x13,
+    0x7c, 0x76, 0x8a, 0xe1, 0x72, 0xf2, 0x8e, 0xb5, 0x60, 0xee, 0x8a, 0xff, 0x7b, 0xae, 0x98, 0xed, 0x4a, 0xf1, 0x6e, 0x51,
+    0x1e, 0x41, 0xc5, 0x4a, 0x4e, 0xfc, 0x9f, 0xbd, 0x2e, 0x79, 0x3e, 0x1b, 0xd7, 0x8c, 0x2c, 0xa6, 0x09, 0xbb, 0x22, 0x63,
+    0x3d, 0x12, 0xaf, 0x98, 0xbb, 0x35, 0x3e, 0x11, 0x9f, 0x8d, 0xab, 0xd4, 0x47, 0x51, 0x9d, 0xc5, 0x3d, 0x55, 0x9d, 0x62,
+    0x57, 0xcc, 0xdd, 0x1a, 0x9f, 0x27, 0xab, 0x78, 0xfc, 0x6a, 0x3a, 0xad, 0x58, 0x87, 0x77, 0x43, 0xc7, 0xb2, 0xb1, 0x3a,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0xe6, 0xb9, 0xfd, 0x61, 0x1e, 0x88, 0x3f, 0xbc, 0xda, 0xf8, 0xff, 0x17 };
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xd6, 0x3f,
+    0xac, 0x03, 0xf1, 0x87, 0x1f, 0x1b, 0xff, 0x7f, 0x01 };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle bluishFontRecs[189] = {
@@ -284,103 +275,103 @@
     { 240, 76, 2 , 8 },
     { 4, 94, 5 , 8 },
     { 17, 94, 5 , 8 },
-    { 30, 94, 8 , 8 },
-    { 46, 94, 6 , 8 },
-    { 60, 94, 5 , 10 },
-    { 73, 94, 5 , 10 },
-    { 86, 94, 5 , 9 },
-    { 99, 94, 7 , 8 },
-    { 114, 94, 4 , 6 },
-    { 126, 94, 5 , 4 },
-    { 139, 94, 5 , 3 },
-    { 152, 94, 7 , 8 },
-    { 167, 94, 5 , 2 },
-    { 180, 94, 4 , 4 },
-    { 192, 94, 6 , 8 },
-    { 206, 94, 4 , 6 },
-    { 218, 94, 4 , 6 },
-    { 230, 94, 8 , 8 },
-    { 4, 112, 5 , 9 },
-    { 17, 112, 6 , 8 },
-    { 31, 112, 2 , 2 },
-    { 41, 112, 8 , 8 },
-    { 57, 112, 3 , 6 },
-    { 68, 112, 4 , 6 },
-    { 80, 112, 5 , 4 },
-    { 93, 112, 6 , 8 },
-    { 107, 112, 6 , 6 },
-    { 121, 112, 5 , 10 },
-    { 134, 112, 5 , 8 },
-    { 147, 112, 5 , 10 },
-    { 160, 112, 5 , 10 },
-    { 173, 112, 5 , 10 },
-    { 186, 112, 5 , 10 },
-    { 199, 112, 5 , 10 },
-    { 212, 112, 5 , 10 },
-    { 225, 112, 6 , 8 },
-    { 239, 112, 5 , 9 },
+    { 30, 94, 0 , 0 },
+    { 38, 94, 6 , 8 },
+    { 52, 94, 5 , 10 },
+    { 65, 94, 5 , 10 },
+    { 78, 94, 5 , 9 },
+    { 91, 94, 7 , 8 },
+    { 106, 94, 4 , 6 },
+    { 118, 94, 5 , 4 },
+    { 131, 94, 5 , 3 },
+    { 144, 94, 7 , 8 },
+    { 159, 94, 5 , 2 },
+    { 172, 94, 4 , 4 },
+    { 184, 94, 6 , 8 },
+    { 198, 94, 4 , 6 },
+    { 210, 94, 4 , 6 },
+    { 222, 94, 0 , 0 },
+    { 230, 94, 5 , 9 },
+    { 4, 112, 6 , 8 },
+    { 18, 112, 2 , 2 },
+    { 28, 112, 0 , 0 },
+    { 36, 112, 3 , 6 },
+    { 47, 112, 4 , 6 },
+    { 59, 112, 5 , 4 },
+    { 72, 112, 6 , 8 },
+    { 86, 112, 6 , 6 },
+    { 100, 112, 5 , 10 },
+    { 113, 112, 5 , 8 },
+    { 126, 112, 5 , 10 },
+    { 139, 112, 5 , 10 },
+    { 152, 112, 5 , 10 },
+    { 165, 112, 5 , 10 },
+    { 178, 112, 5 , 10 },
+    { 191, 112, 5 , 10 },
+    { 204, 112, 6 , 8 },
+    { 218, 112, 5 , 9 },
+    { 231, 112, 5 , 10 },
     { 4, 130, 5 , 10 },
     { 17, 130, 5 , 10 },
     { 30, 130, 5 , 10 },
-    { 43, 130, 5 , 10 },
-    { 56, 130, 4 , 10 },
-    { 68, 130, 4 , 10 },
-    { 80, 130, 4 , 10 },
-    { 92, 130, 4 , 10 },
-    { 104, 130, 6 , 8 },
+    { 43, 130, 4 , 10 },
+    { 55, 130, 4 , 10 },
+    { 67, 130, 4 , 10 },
+    { 79, 130, 4 , 10 },
+    { 91, 130, 6 , 8 },
+    { 105, 130, 5 , 10 },
     { 118, 130, 5 , 10 },
     { 131, 130, 5 , 10 },
     { 144, 130, 5 , 10 },
     { 157, 130, 5 , 10 },
     { 170, 130, 5 , 10 },
-    { 183, 130, 5 , 10 },
-    { 196, 130, 4 , 4 },
+    { 183, 130, 4 , 4 },
+    { 195, 130, 5 , 10 },
     { 208, 130, 5 , 10 },
     { 221, 130, 5 , 10 },
     { 234, 130, 5 , 10 },
     { 4, 148, 5 , 10 },
     { 17, 148, 5 , 10 },
-    { 30, 148, 5 , 10 },
+    { 30, 148, 5 , 8 },
     { 43, 148, 5 , 8 },
-    { 56, 148, 5 , 8 },
+    { 56, 148, 5 , 9 },
     { 69, 148, 5 , 9 },
     { 82, 148, 5 , 9 },
     { 95, 148, 5 , 9 },
-    { 108, 148, 5 , 9 },
-    { 121, 148, 5 , 8 },
-    { 134, 148, 5 , 10 },
-    { 147, 148, 6 , 6 },
-    { 161, 148, 5 , 7 },
+    { 108, 148, 5 , 8 },
+    { 121, 148, 5 , 10 },
+    { 134, 148, 6 , 6 },
+    { 148, 148, 5 , 7 },
+    { 161, 148, 5 , 9 },
     { 174, 148, 5 , 9 },
     { 187, 148, 5 , 9 },
-    { 200, 148, 5 , 9 },
-    { 213, 148, 5 , 8 },
-    { 226, 148, 3 , 9 },
-    { 237, 148, 3 , 9 },
-    { 4, 166, 4 , 9 },
-    { 16, 166, 4 , 8 },
-    { 28, 166, 5 , 9 },
-    { 41, 166, 5 , 9 },
-    { 54, 166, 5 , 9 },
-    { 67, 166, 5 , 9 },
-    { 80, 166, 5 , 9 },
-    { 93, 166, 5 , 9 },
-    { 106, 166, 5 , 8 },
-    { 119, 166, 4 , 6 },
-    { 131, 166, 5 , 8 },
-    { 144, 166, 5 , 9 },
-    { 157, 166, 5 , 9 },
-    { 170, 166, 5 , 9 },
-    { 183, 166, 5 , 8 },
-    { 196, 166, 5 , 10 },
-    { 209, 166, 5 , 10 },
-    { 222, 166, 5 , 9 },
+    { 200, 148, 5 , 8 },
+    { 213, 148, 3 , 9 },
+    { 224, 148, 3 , 9 },
+    { 235, 148, 4 , 9 },
+    { 4, 166, 4 , 8 },
+    { 16, 166, 5 , 9 },
+    { 29, 166, 5 , 9 },
+    { 42, 166, 5 , 9 },
+    { 55, 166, 5 , 9 },
+    { 68, 166, 5 , 9 },
+    { 81, 166, 5 , 9 },
+    { 94, 166, 5 , 8 },
+    { 107, 166, 4 , 6 },
+    { 119, 166, 5 , 8 },
+    { 132, 166, 5 , 9 },
+    { 145, 166, 5 , 9 },
+    { 158, 166, 5 , 9 },
+    { 171, 166, 5 , 8 },
+    { 184, 166, 5 , 10 },
+    { 197, 166, 5 , 10 },
+    { 210, 166, 5 , 9 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo bluishFontGlyphs[189] = {
-    { 32, 0, 9, 5, { 0 }},
+    { 32, 0, 0, 5, { 0 }},
     { 33, 0, 1, 2, { 0 }},
     { 34, 0, 1, 4, { 0 }},
     { 35, 0, 1, 6, { 0 }},
@@ -478,7 +469,7 @@
     { 161, 0, 1, 2, { 0 }},
     { 162, 0, 2, 5, { 0 }},
     { 163, 0, 1, 5, { 0 }},
-    { 8364, 0, 1, 7, { 0 }},
+    { 8364, 0, 0, 0, { 0 }},
     { 165, 0, 1, 6, { 0 }},
     { 352, 0, -1, 5, { 0 }},
     { 167, 0, 0, 5, { 0 }},
@@ -493,11 +484,11 @@
     { 177, 0, 1, 6, { 0 }},
     { 178, 0, -1, 4, { 0 }},
     { 179, 0, -1, 4, { 0 }},
-    { 381, 0, 1, 7, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
     { 181, 0, 1, 5, { 0 }},
     { 182, 0, 1, 6, { 0 }},
     { 183, 0, 4, 2, { 0 }},
-    { 382, 0, 1, 7, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
     { 185, 0, -1, 3, { 0 }},
     { 186, 0, -1, 4, { 0 }},
     { 187, 0, 3, 5, { 0 }},
diff --git a/raygui/styles/candy/style_candy.h b/raygui/styles/candy/style_candy.h
--- a/raygui/styles/candy/style_candy.h
+++ b/raygui/styles/candy/style_candy.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,144 +15,137 @@
 
 // Custom style name: Candy
 static const GuiStyleProp candyStyleProps[CANDY_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0xe58b68ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0xfeda96ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xe59b5fff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xee813fff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xfcd85bff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xfc6955ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xb34848ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xeb7272ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0xbd4a4aff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x94795dff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0xc2a37aff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x9c8369ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x0000000f },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0xd77575ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0xfff5e1ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000016 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0xe58b68ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0xfeda96ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xe59b5fff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xee813fff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xfcd85bff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xfc6955ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xb34848ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xeb7272ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xbd4a4aff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x94795dff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0xc2a37aff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x9c8369ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x0000000f },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0xd77575ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0xfff5e1ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000016 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
 // WARNING: This style uses a custom font: "v5easter.ttf" (size: 15, spacing: 0)
 
-#define CANDY_STYLE_FONT_ATLAS_COMP_SIZE 2260
+#define CANDY_STYLE_FONT_ATLAS_COMP_SIZE 2110
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char candyFontData[CANDY_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0x4d, 0x92, 0xd4, 0x38, 0x10, 0x06, 0x50, 0xdd, 0x80, 0xfb, 0xcc, 0x61, 0xe6, 0x4a, 0x73, 0x45, 0x8e, 0xc0, 0x86,
-    0x25, 0xa1, 0x89, 0x61, 0x88, 0x20, 0x80, 0x2e, 0x4b, 0x4a, 0xa5, 0x6c, 0xd9, 0xf5, 0x78, 0x1b, 0xa2, 0xdd, 0x5d, 0x55,
-    0xfe, 0xf9, 0x2c, 0xc9, 0x2e, 0xa5, 0x6b, 0x01, 0x00, 0x00, 0x00, 0xde, 0xdc, 0x7f, 0xff, 0xfe, 0xfc, 0x59, 0xf9, 0xe0,
-    0xa7, 0x3f, 0x7f, 0xb7, 0x7e, 0xb8, 0xf4, 0x68, 0x49, 0xfc, 0x2f, 0x8f, 0x3f, 0x49, 0xdf, 0x2b, 0x94, 0x83, 0x25, 0xed,
-    0xcf, 0xfc, 0xfb, 0xcf, 0x6a, 0xca, 0xbb, 0xbf, 0x7a, 0x9d, 0xd6, 0x27, 0x8a, 0x2c, 0x3b, 0x7a, 0xd5, 0x7a, 0xb0, 0x4e,
-    0xa5, 0x63, 0x49, 0x0d, 0xad, 0x5d, 0x9d, 0xde, 0xab, 0x63, 0x7b, 0xa7, 0xbd, 0xfe, 0x65, 0x78, 0x1d, 0xcb, 0x82, 0x65,
-    0x75, 0x72, 0x1f, 0xaf, 0xcc, 0xff, 0xef, 0xff, 0x32, 0xf3, 0x7f, 0xfc, 0x9a, 0x33, 0xc7, 0xc4, 0xcf, 0xb5, 0xa9, 0x09,
-    0xf9, 0x1f, 0x3f, 0x8b, 0x1c, 0xaf, 0xc1, 0xeb, 0xa3, 0xb8, 0x0c, 0xff, 0x4d, 0xfb, 0xfc, 0x9a, 0xf9, 0xaa, 0x2b, 0x97,
-    0xf5, 0xa5, 0x76, 0xfe, 0x38, 0x6f, 0x7d, 0x9a, 0xb9, 0x7d, 0x78, 0xe6, 0xb2, 0xdc, 0xfc, 0xb7, 0x7e, 0x37, 0x7a, 0x66,
-    0x8f, 0xf7, 0x0e, 0x7a, 0x5b, 0xf3, 0x3a, 0x74, 0xec, 0xb7, 0x5b, 0xb1, 0x9c, 0x2d, 0x3c, 0xba, 0xc6, 0xc7, 0xe7, 0x92,
-    0xd6, 0xd2, 0xf9, 0x36, 0x77, 0xe4, 0xd8, 0xb8, 0xe2, 0x28, 0x6e, 0xef, 0xcd, 0x8c, 0x76, 0xa9, 0xfe, 0xb2, 0xb5, 0xe5,
-    0xbf, 0xf7, 0xa8, 0xad, 0x8b, 0x5b, 0xda, 0x32, 0xdc, 0xd2, 0xc6, 0xfa, 0x2c, 0x79, 0x5b, 0x38, 0x2b, 0xff, 0x75, 0x22,
-    0xfd, 0x25, 0xd0, 0xe3, 0x3c, 0x3e, 0x6b, 0xb7, 0x3e, 0x7f, 0xf4, 0x28, 0xc9, 0x3d, 0x66, 0x67, 0xf3, 0x7f, 0xdc, 0x1f,
-    0x2e, 0x87, 0xe7, 0x9b, 0xf3, 0x7a, 0x5c, 0x3b, 0xe5, 0x3f, 0xda, 0x43, 0xee, 0x1f, 0xdd, 0xe4, 0xb5, 0xff, 0xeb, 0xf3,
-    0xdf, 0x4a, 0x64, 0x1d, 0x1c, 0xa9, 0xe6, 0xb6, 0xc6, 0x3d, 0xe3, 0xe9, 0xe8, 0x19, 0x7d, 0x7c, 0x8c, 0x7b, 0x7e, 0xff,
-    0xbf, 0x3d, 0x6e, 0xac, 0xcb, 0x46, 0x4e, 0xf9, 0x09, 0xdb, 0xb7, 0xff, 0xdf, 0xd7, 0x2f, 0x58, 0xd3, 0xff, 0x6f, 0x8d,
-    0xff, 0xcb, 0xe9, 0xad, 0x7f, 0x2b, 0x59, 0xf1, 0xbd, 0x5f, 0x43, 0x3d, 0xa0, 0xb9, 0xab, 0x0a, 0xb1, 0xfd, 0x39, 0x97,
-    0xff, 0x15, 0x7d, 0x80, 0xd1, 0xd1, 0xdf, 0x33, 0xfa, 0xff, 0x91, 0x2d, 0x18, 0xbd, 0xce, 0x9c, 0xb5, 0x66, 0xe3, 0xd7,
-    0xf9, 0xaf, 0xdb, 0x62, 0x79, 0xe7, 0xf9, 0xd9, 0x2d, 0x98, 0xff, 0xaa, 0xb1, 0x5e, 0xf5, 0xe8, 0x51, 0x5c, 0x2f, 0xbd,
-    0x37, 0x56, 0x6f, 0x71, 0xe5, 0xf4, 0x8c, 0xf6, 0xbf, 0x75, 0xfd, 0x7f, 0xe6, 0xea, 0xc0, 0xd8, 0x9d, 0x8d, 0xba, 0xe1,
-    0xb1, 0x22, 0xff, 0x77, 0x69, 0xc5, 0xb2, 0x7a, 0xd5, 0xae, 0xff, 0x45, 0xee, 0x69, 0x67, 0xf4, 0xe7, 0xee, 0x92, 0xff,
-    0xda, 0x71, 0x36, 0xed, 0x5f, 0x12, 0xbf, 0xe3, 0xbe, 0xea, 0x5b, 0x05, 0xad, 0xbb, 0x8d, 0xe7, 0xdd, 0xc5, 0xde, 0x31,
-    0xff, 0xf7, 0xb8, 0xff, 0xbf, 0xc7, 0xb6, 0x5c, 0x3b, 0x92, 0xdb, 0x31, 0xff, 0xf0, 0x1e, 0xdf, 0x26, 0x3c, 0xef, 0x5b,
-    0x8b, 0xf2, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xdd, 0x67, 0xe7, 0xc4, 0x2b,
-    0x44, 0x8e, 0xcd, 0xb9, 0xa9, 0x4b, 0xea, 0x22, 0xc5, 0xe6, 0x46, 0x9f, 0x5b, 0xc3, 0x39, 0x5a, 0x35, 0x7b, 0x6e, 0x5e,
-    0xf8, 0xeb, 0x7a, 0x42, 0x33, 0xaf, 0x7b, 0x3c, 0x17, 0x79, 0x64, 0x5e, 0x76, 0xb4, 0xd6, 0xf9, 0x68, 0x4d, 0xa0, 0x48,
-    0xed, 0xa4, 0xd9, 0xda, 0xe9, 0xe3, 0x6b, 0x1c, 0x99, 0xc1, 0x3a, 0x3b, 0xe7, 0xed, 0xac, 0x8a, 0xcd, 0xd1, 0x9c, 0xf6,
-    0x55, 0xbd, 0xad, 0xc3, 0x95, 0xea, 0x76, 0xab, 0xd3, 0x90, 0x5d, 0x35, 0x63, 0x65, 0xfe, 0x4b, 0x57, 0x6d, 0xec, 0xbe,
-    0xf9, 0xce, 0x35, 0x71, 0x46, 0xfb, 0x79, 0x35, 0xf7, 0xca, 0xf4, 0x3e, 0x19, 0x6d, 0xa9, 0x9e, 0x9f, 0xff, 0xe8, 0xba,
-    0xb7, 0xb6, 0x6a, 0xdd, 0xb6, 0x8a, 0xfd, 0x95, 0xf9, 0x8f, 0xf6, 0xed, 0x8e, 0x5b, 0xbe, 0xf1, 0x8a, 0x07, 0xe7, 0x54,
-    0xb4, 0xa9, 0x1b, 0xe5, 0x7f, 0xfc, 0xdd, 0x56, 0xce, 0x61, 0x9f, 0x6b, 0x7b, 0x6b, 0x5a, 0xfe, 0xe3, 0x7d, 0x9f, 0xdd,
-    0xda, 0xf8, 0xdc, 0xaa, 0x39, 0xeb, 0xf2, 0x5f, 0xd2, 0xab, 0x32, 0xed, 0x99, 0xff, 0x9e, 0xf6, 0x21, 0x32, 0x36, 0x18,
-    0xff, 0xcb, 0xe8, 0x19, 0x77, 0xf4, 0xdd, 0x22, 0xed, 0xff, 0xf1, 0x5a, 0xde, 0xb9, 0xfd, 0xdf, 0x25, 0xff, 0x65, 0xab,
-    0xfe, 0xff, 0xec, 0xd1, 0x38, 0x96, 0xf3, 0xeb, 0x6a, 0xb3, 0xed, 0xfe, 0x24, 0xa3, 0x91, 0x2d, 0x3e, 0x5b, 0x1d, 0x39,
-    0x7a, 0xdd, 0x21, 0xf3, 0x89, 0x4d, 0xf7, 0xba, 0xc6, 0x55, 0xde, 0x72, 0xfc, 0x1f, 0xbf, 0xee, 0x2b, 0xff, 0xb3, 0xfd,
-    0xe9, 0x72, 0x41, 0xfe, 0x57, 0x6d, 0xbb, 0x3b, 0xe7, 0x7f, 0xa6, 0xe7, 0x77, 0x56, 0xfe, 0xcb, 0xcb, 0xa7, 0x50, 0xcd,
-    0xe6, 0x3f, 0xfe, 0x6c, 0x9c, 0xd1, 0x9c, 0xd7, 0x0b, 0xef, 0x6f, 0xad, 0x38, 0x07, 0xc6, 0xef, 0x37, 0x14, 0xf9, 0xbf,
-    0x55, 0xfe, 0xaf, 0x6f, 0xff, 0x7b, 0x9f, 0xad, 0xf8, 0xde, 0xf9, 0x2f, 0x53, 0xd7, 0xf8, 0x56, 0xfc, 0xdd, 0x6e, 0xfd,
-    0xff, 0xec, 0x73, 0xe7, 0xb9, 0xe7, 0xd3, 0x75, 0x7d, 0xdc, 0xd8, 0xf5, 0xa4, 0xd8, 0xd3, 0x0c, 0x62, 0x55, 0xb3, 0xd7,
-    0x1c, 0x53, 0x25, 0xfc, 0x0c, 0xaf, 0x32, 0x7c, 0x06, 0x90, 0xff, 0x5f, 0x97, 0xac, 0x7b, 0x3a, 0x32, 0xac, 0xce, 0xd3,
-    0xbd, 0xf2, 0x3f, 0xf7, 0x4d, 0x83, 0xfc, 0xa7, 0x0d, 0xd6, 0x0b, 0xda, 0x7f, 0x58, 0x7b, 0x97, 0x3d, 0xef, 0xf7, 0xc9,
-    0xe8, 0xd3, 0x03, 0xef, 0xdb, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x38, 0x57,
-    0xe0, 0xf5, 0x2c, 0xcf, 0xe3, 0x2a, 0x18, 0x25, 0x58, 0x05, 0xbb, 0x74, 0x54, 0x4e, 0xae, 0xc1, 0xf7, 0x1b, 0x99, 0xa5,
-    0xbc, 0x5b, 0x2d, 0x82, 0xd1, 0xfa, 0x88, 0x23, 0xb5, 0xb1, 0xcb, 0x70, 0x9d, 0xaf, 0x68, 0x15, 0x9b, 0x32, 0x59, 0x73,
-    0x34, 0x3e, 0xff, 0x2e, 0x3a, 0x83, 0xfa, 0x68, 0x7f, 0x7e, 0xaa, 0xdf, 0xea, 0x97, 0xef, 0xbe, 0xd6, 0x7f, 0x86, 0xdf,
-    0xb5, 0x76, 0xcd, 0xf7, 0x9b, 0xdd, 0x27, 0x3d, 0x5b, 0x28, 0x92, 0xff, 0x1d, 0xab, 0xee, 0x65, 0x55, 0xf9, 0xb8, 0x53,
-    0xbd, 0xed, 0x91, 0x3a, 0xbc, 0xed, 0x7a, 0x1e, 0xf3, 0xf9, 0xef, 0xad, 0x29, 0x50, 0x27, 0xab, 0x7e, 0x8e, 0x9e, 0x2b,
-    0x8e, 0xb7, 0xcb, 0xd1, 0xdc, 0xe4, 0xd7, 0xad, 0xdc, 0xa7, 0xfa, 0xf9, 0xc7, 0xff, 0xfe, 0xfe, 0x30, 0xff, 0x35, 0x3c,
-    0x1b, 0xa7, 0xfd, 0x89, 0x32, 0xe7, 0x57, 0xbf, 0x47, 0xfe, 0xc7, 0xcf, 0x99, 0x77, 0xa8, 0xb7, 0x5d, 0x12, 0x7e, 0x9a,
-    0x39, 0xf3, 0x3d, 0x5a, 0x53, 0x24, 0x9a, 0xff, 0x91, 0x34, 0xbd, 0x3e, 0xcb, 0x95, 0x8e, 0x0a, 0x6a, 0xa3, 0xf9, 0x6f,
-    0xbd, 0x6b, 0x24, 0xff, 0x75, 0xb8, 0xf6, 0x4a, 0xff, 0xb6, 0x1c, 0x6b, 0x5d, 0xf6, 0xcc, 0x7f, 0xa4, 0xc7, 0x59, 0x4f,
-    0xcc, 0xff, 0x5c, 0x8d, 0xaf, 0x72, 0x7a, 0xfe, 0x73, 0x9f, 0x53, 0xd1, 0x37, 0x92, 0xe9, 0x4d, 0x76, 0xff, 0xef, 0xd6,
-    0x46, 0x5b, 0xbc, 0x2e, 0xff, 0xf5, 0x83, 0xf7, 0x5f, 0xd5, 0xfe, 0xc7, 0xeb, 0xab, 0xd6, 0x83, 0x4f, 0xf8, 0x84, 0xfc,
-    0x97, 0x4d, 0xfa, 0xff, 0xf9, 0xcf, 0x52, 0xdb, 0x33, 0xff, 0xe3, 0x3d, 0xae, 0xf1, 0x76, 0x7d, 0xac, 0xaf, 0xd0, 0x1e,
-    0x01, 0xd4, 0xc0, 0x19, 0xa0, 0x27, 0xff, 0xb1, 0xf6, 0x3f, 0x3a, 0xfe, 0x8f, 0x27, 0xa3, 0x3e, 0xb6, 0xfd, 0xcf, 0x1d,
-    0xff, 0xaf, 0xaa, 0x45, 0x98, 0xfb, 0xbc, 0xad, 0xac, 0x5a, 0x9b, 0xb1, 0xbe, 0x56, 0xb4, 0xfd, 0x6f, 0xf5, 0x0e, 0x66,
-    0xf2, 0x5f, 0x26, 0x93, 0xf8, 0xf1, 0xf5, 0xbf, 0x55, 0xed, 0xff, 0xf9, 0xd7, 0xff, 0x6b, 0x20, 0x21, 0xab, 0xaa, 0x6e,
-    0xd6, 0xe1, 0x6b, 0x31, 0xb3, 0x55, 0xd5, 0xfa, 0xef, 0x34, 0xf4, 0x9c, 0x9b, 0x5f, 0xdf, 0xa5, 0xe8, 0xd9, 0x2a, 0x19,
-    0xa3, 0xbe, 0x5d, 0xfb, 0xff, 0xd7, 0xb4, 0xff, 0x3d, 0xad, 0x7f, 0xa4, 0x0e, 0xd9, 0xba, 0xeb, 0x7f, 0xd1, 0xf1, 0xd3,
-    0xdc, 0xfd, 0xbf, 0xeb, 0xeb, 0x08, 0x66, 0x5f, 0xf9, 0x39, 0xf3, 0xb5, 0xae, 0xfa, 0x44, 0x3b, 0xf6, 0xff, 0xe3, 0x63,
-    0xab, 0x91, 0x51, 0x7d, 0xde, 0xfd, 0xbf, 0x98, 0x56, 0xfe, 0xd7, 0xbc, 0xeb, 0xba, 0x63, 0x4e, 0xfe, 0xf7, 0xdc, 0x07,
-    0x7b, 0xe5, 0xbf, 0xf7, 0x29, 0xe6, 0x35, 0xfd, 0x4e, 0xfc, 0x6e, 0x7b, 0xb0, 0x9d, 0xff, 0x7b, 0x7d, 0xff, 0xe7, 0xfc,
-    0x27, 0x8f, 0x5e, 0x9d, 0xff, 0xdd, 0xce, 0x00, 0x59, 0x4f, 0xbc, 0x2e, 0x49, 0x7b, 0x32, 0x72, 0x17, 0x6f, 0xf7, 0xf3,
-    0xa5, 0xfc, 0xf3, 0xfc, 0xef, 0x6f, 0xd6, 0xc7, 0xf7, 0x9b, 0xae, 0xef, 0x5b, 0xcb, 0x3f, 0xbc, 0xaf, 0xf6, 0xf7, 0x7f,
-    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0x9e, 0x0f, 0x58, 0x27, 0xe7, 0x0a, 0xc6, 0x2a,
-    0x0d, 0x95, 0xe6, 0x8c, 0xd3, 0x1a, 0xfe, 0x9d, 0x91, 0x75, 0x89, 0xd7, 0xb0, 0x88, 0xd4, 0xe0, 0x2e, 0x8d, 0xba, 0x35,
-    0x39, 0xdb, 0xe6, 0x9c, 0xf5, 0x6c, 0xd7, 0xda, 0x1b, 0x3b, 0x2e, 0x46, 0xb6, 0x54, 0xbb, 0x86, 0xca, 0x79, 0xdb, 0x21,
-    0xba, 0xbf, 0x6b, 0x63, 0x76, 0x77, 0x6d, 0x54, 0xae, 0xc9, 0xa9, 0x07, 0x50, 0x1b, 0x75, 0x54, 0xe2, 0xf3, 0xd5, 0x46,
-    0xcf, 0x00, 0xf5, 0x8f, 0xda, 0x4e, 0x35, 0xf4, 0x3b, 0xa3, 0x7b, 0x3d, 0xb6, 0xac, 0xa7, 0xaa, 0x63, 0x5e, 0xa5, 0xe8,
-    0xc8, 0xb6, 0x59, 0xbf, 0x9e, 0xe3, 0xc7, 0x45, 0x4d, 0x7a, 0x87, 0xde, 0xba, 0x24, 0xfb, 0xef, 0xef, 0x76, 0x05, 0xd9,
-    0xba, 0xac, 0xfd, 0xcf, 0xc8, 0xff, 0x58, 0x0d, 0xd6, 0x91, 0x3a, 0x63, 0x99, 0xf9, 0xdf, 0xab, 0x3f, 0x16, 0x7f, 0x1e,
-    0x44, 0x56, 0xc5, 0xfd, 0x7b, 0xcc, 0xda, 0x8d, 0x3f, 0x99, 0xe0, 0x3e, 0xfb, 0x7b, 0xf5, 0xfa, 0xf5, 0x9c, 0x29, 0xe7,
-    0xea, 0x42, 0x5c, 0x95, 0xff, 0xbc, 0xfe, 0x7f, 0xb4, 0x3f, 0x1d, 0x79, 0x26, 0xd2, 0x6c, 0x2d, 0xc5, 0x99, 0x27, 0x8b,
-    0x9c, 0xbd, 0x2e, 0x73, 0xa3, 0x82, 0x9e, 0x31, 0xeb, 0x0e, 0xdb, 0x61, 0x66, 0x1b, 0x95, 0xe0, 0x93, 0x17, 0xb2, 0xf2,
-    0x9f, 0x51, 0xcf, 0x74, 0x7c, 0xfc, 0xd7, 0xde, 0x56, 0xb9, 0xe7, 0xc5, 0xd8, 0xeb, 0xed, 0xba, 0xac, 0x4e, 0x8c, 0x63,
-    0xae, 0x7d, 0x92, 0x51, 0x4e, 0xfe, 0xfb, 0x7b, 0xb5, 0xbb, 0xef, 0xd3, 0xd2, 0x31, 0x4e, 0xde, 0x3f, 0xff, 0xa3, 0x9f,
-    0x25, 0xb3, 0x8f, 0x3b, 0x5b, 0x5d, 0xfa, 0x9a, 0x65, 0xf3, 0x4f, 0x2a, 0xd9, 0xfd, 0xd8, 0xbe, 0x3a, 0xff, 0x6b, 0x9e,
-    0x06, 0x93, 0xbd, 0x8d, 0x5a, 0xa3, 0x86, 0xac, 0xf6, 0xbf, 0x35, 0x02, 0xa9, 0x5b, 0x8c, 0x0b, 0xfb, 0xb2, 0x3d, 0x7e,
-    0xfd, 0x7f, 0xee, 0x58, 0x59, 0xb1, 0x6c, 0xfe, 0xb8, 0xdd, 0x37, 0xe3, 0xbb, 0xe4, 0xff, 0x0e, 0xe7, 0xc8, 0x78, 0xab,
-    0x9d, 0xd1, 0xf7, 0x3d, 0xbf, 0xfd, 0xcf, 0xc8, 0x7f, 0xfe, 0x15, 0xd0, 0x3b, 0x2e, 0x5b, 0x9f, 0xd5, 0xd9, 0x6b, 0x70,
-    0xf2, 0x2f, 0xff, 0xf1, 0xbe, 0x4a, 0x4d, 0xaf, 0x70, 0xb9, 0xe2, 0xe9, 0x60, 0x57, 0x8d, 0xff, 0x73, 0xaf, 0x43, 0xdd,
-    0xb5, 0xff, 0x7f, 0xf7, 0x7d, 0x3a, 0xd3, 0x6b, 0xa7, 0xbc, 0x55, 0xdf, 0x60, 0xf6, 0x6c, 0xff, 0xb4, 0xfc, 0x3f, 0x7b,
-    0x9f, 0x4a, 0xbd, 0xfc, 0xe7, 0x9e, 0x01, 0xd6, 0x65, 0x7c, 0x24, 0xbb, 0xef, 0x97, 0xff, 0xbb, 0x3c, 0x35, 0x48, 0xfe,
-    0x9f, 0x31, 0xe6, 0x3b, 0xf7, 0x7b, 0x0e, 0x39, 0xdf, 0xff, 0x2b, 0x27, 0x3e, 0x01, 0xef, 0xec, 0xfb, 0xff, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x33, 0x7e, 0x33, 0x2b, 0x3d, 0x1f, 0xfd, 0xe5, 0xa7, 0xfa,
-    0xad, 0x7e, 0xf9, 0xee, 0x6b, 0xfd, 0xe7, 0xb0, 0xce, 0xc9, 0x19, 0x6b, 0xa1, 0x2e, 0xf7, 0xda, 0xba, 0xdc, 0xc7, 0x7b,
-    0xfb, 0x2e, 0x55, 0xb9, 0x7b, 0x8f, 0xd9, 0x3a, 0xb8, 0x85, 0x57, 0xa6, 0x2f, 0xab, 0x9e, 0x43, 0x74, 0xee, 0xe1, 0xab,
-    0xd7, 0xfc, 0x54, 0x3f, 0xff, 0xf8, 0xdf, 0xdf, 0x1f, 0x6e, 0xcb, 0xbe, 0x7a, 0x60, 0x79, 0x6b, 0xa1, 0x2e, 0xf7, 0xca,
-    0xba, 0xdc, 0x7d, 0x7b, 0x7b, 0xf7, 0xaa, 0xdc, 0xfd, 0x6b, 0x51, 0x37, 0x49, 0xdf, 0xb5, 0xb3, 0xe4, 0xe2, 0xdb, 0x72,
-    0xaf, 0x1a, 0xce, 0x35, 0x5c, 0x5b, 0x73, 0xae, 0x2e, 0xef, 0xf9, 0xeb, 0xb2, 0xaa, 0x87, 0xd9, 0x4a, 0xce, 0x6e, 0x47,
-    0x74, 0x6c, 0x2d, 0xae, 0xd9, 0x9b, 0xfd, 0xef, 0xb8, 0xd3, 0xec, 0xe7, 0x99, 0xfc, 0xef, 0x54, 0xa7, 0xb9, 0xbd, 0xbd,
-    0xe3, 0xcf, 0x51, 0x7a, 0x4e, 0x5d, 0xee, 0xd6, 0xde, 0x2e, 0x27, 0xcf, 0xbc, 0x8d, 0x6d, 0xa1, 0xfe, 0x3e, 0xeb, 0x1e,
-    0x75, 0x96, 0xee, 0x9e, 0xff, 0x9d, 0xb6, 0x63, 0x0d, 0xd6, 0xe5, 0xdf, 0xab, 0xf6, 0xf6, 0xae, 0xf9, 0xbf, 0x47, 0xa5,
-    0x95, 0x9e, 0xfc, 0xef, 0x76, 0xdc, 0x3e, 0xb1, 0xfd, 0x2f, 0x1d, 0xd5, 0xee, 0x33, 0x97, 0xe5, 0xd4, 0x8c, 0x7f, 0xe7,
-    0xba, 0xbc, 0xef, 0x93, 0xff, 0x22, 0xff, 0xa7, 0xf4, 0xff, 0xcf, 0x5b, 0x36, 0xdf, 0x37, 0x58, 0x71, 0xfd, 0xff, 0x5e,
-    0x75, 0x79, 0xdf, 0xa9, 0xfd, 0xdf, 0xa7, 0xce, 0xe2, 0x33, 0xf3, 0xbf, 0x63, 0x7b, 0xaa, 0x2e, 0xa7, 0xfc, 0xd7, 0xcd,
-    0xd2, 0xb7, 0xcb, 0x11, 0x15, 0x3f, 0x22, 0x76, 0xaa, 0x57, 0x9d, 0x7d, 0x6d, 0xb0, 0xff, 0x0e, 0xfb, 0x13, 0xea, 0x72,
-    0xde, 0x27, 0xff, 0x33, 0xd7, 0xff, 0xcf, 0x4f, 0x5f, 0x19, 0x7a, 0x0e, 0xf8, 0x15, 0x67, 0xa0, 0xd8, 0xfd, 0xff, 0xb3,
-    0xaf, 0x8b, 0x67, 0x3d, 0x97, 0x28, 0xf7, 0x3b, 0x59, 0xcf, 0xa9, 0xcb, 0x79, 0x9f, 0xbb, 0x3d, 0x33, 0xf7, 0xff, 0xfb,
-    0xee, 0x2c, 0x96, 0xe1, 0x1c, 0x67, 0x3c, 0xeb, 0xbf, 0x5c, 0x54, 0xc5, 0x38, 0xfa, 0xfd, 0x9f, 0x3b, 0x7d, 0xa3, 0x52,
-    0x7d, 0xd7, 0xd5, 0xc9, 0xb1, 0x16, 0xeb, 0x8f, 0xd6, 0x35, 0x55, 0x8c, 0x5f, 0xe7, 0xbf, 0xf5, 0x5d, 0x4a, 0x9e, 0x94,
-    0xff, 0x27, 0xec, 0x6d, 0xc7, 0x2c, 0xac, 0xf4, 0x97, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xd0, 0x3d, 0x43, 0xf3, 0xf8, 0xbb, 0xcd, 0x3b, 0xd7, 0x20, 0x05, 0x66, 0xe7, 0xe3, 0x3c, 0x63, 0x3e, 0x1e, 0x90, 0x3d,
-    0xb7, 0x71, 0xaf, 0x7a, 0xdc, 0x40, 0xee, 0xfc, 0x7f, 0xf9, 0x07, 0xf9, 0x97, 0x7f, 0x90, 0x7f, 0xf9, 0x07, 0xf9, 0xff,
-    0xf9, 0x97, 0x55, 0xfe, 0xe1, 0xe6, 0xf9, 0xaf, 0x4b, 0x6a, 0x9b, 0x03, 0x77, 0x39, 0x03, 0x44, 0xf2, 0x5f, 0xf4, 0xff,
-    0xe1, 0xb1, 0xdf, 0x01, 0x30, 0xfe, 0x07, 0xe3, 0x7f, 0xf9, 0x87, 0x77, 0xcc, 0x7f, 0xeb, 0xfb, 0xbf, 0x46, 0xff, 0xf0,
-    0x8c, 0xf1, 0xbf, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xdd, 0x5b, 0x76, 0xa4, 0x36, 0x10, 0x00, 0x50, 0xad, 0x2b, 0xfb, 0xdf, 0x97, 0x72, 0x92, 0x7c, 0xe4, 0xcc, 0x8c, 0x1b,
+    0x54, 0xa5, 0x12, 0x08, 0xfa, 0xfa, 0xfe, 0x19, 0xbb, 0x1b, 0x10, 0xa5, 0x07, 0xa0, 0x52, 0x6f, 0x00, 0x00, 0x00, 0xc0,
+    0x97, 0xfb, 0xe7, 0xe7, 0xcf, 0xdf, 0xb5, 0x1f, 0x7e, 0xfb, 0xff, 0xdf, 0xf6, 0x1f, 0xb7, 0x1e, 0x6d, 0xc9, 0xff, 0xe7,
+    0xf1, 0x9e, 0x8c, 0x7d, 0x42, 0x3b, 0xd8, 0x72, 0xbe, 0xcf, 0xbf, 0xff, 0xae, 0x97, 0x7c, 0xfb, 0xa7, 0xcf, 0x39, 0xdb,
+    0xa3, 0xcc, 0xb6, 0xa3, 0x4f, 0xed, 0x07, 0xc7, 0xd4, 0x06, 0xb6, 0xf4, 0xd4, 0xd1, 0xf5, 0xe9, 0x52, 0x8d, 0x95, 0xce,
+    0xf9, 0xf1, 0xb7, 0xf0, 0x31, 0xb6, 0x05, 0xdb, 0xfa, 0x64, 0x19, 0xaf, 0x8c, 0xff, 0xdf, 0x7f, 0x2a, 0xe3, 0xff, 0xf8,
+    0x33, 0x67, 0xae, 0x89, 0xff, 0x8f, 0xa6, 0x17, 0xc4, 0x7f, 0xbc, 0x16, 0x39, 0x3e, 0x82, 0xcf, 0x57, 0x71, 0x0b, 0xff,
+    0xcf, 0x79, 0xfd, 0x5a, 0xf9, 0xa9, 0x2b, 0xb7, 0x8d, 0x45, 0xed, 0xfc, 0x75, 0x7e, 0xb6, 0x37, 0x73, 0x65, 0x78, 0xe5,
+    0xb6, 0xda, 0xf8, 0x3f, 0xfb, 0xdb, 0x6c, 0xcd, 0x9e, 0xef, 0x1d, 0x8c, 0xb6, 0xe6, 0x3d, 0x74, 0xed, 0x9f, 0xb7, 0x62,
+    0x35, 0x67, 0x38, 0x7a, 0xc4, 0xc7, 0x75, 0xc9, 0xd9, 0xd6, 0xf9, 0x36, 0x37, 0x72, 0x6d, 0xdc, 0x71, 0x15, 0x9f, 0x97,
+    0x66, 0x45, 0xbb, 0xd4, 0x7f, 0x39, 0xdb, 0xe2, 0x7f, 0xf4, 0xaa, 0xed, 0x8b, 0x5b, 0xda, 0x16, 0x6e, 0x69, 0x73, 0x7d,
+    0x96, 0xba, 0x33, 0x5c, 0x15, 0xff, 0x7d, 0x22, 0xfa, 0x5b, 0xa2, 0xc7, 0x79, 0x5c, 0x6b, 0x9f, 0xed, 0x7f, 0xf6, 0x2a,
+    0xa9, 0xbd, 0x66, 0x67, 0xe3, 0xff, 0xb8, 0x3f, 0xdc, 0x0e, 0xeb, 0x9b, 0xeb, 0x7a, 0x5c, 0x3b, 0xc5, 0x7f, 0xb6, 0x87,
+    0x3c, 0x3e, 0xba, 0xa9, 0x6b, 0xff, 0xd7, 0xc7, 0xff, 0x59, 0x44, 0xf6, 0xe0, 0x48, 0xb5, 0xb6, 0x35, 0x1e, 0x19, 0x4f,
+    0x67, 0x6b, 0xf4, 0xf8, 0x18, 0xf7, 0xfa, 0xfe, 0xff, 0xf9, 0xb8, 0xb1, 0x2f, 0x1b, 0x39, 0xd5, 0x47, 0xd8, 0xbe, 0xfd,
+    0xff, 0xb1, 0x7e, 0xc1, 0x9a, 0xfe, 0xff, 0xd9, 0xf8, 0xbf, 0x5d, 0xde, 0xfa, 0x9f, 0x45, 0x56, 0xbe, 0xf4, 0x7b, 0xaa,
+    0x07, 0x34, 0x77, 0x57, 0x21, 0x57, 0x9e, 0x73, 0xf1, 0xbf, 0xa2, 0x0f, 0x10, 0x1d, 0xfd, 0xbd, 0xa3, 0xff, 0x9f, 0x39,
+    0x83, 0xd9, 0xfb, 0xcc, 0x55, 0x47, 0x16, 0xbf, 0xcf, 0x7f, 0xdf, 0x19, 0xab, 0xab, 0xe7, 0x67, 0xcf, 0x60, 0xfd, 0xa7,
+    0xe6, 0x7a, 0xd5, 0xd1, 0xab, 0xb8, 0xdf, 0xfa, 0x6c, 0xac, 0x3f, 0xe2, 0xce, 0xe9, 0x15, 0xed, 0xff, 0xd9, 0xfd, 0xff,
+    0x99, 0xbb, 0x03, 0xb1, 0x27, 0x1b, 0x7d, 0xc3, 0x6b, 0x45, 0xfc, 0x3f, 0xa5, 0x15, 0xab, 0xea, 0x55, 0xbb, 0xff, 0x97,
+    0x79, 0xa6, 0x5d, 0xd1, 0x9f, 0x7b, 0x4a, 0xfc, 0xf7, 0x81, 0xda, 0x74, 0x7c, 0x4b, 0xfe, 0x89, 0xfb, 0xaa, 0xb7, 0x0a,
+    0xce, 0x9e, 0x36, 0x5e, 0xf7, 0x14, 0x7b, 0xc7, 0xf8, 0x7f, 0xc6, 0xf3, 0xff, 0x3d, 0xce, 0xe5, 0xda, 0x91, 0xdc, 0x8e,
+    0xf1, 0x0f, 0xdf, 0xf1, 0x36, 0xe1, 0x75, 0x6f, 0x2d, 0x8a, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x9e, 0x3e, 0x3b, 0x27, 0x9f, 0x21, 0x32, 0x36, 0xe7, 0xa6, 0x2f, 0xc9, 0x8b, 0x94, 0x9b, 0x1b, 0x7d,
+    0x6d, 0x0e, 0xe7, 0x6c, 0xd6, 0xec, 0xb9, 0x79, 0xe1, 0x9f, 0xf3, 0x09, 0xcd, 0x7c, 0xee, 0xf1, 0x5c, 0xe4, 0xc8, 0xbc,
+    0xec, 0x6c, 0xae, 0xf3, 0x68, 0x4e, 0xa0, 0x4c, 0xee, 0xa4, 0xd9, 0xdc, 0xe9, 0xf1, 0x23, 0xce, 0xcc, 0x60, 0x9d, 0x9d,
+    0xf3, 0x76, 0x55, 0xc6, 0xe6, 0x6c, 0x9c, 0x8e, 0x65, 0xbd, 0xed, 0xe1, 0x4c, 0x75, 0xbb, 0xe5, 0x69, 0xa8, 0xce, 0x9a,
+    0xb1, 0x32, 0xfe, 0xdb, 0x50, 0x6e, 0xec, 0xb1, 0xf9, 0xce, 0xbd, 0x70, 0x46, 0xfb, 0x75, 0x39, 0xf7, 0xda, 0x74, 0x99,
+    0x44, 0x5b, 0xaa, 0xf7, 0xc7, 0x7f, 0xf6, 0xd8, 0xcf, 0xce, 0x6a, 0xdf, 0x36, 0x8b, 0xfd, 0x9d, 0xf1, 0x9f, 0xed, 0xdb,
+    0x1d, 0xb7, 0x7c, 0xf1, 0x8c, 0x07, 0xd7, 0x64, 0xb4, 0xe9, 0x1b, 0xc5, 0x7f, 0xfc, 0xdb, 0x56, 0xce, 0x61, 0x9f, 0x6b,
+    0x7b, 0x7b, 0x59, 0xfc, 0xe7, 0xfb, 0x3e, 0xbb, 0xb5, 0xf1, 0xb5, 0x59, 0x73, 0xd6, 0xc5, 0x7f, 0x2b, 0xcf, 0xca, 0xb4,
+    0x67, 0xfc, 0x8f, 0xb4, 0x0f, 0x99, 0xb1, 0x41, 0xfc, 0x3f, 0xb3, 0x35, 0x6e, 0xf4, 0xdb, 0x32, 0xed, 0xff, 0xf1, 0x51,
+    0x3e, 0xb9, 0xfd, 0xdf, 0x25, 0xfe, 0xdb, 0x56, 0xfd, 0xff, 0xd9, 0xab, 0x31, 0x16, 0xe7, 0xf7, 0xe5, 0x66, 0xdb, 0x7d,
+    0x25, 0xa3, 0xc8, 0x19, 0x9f, 0xcd, 0x8e, 0x9c, 0xbd, 0xef, 0x50, 0xb9, 0x62, 0xd3, 0xb3, 0xee, 0x71, 0xb5, 0xaf, 0x1c,
+    0xff, 0xe7, 0xef, 0xfb, 0x8a, 0xff, 0xd9, 0xfe, 0x74, 0xbb, 0x21, 0xfe, 0x57, 0x9d, 0xbb, 0x27, 0xc7, 0xff, 0x4c, 0xcf,
+    0xef, 0xaa, 0xf8, 0x6f, 0x1f, 0x57, 0xa1, 0x9a, 0x8d, 0xff, 0xfc, 0xda, 0x38, 0xd1, 0x38, 0xef, 0x37, 0x3e, 0xdf, 0x5a,
+    0x51, 0x07, 0xe6, 0x9f, 0x37, 0x34, 0xf1, 0xff, 0xa8, 0xf8, 0xbf, 0xbf, 0xfd, 0x1f, 0x5d, 0x5b, 0xf1, 0xbb, 0xe3, 0xbf,
+    0x4d, 0xdd, 0xe3, 0x5b, 0xf1, 0x7f, 0xbb, 0xf5, 0xff, 0xab, 0xeb, 0xce, 0x6b, 0xeb, 0xd3, 0x75, 0x7d, 0xdc, 0xdc, 0xfd,
+    0xa4, 0xdc, 0x6a, 0x06, 0xb9, 0xac, 0xd9, 0x6b, 0xae, 0xa9, 0x96, 0x5e, 0xc3, 0xab, 0x85, 0x6b, 0x00, 0xf1, 0xff, 0xeb,
+    0x96, 0x75, 0xab, 0x23, 0xc3, 0xea, 0x78, 0x7a, 0x56, 0xfc, 0xcf, 0xbd, 0x69, 0x50, 0xbf, 0xda, 0x60, 0xbf, 0xa1, 0xfd,
+    0x87, 0xb5, 0x4f, 0xd9, 0xeb, 0xfe, 0x9e, 0x8a, 0x3e, 0x3d, 0xf0, 0xbd, 0xfd, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xe0, 0x8d, 0x73, 0x05, 0x3e, 0xcf, 0xf2, 0x3c, 0xce, 0x82, 0xd1, 0x92, 0x59, 0xb0, 0xdb, 0x40,
+    0xe6, 0xe4, 0x9e, 0xfc, 0xbe, 0xc8, 0x2c, 0xe5, 0xdd, 0x72, 0x11, 0x44, 0xf3, 0x23, 0x46, 0x72, 0x63, 0xb7, 0x70, 0x9e,
+    0xaf, 0x6c, 0x16, 0x9b, 0x36, 0x99, 0x73, 0x34, 0x3f, 0xff, 0x2e, 0x3b, 0x83, 0x7a, 0x2c, 0xa3, 0xdb, 0xd8, 0xf7, 0xf4,
+    0xa1, 0x19, 0x7e, 0xb3, 0xa5, 0x50, 0x3f, 0x27, 0xb1, 0x5d, 0x9e, 0x75, 0xe3, 0xde, 0x6c, 0x4c, 0x4f, 0xca, 0xb7, 0x1d,
+    0xc9, 0xc3, 0x7b, 0x9e, 0xcf, 0x63, 0x3e, 0xfe, 0x47, 0x73, 0x0a, 0xf4, 0xc9, 0xac, 0x9f, 0xd1, 0xba, 0xe2, 0xf8, 0xbc,
+    0x1c, 0xcd, 0x4d, 0x8e, 0x45, 0x7f, 0x76, 0xfe, 0xcd, 0xf9, 0x3e, 0xac, 0x9b, 0x51, 0xfd, 0x1d, 0xf1, 0x1f, 0xaf, 0x41,
+    0x9f, 0x90, 0x6f, 0xbb, 0x15, 0xfc, 0xb6, 0x72, 0xe6, 0x7b, 0x36, 0xa7, 0x48, 0x36, 0xfe, 0x23, 0xb1, 0xf5, 0xb9, 0x96,
+    0x6b, 0x03, 0x19, 0xd4, 0xe6, 0xbf, 0x27, 0x13, 0xff, 0x3d, 0x9c, 0x6d, 0x25, 0xdb, 0xfe, 0xc7, 0x5a, 0x97, 0x3d, 0xe3,
+    0x3f, 0xd3, 0xe3, 0xec, 0x17, 0xc6, 0xff, 0x5c, 0x8e, 0xaf, 0x76, 0x79, 0xfc, 0xd7, 0xae, 0x53, 0x31, 0x36, 0x92, 0xa9,
+    0xe8, 0x69, 0xff, 0x34, 0x3a, 0x3c, 0x1a, 0x19, 0xd5, 0xc5, 0x7f, 0xff, 0xe1, 0x1b, 0x57, 0xb5, 0xff, 0x55, 0x19, 0x55,
+    0xfb, 0xc1, 0xfe, 0xbe, 0x21, 0xfe, 0xdb, 0x26, 0xfd, 0xff, 0xfa, 0xb5, 0xd4, 0xf6, 0x8c, 0xff, 0x78, 0x8f, 0x2b, 0xde,
+    0x7a, 0xc5, 0xfa, 0x0a, 0xe7, 0x23, 0x80, 0x5e, 0x50, 0x03, 0xf4, 0xdf, 0xd6, 0xa3, 0xaa, 0x1a, 0x85, 0xc7, 0x56, 0x12,
+    0xcb, 0xb5, 0xff, 0xfd, 0xb5, 0xed, 0x7f, 0xed, 0xf8, 0x7f, 0x55, 0x2e, 0xc2, 0xda, 0xf5, 0xb6, 0xaa, 0x72, 0x6d, 0xe6,
+    0xfa, 0x5a, 0xd9, 0xf6, 0xff, 0xac, 0x77, 0x30, 0x13, 0xff, 0x6d, 0x32, 0x2e, 0x63, 0xf5, 0x4c, 0x7f, 0x54, 0x06, 0x8e,
+    0x91, 0x35, 0xf2, 0xea, 0xae, 0xf4, 0xd1, 0x08, 0x8a, 0x6c, 0x9d, 0xcd, 0xaa, 0x36, 0xfe, 0xa4, 0x61, 0xa4, 0xa6, 0xfe,
+    0xfc, 0x94, 0x62, 0xe4, 0xac, 0x54, 0x8c, 0x01, 0x77, 0xed, 0xff, 0xdf, 0xd3, 0xfe, 0x8f, 0xb4, 0xfe, 0x35, 0x79, 0xc8,
+    0x7a, 0x79, 0xfe, 0x9d, 0x5e, 0xb8, 0x77, 0x75, 0x2d, 0xe4, 0x1d, 0x4f, 0x22, 0x57, 0xec, 0xd1, 0x7e, 0x59, 0xd2, 0x7a,
+    0xf2, 0xce, 0xf1, 0x5e, 0xfd, 0xff, 0xfc, 0xd8, 0x2a, 0x32, 0xaa, 0xaf, 0x7b, 0xfe, 0x57, 0xdd, 0x96, 0x3e, 0x2b, 0xfb,
+    0x96, 0xf8, 0xdf, 0xbb, 0x0c, 0xf6, 0x8a, 0xff, 0xd1, 0x55, 0xcc, 0x7b, 0xf9, 0x93, 0xf8, 0x67, 0xd4, 0xe0, 0xcf, 0x7d,
+    0xff, 0xe7, 0xfa, 0x95, 0x47, 0xef, 0x8e, 0xff, 0xdd, 0x6a, 0x80, 0xaa, 0x15, 0xaf, 0xdb, 0xe2, 0x37, 0x43, 0xee, 0x3f,
+    0x6b, 0x2b, 0x56, 0x08, 0x86, 0xb7, 0xbc, 0xbf, 0xd9, 0x5f, 0xdf, 0x6f, 0x7a, 0x62, 0x4f, 0x1b, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xd5, 0xfc, 0xbf, 0x3e, 0x39, 0x37, 0x30, 0x97, 0x59, 0xa8, 0x9d, 0xce,
+    0x30, 0xed, 0xe9, 0xbf, 0x89, 0x1c, 0x4b, 0x3e, 0x67, 0x45, 0x26, 0xe7, 0x76, 0x3b, 0xc9, 0x53, 0x53, 0x73, 0x6e, 0xae,
+    0x39, 0xce, 0xf3, 0xdc, 0x7a, 0xb1, 0xeb, 0x22, 0x72, 0xa6, 0xce, 0x73, 0xa6, 0x5c, 0x77, 0x1e, 0xb2, 0xe5, 0xdd, 0x4f,
+    0x66, 0x73, 0xf7, 0xc3, 0x4c, 0x35, 0x15, 0xb9, 0x54, 0xce, 0x3e, 0x3f, 0x9f, 0x79, 0xaf, 0x25, 0x32, 0xae, 0xf5, 0x3f,
+    0x72, 0x39, 0xf5, 0xd4, 0xdf, 0x44, 0x4b, 0x3d, 0xb7, 0x6d, 0x24, 0xa7, 0x63, 0x5d, 0x9e, 0xe8, 0xcc, 0xb9, 0x59, 0x7f,
+    0x9c, 0xf1, 0xeb, 0xa2, 0x17, 0x7d, 0xc3, 0x68, 0x1e, 0x92, 0xfd, 0xcb, 0xfb, 0x3c, 0x63, 0xec, 0x5c, 0x36, 0x85, 0xd5,
+    0xf1, 0x1f, 0xcb, 0xb9, 0x1a, 0xc9, 0x2b, 0x56, 0x19, 0xff, 0x7b, 0xf5, 0xc7, 0xf2, 0xab, 0x41, 0x54, 0x65, 0xd8, 0x7f,
+    0xc6, 0x2c, 0xdd, 0xfc, 0x4a, 0x04, 0xcf, 0x29, 0xef, 0xd5, 0xc7, 0x37, 0x52, 0x53, 0xce, 0xe5, 0x81, 0xb8, 0x2b, 0xfe,
+    0xeb, 0xfa, 0xff, 0xd9, 0xfe, 0x74, 0x66, 0x0d, 0xa4, 0xd9, 0xdc, 0x89, 0x33, 0x2b, 0x89, 0x5c, 0x7d, 0x2c, 0x73, 0xa3,
+    0x82, 0x91, 0x31, 0xeb, 0x0e, 0xe7, 0x61, 0xe6, 0x1c, 0xb5, 0xc9, 0x95, 0x16, 0xce, 0x23, 0xa0, 0x0f, 0x8c, 0x4f, 0xfa,
+    0x64, 0x0d, 0x13, 0x2d, 0xd1, 0xf3, 0x73, 0x55, 0x5b, 0x2f, 0xe6, 0x3e, 0x6f, 0xd7, 0x6d, 0x7d, 0x62, 0x1c, 0x73, 0xef,
+    0xca, 0x45, 0x35, 0xf1, 0x3f, 0xde, 0xab, 0xdd, 0xbd, 0x4c, 0xdb, 0xc0, 0x38, 0x79, 0xff, 0xf8, 0x8f, 0xc6, 0x61, 0x65,
+    0x1f, 0x77, 0x36, 0x9b, 0xf4, 0x3d, 0xdb, 0xe6, 0x57, 0x26, 0xd9, 0xfd, 0xda, 0xbe, 0x3b, 0xfe, 0xd7, 0xac, 0xfe, 0x52,
+    0x7d, 0x8e, 0xce, 0x46, 0x0d, 0x55, 0xed, 0xff, 0xd9, 0x08, 0xa4, 0x6f, 0x31, 0x2e, 0x1c, 0x8b, 0xed, 0xf8, 0xfd, 0xff,
+    0xb9, 0x6b, 0x65, 0xc5, 0xb6, 0xf9, 0xeb, 0x76, 0xdf, 0x18, 0xdf, 0x25, 0xfe, 0x9f, 0x50, 0x47, 0xce, 0xb4, 0xda, 0xd1,
+    0xf6, 0xbf, 0x5d, 0x18, 0xff, 0x35, 0x77, 0x2a, 0x7a, 0xe1, 0x8a, 0x76, 0x4f, 0xbf, 0x56, 0xe6, 0xef, 0xff, 0xad, 0x78,
+    0x02, 0xb2, 0x73, 0xfb, 0x2f, 0xfe, 0x9f, 0x16, 0xff, 0xd9, 0x67, 0xfb, 0x33, 0xfd, 0x91, 0x67, 0x8e, 0xff, 0x6b, 0xef,
+    0x43, 0x3d, 0xb5, 0xff, 0xff, 0xf4, 0x32, 0x9d, 0xeb, 0xb5, 0xcb, 0x90, 0xfc, 0x4d, 0x7d, 0x83, 0xd9, 0x3b, 0x9d, 0x6f,
+    0x8b, 0xff, 0x77, 0x97, 0x69, 0xf5, 0x0a, 0xe9, 0x7c, 0x4b, 0xfc, 0xb7, 0x0b, 0xdb, 0xc4, 0xf8, 0xfb, 0x86, 0xdf, 0x17,
+    0xff, 0xab, 0xde, 0x91, 0x10, 0xff, 0xcf, 0x8c, 0xff, 0xd5, 0x65, 0x7a, 0xed, 0x7b, 0x0e, 0x35, 0xef, 0xff, 0xad, 0x5a,
+    0xf5, 0x65, 0x87, 0xe7, 0xff, 0xcf, 0x7d, 0xf7, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb8,
+    0xfb, 0x3d, 0xf7, 0xd8, 0x96, 0xe3, 0xb9, 0x48, 0xb1, 0xec, 0x7b, 0xbb, 0x67, 0x63, 0x6e, 0xb2, 0x6f, 0x87, 0x4a, 0x7f,
+    0xf4, 0x7c, 0xef, 0x54, 0xbe, 0x9f, 0x4b, 0xac, 0xdf, 0x5e, 0x66, 0xb1, 0x98, 0x8a, 0x1f, 0x67, 0xc5, 0xe7, 0xe4, 0xa3,
+    0xff, 0xca, 0xac, 0xd4, 0xb2, 0x6f, 0xaf, 0xce, 0xbe, 0x1d, 0x39, 0xdf, 0xfb, 0x94, 0x6f, 0x7c, 0xbe, 0xd5, 0xb5, 0x65,
+    0xb6, 0xd7, 0x6c, 0xbe, 0xaa, 0x19, 0x86, 0x7b, 0xcd, 0x54, 0xec, 0x13, 0x19, 0xd5, 0xbb, 0xec, 0xdb, 0x8f, 0xe8, 0xe7,
+    0x56, 0xac, 0x8c, 0xf1, 0x9e, 0x98, 0xba, 0x7b, 0x86, 0xeb, 0x73, 0xb2, 0x31, 0x8f, 0xf5, 0x61, 0x72, 0xab, 0x25, 0xbd,
+    0x27, 0xfb, 0xf6, 0x4f, 0xad, 0xdd, 0xb5, 0x33, 0x6a, 0xab, 0xca, 0xf7, 0x38, 0xc7, 0xee, 0xce, 0xd9, 0x1f, 0x9e, 0x17,
+    0xff, 0xbb, 0x64, 0xa5, 0x6a, 0x27, 0x25, 0x9e, 0xbf, 0xae, 0xbe, 0x29, 0xfb, 0xee, 0x3b, 0x32, 0xa8, 0xf4, 0xad, 0xae,
+    0xcd, 0x77, 0xc7, 0x7f, 0x26, 0x1e, 0x57, 0x6c, 0xab, 0xc9, 0x0c, 0xff, 0xed, 0xd9, 0x77, 0xdf, 0x13, 0xff, 0x4d, 0xfc,
+    0xdf, 0xde, 0xff, 0xbf, 0x6e, 0xdb, 0x7c, 0xdf, 0x60, 0xc5, 0xfd, 0xff, 0xe7, 0x65, 0xdf, 0x7d, 0x53, 0xfb, 0xbf, 0x4b,
+    0xc6, 0xc4, 0x68, 0x4c, 0xf5, 0x17, 0xc4, 0xff, 0x8e, 0xed, 0xa9, 0xec, 0x9b, 0xdf, 0x11, 0xff, 0x7d, 0xc3, 0xe3, 0xea,
+    0x45, 0x75, 0xd7, 0x35, 0xd7, 0x57, 0xf4, 0x2e, 0xcb, 0x2e, 0x71, 0x51, 0x7d, 0x6f, 0x70, 0xfc, 0x09, 0xfb, 0x1b, 0xb3,
+    0x6f, 0xbe, 0x35, 0xdb, 0xe6, 0xca, 0xfd, 0x6b, 0xe1, 0xb5, 0xf8, 0xf3, 0x4f, 0x68, 0x56, 0xd5, 0xa5, 0x15, 0x2b, 0x6f,
+    0xef, 0x94, 0x8d, 0x51, 0xf6, 0xcd, 0x15, 0xcf, 0xa2, 0x9e, 0x9c, 0x6d, 0xf3, 0x3c, 0x56, 0xe3, 0xdb, 0xd6, 0xc7, 0xf4,
+    0x55, 0x75, 0xe9, 0xfb, 0x72, 0x8f, 0xca, 0xa8, 0xca, 0x33, 0xde, 0xb7, 0xc9, 0xdd, 0xe7, 0xa8, 0xbd, 0xe6, 0x45, 0x0a,
+    0xdc, 0xf5, 0x6e, 0x90, 0xb6, 0x8a, 0xdd, 0xfc, 0xe5, 0x1c, 0xe8, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0xf2, 0xff, 0x3f, 0xdb, 0x01, 0xe4, 0x1f, 0x78, 0xbb, 0xfc, 0xff, 0x0b };
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfa, 0xef, 0xc7,
+    0x79, 0x00, 0xf1, 0x0f, 0x7c, 0x5d, 0xfc, 0xff, 0x0d };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle candyFontRecs[189] = {
@@ -262,95 +255,95 @@
     { 372, 50, 8 , 8 },
     { 388, 50, 5 , 7 },
     { 401, 50, 7 , 6 },
-    { 416, 50, 7 , 12 },
-    { 431, 50, 8 , 8 },
-    { 447, 50, 6 , 1 },
-    { 461, 50, 4 , 5 },
-    { 473, 50, 6 , 7 },
-    { 487, 50, 3 , 5 },
-    { 498, 50, 3 , 5 },
-    { 4, 73, 0 , 0 },
-    { 12, 73, 6 , 9 },
-    { 26, 73, 6 , 9 },
-    { 40, 73, 4 , 4 },
-    { 52, 73, 0 , 0 },
-    { 60, 73, 2 , 5 },
-    { 70, 73, 4 , 7 },
-    { 82, 73, 7 , 6 },
-    { 97, 73, 10 , 9 },
-    { 115, 73, 10 , 7 },
-    { 133, 73, 0 , 0 },
-    { 141, 73, 6 , 9 },
-    { 155, 73, 6 , 12 },
-    { 169, 73, 6 , 12 },
-    { 183, 73, 6 , 12 },
-    { 197, 73, 6 , 12 },
-    { 211, 73, 6 , 11 },
-    { 225, 73, 6 , 12 },
-    { 239, 73, 10 , 9 },
-    { 257, 73, 6 , 12 },
-    { 271, 73, 6 , 12 },
-    { 285, 73, 6 , 12 },
-    { 299, 73, 6 , 12 },
-    { 313, 73, 6 , 11 },
-    { 327, 73, 3 , 12 },
-    { 338, 73, 3 , 12 },
-    { 349, 73, 4 , 12 },
-    { 361, 73, 4 , 11 },
-    { 373, 73, 8 , 9 },
-    { 389, 73, 6 , 12 },
-    { 403, 73, 6 , 12 },
-    { 417, 73, 6 , 12 },
-    { 431, 73, 6 , 12 },
-    { 445, 73, 6 , 12 },
-    { 459, 73, 6 , 11 },
-    { 473, 73, 5 , 5 },
-    { 486, 73, 9 , 10 },
+    { 416, 50, 0 , 0 },
+    { 424, 50, 8 , 8 },
+    { 440, 50, 6 , 1 },
+    { 454, 50, 4 , 5 },
+    { 466, 50, 6 , 7 },
+    { 480, 50, 3 , 5 },
+    { 491, 50, 3 , 5 },
+    { 502, 50, 0 , 0 },
+    { 4, 73, 6 , 9 },
+    { 18, 73, 6 , 9 },
+    { 32, 73, 4 , 4 },
+    { 44, 73, 0 , 0 },
+    { 52, 73, 2 , 5 },
+    { 62, 73, 4 , 7 },
+    { 74, 73, 7 , 6 },
+    { 89, 73, 10 , 9 },
+    { 107, 73, 10 , 7 },
+    { 125, 73, 0 , 0 },
+    { 133, 73, 6 , 9 },
+    { 147, 73, 6 , 12 },
+    { 161, 73, 6 , 12 },
+    { 175, 73, 6 , 12 },
+    { 189, 73, 6 , 12 },
+    { 203, 73, 6 , 11 },
+    { 217, 73, 6 , 12 },
+    { 231, 73, 10 , 9 },
+    { 249, 73, 6 , 12 },
+    { 263, 73, 6 , 12 },
+    { 277, 73, 6 , 12 },
+    { 291, 73, 6 , 12 },
+    { 305, 73, 6 , 11 },
+    { 319, 73, 3 , 12 },
+    { 330, 73, 3 , 12 },
+    { 341, 73, 4 , 12 },
+    { 353, 73, 4 , 11 },
+    { 365, 73, 8 , 9 },
+    { 381, 73, 6 , 12 },
+    { 395, 73, 6 , 12 },
+    { 409, 73, 6 , 12 },
+    { 423, 73, 6 , 12 },
+    { 437, 73, 6 , 12 },
+    { 451, 73, 6 , 11 },
+    { 465, 73, 5 , 5 },
+    { 478, 73, 9 , 10 },
+    { 495, 73, 6 , 12 },
     { 4, 96, 6 , 12 },
     { 18, 96, 6 , 12 },
-    { 32, 96, 6 , 12 },
-    { 46, 96, 6 , 11 },
-    { 60, 96, 6 , 12 },
-    { 74, 96, 7 , 12 },
-    { 89, 96, 6 , 10 },
-    { 103, 96, 6 , 10 },
-    { 117, 96, 6 , 10 },
-    { 131, 96, 6 , 10 },
-    { 145, 96, 6 , 10 },
-    { 159, 96, 6 , 9 },
-    { 173, 96, 6 , 11 },
-    { 187, 96, 10 , 7 },
-    { 205, 96, 6 , 10 },
-    { 219, 96, 6 , 10 },
-    { 233, 96, 6 , 10 },
-    { 247, 96, 6 , 10 },
-    { 261, 96, 6 , 9 },
-    { 275, 96, 3 , 10 },
-    { 286, 96, 3 , 10 },
-    { 297, 96, 4 , 10 },
-    { 309, 96, 4 , 9 },
-    { 321, 96, 7 , 12 },
-    { 336, 96, 6 , 13 },
+    { 32, 96, 6 , 11 },
+    { 46, 96, 6 , 12 },
+    { 60, 96, 0 , 0 },
+    { 68, 96, 6 , 10 },
+    { 82, 96, 6 , 10 },
+    { 96, 96, 6 , 10 },
+    { 110, 96, 6 , 10 },
+    { 124, 96, 6 , 10 },
+    { 138, 96, 6 , 9 },
+    { 152, 96, 6 , 11 },
+    { 166, 96, 10 , 7 },
+    { 184, 96, 6 , 10 },
+    { 198, 96, 6 , 10 },
+    { 212, 96, 6 , 10 },
+    { 226, 96, 6 , 10 },
+    { 240, 96, 6 , 9 },
+    { 254, 96, 3 , 10 },
+    { 265, 96, 3 , 10 },
+    { 276, 96, 4 , 10 },
+    { 288, 96, 4 , 9 },
+    { 300, 96, 0 , 0 },
+    { 308, 96, 6 , 13 },
+    { 322, 96, 6 , 10 },
+    { 336, 96, 6 , 10 },
     { 350, 96, 6 , 10 },
     { 364, 96, 6 , 10 },
-    { 378, 96, 6 , 10 },
-    { 392, 96, 6 , 10 },
-    { 406, 96, 6 , 9 },
-    { 420, 96, 7 , 12 },
-    { 435, 96, 8 , 8 },
-    { 451, 96, 6 , 10 },
-    { 465, 96, 6 , 10 },
-    { 479, 96, 6 , 10 },
-    { 493, 96, 6 , 9 },
-    { 4, 119, 6 , 13 },
-    { 18, 119, 7 , 12 },
-    { 33, 119, 6 , 12 },
+    { 378, 96, 6 , 9 },
+    { 392, 96, 0 , 0 },
+    { 400, 96, 8 , 8 },
+    { 416, 96, 6 , 10 },
+    { 430, 96, 6 , 10 },
+    { 444, 96, 6 , 10 },
+    { 458, 96, 6 , 9 },
+    { 472, 96, 6 , 13 },
+    { 486, 96, 0 , 0 },
+    { 494, 96, 6 , 12 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo candyFontGlyphs[189] = {
-    { 32, 0, 12, 3, { 0 }},
+    { 32, 0, 0, 3, { 0 }},
     { 33, 0, 3, 3, { 0 }},
     { 34, 0, 2, 4, { 0 }},
     { 35, 0, 3, 9, { 0 }},
@@ -450,30 +443,30 @@
     { 163, 0, 3, 9, { 0 }},
     { 8364, 0, 3, 8, { 0 }},
     { 165, 0, 3, 7, { 0 }},
-    { 352, 0, 12, 1, { 0 }},
+    { 352, 0, 0, 0, { 0 }},
     { 167, 0, 2, 7, { 0 }},
-    { 353, 0, 12, 1, { 0 }},
+    { 353, 0, 0, 0, { 0 }},
     { 169, 0, 0, 9, { 0 }},
     { 170, 0, 0, 6, { 0 }},
     { 171, 1, 5, 9, { 0 }},
-    { 172, 0, 0, 7, { 0 }},
+    { 172, 0, 0, 0, { 0 }},
     { 174, 0, 0, 9, { 0 }},
     { 175, 0, 0, 7, { 0 }},
     { 176, 0, 0, 5, { 0 }},
     { 177, 0, 4, 7, { 0 }},
     { 178, 0, 0, 4, { 0 }},
     { 179, 0, 0, 4, { 0 }},
-    { 381, 0, 12, 1, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
     { 181, 0, 5, 7, { 0 }},
     { 182, 0, 3, 7, { 0 }},
     { 183, 0, 6, 5, { 0 }},
-    { 382, 0, 12, 1, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
     { 185, 0, 0, 3, { 0 }},
     { 186, 0, 0, 5, { 0 }},
     { 187, 1, 5, 9, { 0 }},
     { 338, 0, 3, 11, { 0 }},
     { 339, 0, 5, 11, { 0 }},
-    { 376, 0, 12, 1, { 0 }},
+    { 376, 0, 0, 0, { 0 }},
     { 191, 0, 4, 7, { 0 }},
     { 192, 0, 0, 7, { 0 }},
     { 193, 0, 0, 7, { 0 }},
@@ -505,7 +498,7 @@
     { 219, 0, 0, 7, { 0 }},
     { 220, 0, 1, 7, { 0 }},
     { 221, 0, 0, 7, { 0 }},
-    { 222, 0, 0, 7, { 0 }},
+    { 222, 0, 0, 0, { 0 }},
     { 223, 0, 3, 7, { 0 }},
     { 224, 0, 2, 7, { 0 }},
     { 225, 0, 2, 7, { 0 }},
@@ -523,21 +516,21 @@
     { 237, 0, 2, 4, { 0 }},
     { 238, 0, 2, 4, { 0 }},
     { 239, 0, 3, 4, { 0 }},
-    { 240, 0, 0, 7, { 0 }},
+    { 240, 0, 0, 0, { 0 }},
     { 241, 0, 2, 7, { 0 }},
     { 242, 0, 2, 7, { 0 }},
     { 243, 0, 2, 7, { 0 }},
     { 244, 0, 2, 7, { 0 }},
     { 245, 0, 2, 7, { 0 }},
     { 246, 0, 3, 7, { 0 }},
-    { 247, 0, 0, 7, { 0 }},
+    { 247, 0, 0, 0, { 0 }},
     { 248, 0, 5, 9, { 0 }},
     { 249, 0, 2, 7, { 0 }},
     { 250, 0, 2, 7, { 0 }},
     { 251, 0, 2, 7, { 0 }},
     { 252, 0, 3, 7, { 0 }},
     { 253, 0, 2, 7, { 0 }},
-    { 254, 0, 0, 7, { 0 }},
+    { 254, 0, 0, 0, { 0 }},
     { 255, 0, 3, 7, { 0 }},
 };
 
diff --git a/raygui/styles/cherry/style_cherry.h b/raygui/styles/cherry/style_cherry.h
--- a/raygui/styles/cherry/style_cherry.h
+++ b/raygui/styles/cherry/style_cherry.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,23 +15,23 @@
 
 // Custom style name: Cherry
 static const GuiStyleProp cherryStyleProps[CHERRY_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0xda5757ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x753233ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xe17373ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xfaaa97ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xe06262ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xfdb4aaff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xe03c46ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0x5b1e20ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0xc2474fff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0xa19292ff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x706060ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x9e8585ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x0000000f },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0xfb8170ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x3a1720ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000016 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0xda5757ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x753233ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xe17373ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xfaaa97ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xe06262ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xfdb4aaff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xe03c46ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0x5b1e20ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xc2474fff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0xa19292ff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x706060ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x9e8585ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x0000000f },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0xfb8170ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x3a1720ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000016 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
 // WARNING: This style uses a custom font: "Westington.ttf" (size: 15, spacing: 0)
@@ -378,7 +378,7 @@
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo cherryFontGlyphs[189] = {
-    { 32, 0, 12, 5, { 0 }},
+    { 32, 0, 0, 5, { 0 }},
     { 33, 0, 2, 4, { 0 }},
     { 34, 0, 2, 6, { 0 }},
     { 35, 0, 2, 11, { 0 }},
diff --git a/raygui/styles/cyber/style_cyber.h b/raygui/styles/cyber/style_cyber.h
--- a/raygui/styles/cyber/style_cyber.h
+++ b/raygui/styles/cyber/style_cyber.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,146 +15,147 @@
 
 // Custom style name: Cyber
 static const GuiStyleProp cyberStyleProps[CYBER_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x2f7486ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x024658ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0x51bfd3ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0x82cde0ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x3299b4ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xb6e1eaff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xeb7630ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xffbc51ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0xd86f36ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x134b5aff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x02313dff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x17505fff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x0000000e },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0x81c0d0ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x00222bff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000015 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0x2f7486ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x024658ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x51bfd3ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0x82cde0ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x3299b4ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xb6e1eaff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xeb7630ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xffbc51ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xd86f36ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x134b5aff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x02313dff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x17505fff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x0000000e },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x81c0d0ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x00222bff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000015 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
-// WARNING: This style uses a custom font: "Kyrou 7 Wide.ttf" (size: 14, spacing: 0)
+// WARNING: This style uses a custom font: "Kyrou7Wide.ttf" (size: 14, spacing: 0)
 
-#define CYBER_STYLE_FONT_ATLAS_COMP_SIZE 2286
+#define CYBER_STYLE_FONT_ATLAS_COMP_SIZE 2306
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char cyberFontData[CYBER_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0x81, 0x6e, 0xdb, 0x36, 0x10, 0x06, 0x60, 0x21, 0xef, 0xff, 0xc6, 0x99, 0xf6, 0x0f, 0xe8, 0xd0, 0x2d, 0x45, 0x6b,
-    0x89, 0x3c, 0x92, 0xb2, 0xec, 0x7c, 0xfd, 0x80, 0x61, 0x88, 0x62, 0x5b, 0xa2, 0x78, 0xe4, 0x49, 0x0a, 0xcf, 0xd9, 0x00,
-    0x00, 0x00, 0x00, 0x26, 0xcb, 0xc5, 0x9f, 0x96, 0xe6, 0x9f, 0xce, 0x3b, 0xa2, 0x9f, 0xff, 0x9e, 0x7b, 0xec, 0xfa, 0x57,
-    0x7f, 0xbb, 0xe7, 0xe1, 0xb9, 0xd3, 0x9e, 0x5f, 0x7f, 0x92, 0xc3, 0xdf, 0x4d, 0xa9, 0x75, 0x73, 0x7a, 0xc6, 0x8e, 0xcf,
-    0x4c, 0x5f, 0xbc, 0x3d, 0xde, 0x87, 0x74, 0xbf, 0x62, 0xe5, 0xc8, 0xf8, 0xf3, 0xdf, 0xde, 0xd5, 0xdf, 0x8f, 0x7b, 0x71,
-    0x5b, 0x2f, 0xcf, 0xc9, 0x1e, 0xa5, 0xe1, 0x37, 0xce, 0x5e, 0xbf, 0x0d, 0xec, 0xfd, 0x7e, 0xe9, 0x5c, 0x94, 0xd3, 0x31,
-    0x63, 0x5b, 0x70, 0x34, 0x19, 0x3c, 0x97, 0x19, 0x1e, 0xd1, 0xda, 0xe3, 0xff, 0xac, 0x5f, 0xe4, 0xb7, 0xff, 0x6e, 0xa5,
-    0x48, 0xcb, 0xc2, 0xf8, 0xdf, 0xba, 0xc6, 0x97, 0xf5, 0x79, 0x51, 0x1a, 0x22, 0xbd, 0xff, 0x35, 0xad, 0x47, 0x5b, 0x1b,
-    0xf1, 0xd2, 0xfc, 0xbb, 0x29, 0x1d, 0xf1, 0xac, 0x33, 0xd2, 0x3b, 0x72, 0x9e, 0xb5, 0xc6, 0x8a, 0xa3, 0xf9, 0xfa, 0xb3,
-    0xcf, 0xc1, 0xd7, 0xef, 0xcb, 0xc7, 0xc8, 0xbd, 0xa1, 0xa5, 0xf6, 0xf2, 0x27, 0xa6, 0x90, 0xcd, 0xcd, 0x3a, 0xfb, 0xab,
-    0xf3, 0xc2, 0x94, 0x63, 0x29, 0x43, 0x67, 0xef, 0xf8, 0x73, 0xab, 0xf3, 0xf7, 0xda, 0x11, 0xaf, 0x3f, 0xd7, 0xcf, 0xa4,
-    0xd1, 0xbc, 0x36, 0x9a, 0xfd, 0x6c, 0xa9, 0x2c, 0x88, 0xdf, 0x79, 0x63, 0xf1, 0xcc, 0x1c, 0x69, 0x24, 0xef, 0x3c, 0xda,
-    0xf6, 0xf9, 0x43, 0xfb, 0x5e, 0xbd, 0xca, 0xfc, 0x9f, 0x81, 0xf3, 0x97, 0x52, 0x3b, 0x9f, 0xe5, 0x94, 0xad, 0x47, 0x7c,
-    0xfc, 0xe9, 0x7b, 0xe1, 0x98, 0x3f, 0xb2, 0xff, 0x67, 0x9b, 0x92, 0xd3, 0x66, 0xe2, 0xb9, 0x4c, 0xf7, 0xb5, 0xd2, 0xbf,
-    0x71, 0x5b, 0xcf, 0x99, 0x93, 0xbf, 0x7f, 0xd8, 0x17, 0xe7, 0x62, 0x57, 0xc4, 0xff, 0xff, 0xa3, 0x58, 0x06, 0x22, 0x36,
-    0x83, 0x33, 0xcc, 0xdc, 0xeb, 0xff, 0x71, 0xfb, 0xd0, 0xf9, 0xcb, 0xf4, 0xab, 0xda, 0xff, 0xa3, 0x37, 0xa5, 0xfc, 0x21,
-    0xd3, 0x46, 0xd9, 0x7d, 0xe1, 0xbd, 0xe4, 0x2c, 0xca, 0xd2, 0x1e, 0xcd, 0xff, 0xd7, 0x65, 0x3b, 0x6b, 0xae, 0x96, 0x7a,
-    0xee, 0x9d, 0x56, 0xaf, 0xff, 0xd3, 0x38, 0xba, 0xac, 0x8b, 0xff, 0xeb, 0xfd, 0x35, 0x78, 0xfe, 0x72, 0xf1, 0xf5, 0xf9,
-    0x9c, 0x2b, 0xf4, 0x7a, 0xc6, 0xb3, 0xf6, 0xfa, 0xff, 0xaa, 0x11, 0x63, 0x45, 0xfe, 0x9f, 0x17, 0xcf, 0xff, 0x7b, 0xee,
-    0xff, 0xe7, 0x4d, 0xe2, 0x3f, 0xc3, 0x4f, 0xa4, 0x32, 0xfd, 0xfe, 0x7c, 0x5b, 0xb6, 0x3b, 0xf6, 0x7c, 0x21, 0x43, 0x47,
-    0x3c, 0xeb, 0x5e, 0x4c, 0x26, 0xfd, 0x6e, 0x1a, 0x67, 0xc5, 0xda, 0xfd, 0xdd, 0xe7, 0xde, 0xff, 0xf7, 0xfc, 0x1f, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x9e, 0xd5, 0x9d, 0xb9, 0xe0, 0x75, 0x95,
-    0x55, 0xb4, 0x23, 0xab, 0x6e, 0x47, 0xd6, 0xec, 0x56, 0x5f, 0x79, 0x5e, 0x87, 0xa8, 0xbe, 0xce, 0xb8, 0x7d, 0x95, 0x73,
-    0xdf, 0xba, 0xe1, 0xb1, 0xea, 0x28, 0x39, 0x5d, 0x47, 0xdf, 0x56, 0x8f, 0xe0, 0xbc, 0x76, 0xeb, 0x59, 0x25, 0xab, 0xf5,
-    0xb5, 0x5f, 0x57, 0xd6, 0xa6, 0x4c, 0xb9, 0x05, 0x7b, 0x56, 0xbf, 0xa7, 0xab, 0x3e, 0x41, 0x75, 0x1d, 0xfa, 0x75, 0x55,
-    0x01, 0xc7, 0x57, 0xc8, 0x1f, 0xbf, 0xf3, 0xbe, 0xfc, 0x28, 0xda, 0xf7, 0x38, 0x13, 0xce, 0xd9, 0x8a, 0xf8, 0xcf, 0x61,
-    0x45, 0x80, 0x5c, 0x58, 0xdb, 0x75, 0x5d, 0x25, 0xdc, 0x75, 0x95, 0x76, 0xce, 0xe3, 0xbf, 0xbd, 0x66, 0x73, 0x26, 0x47,
-    0x6b, 0xca, 0xd1, 0x33, 0x6b, 0x04, 0xa8, 0x8c, 0x5b, 0x33, 0xe2, 0xbf, 0x9a, 0x3b, 0xbc, 0x76, 0xfc, 0x57, 0xeb, 0x89,
-    0xe5, 0xe0, 0xbc, 0xa7, 0xb1, 0x6f, 0xdf, 0x61, 0xfb, 0x76, 0xd3, 0xf8, 0x3f, 0x8f, 0xa9, 0x0c, 0x57, 0x29, 0x9e, 0xd7,
-    0x9f, 0xe6, 0xd9, 0x9b, 0xa2, 0xb4, 0x56, 0xd3, 0x73, 0x2f, 0x8f, 0x2c, 0xab, 0xf2, 0xff, 0x9c, 0x5c, 0x79, 0xa4, 0x29,
-    0x97, 0x9d, 0x3b, 0xc6, 0xb6, 0x8e, 0x5a, 0x8f, 0x2b, 0x8a, 0x9d, 0xd5, 0x2b, 0x7c, 0xfe, 0xfc, 0x3e, 0x56, 0x4f, 0xf9,
-    0x9a, 0xf8, 0x1f, 0x8f, 0xc4, 0x3c, 0x65, 0xce, 0x9a, 0x73, 0x6f, 0x22, 0xc5, 0x79, 0x6b, 0xcd, 0x6b, 0xaf, 0x98, 0xff,
-    0xf3, 0x52, 0xf3, 0x7f, 0xcb, 0xf5, 0x7f, 0x26, 0x5c, 0xbd, 0xd6, 0x5a, 0x6d, 0x6f, 0x3e, 0x97, 0x2b, 0xf2, 0xff, 0x96,
-    0x99, 0x7d, 0x7c, 0xfe, 0x1f, 0xd9, 0x9a, 0xe6, 0x59, 0xf8, 0xfa, 0xf8, 0x6f, 0x3b, 0xa6, 0x5c, 0xfa, 0xda, 0xd7, 0x8b,
-    0xff, 0xdc, 0xe2, 0xfe, 0x5f, 0x16, 0xd6, 0x66, 0xce, 0x94, 0xd1, 0x6d, 0x4d, 0xfc, 0xb7, 0xd5, 0x43, 0x1f, 0xb9, 0xfe,
-    0x1f, 0x8b, 0xf0, 0xf3, 0x08, 0xf8, 0x4b, 0xfc, 0x4f, 0xca, 0xff, 0x67, 0xbd, 0xba, 0xff, 0x5e, 0xc9, 0xc8, 0x0c, 0xda,
-    0x7e, 0x4c, 0x99, 0x9e, 0x7f, 0x8f, 0xd6, 0xf6, 0x5d, 0x9d, 0xff, 0xcf, 0x7a, 0xfd, 0x56, 0x3e, 0xef, 0xe7, 0x5b, 0xc6,
-    0x7a, 0xe5, 0xf6, 0x84, 0x6f, 0x73, 0x58, 0xfd, 0x7c, 0xcf, 0x77, 0x94, 0xbe, 0xdf, 0x53, 0x6c, 0x6d, 0x70, 0xef, 0xe7,
-    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x5e, 0x65, 0x75, 0xbf, 0x15,
-    0x53, 0x69, 0x58, 0x1f, 0xda, 0x52, 0x3f, 0xf3, 0xb8, 0xfa, 0xe6, 0xa3, 0x1a, 0x12, 0x7b, 0x53, 0xed, 0xcd, 0x59, 0xad,
-    0xd9, 0x5a, 0x07, 0x61, 0xb4, 0x4e, 0x67, 0xe5, 0xdd, 0x5b, 0x5a, 0xf9, 0x7c, 0xcd, 0x63, 0x4e, 0xd6, 0x01, 0x6f, 0x13,
-    0x7b, 0xe0, 0xf9, 0x9a, 0xe7, 0x0c, 0xd4, 0x5c, 0x18, 0xa9, 0x22, 0x98, 0x85, 0x2b, 0x4c, 0x53, 0x6a, 0x95, 0xf6, 0x0a,
-    0x1c, 0xad, 0x5b, 0x33, 0xa9, 0xca, 0xc3, 0xea, 0x7e, 0x3d, 0x63, 0x6b, 0x7f, 0x1d, 0x85, 0x67, 0xc4, 0xff, 0x71, 0x0f,
-    0xc9, 0xe4, 0xb3, 0xdd, 0xf7, 0xd3, 0xb3, 0xd8, 0x7f, 0x5c, 0x5b, 0xf9, 0xa3, 0xb1, 0xdf, 0xf4, 0x6c, 0xcf, 0xf0, 0x59,
-    0xce, 0x60, 0x25, 0x84, 0xbd, 0x73, 0xbe, 0x69, 0x8f, 0xfe, 0xb1, 0x9e, 0xb7, 0x6e, 0x05, 0xfc, 0x3b, 0xc5, 0xff, 0x56,
-    0xac, 0x58, 0xb8, 0x3a, 0xfe, 0xb7, 0xa6, 0x7d, 0xca, 0xb4, 0x23, 0x9a, 0x57, 0x7d, 0xfb, 0xa3, 0x3b, 0xc7, 0x9a, 0x53,
-    0xcb, 0x2a, 0x17, 0xd6, 0x7a, 0x48, 0x47, 0xef, 0xaa, 0x8e, 0x00, 0x29, 0xce, 0xff, 0xfd, 0x2d, 0x3d, 0x2b, 0xc7, 0x78,
-    0xcd, 0xf8, 0xaf, 0xb5, 0x66, 0x06, 0xce, 0xf0, 0xda, 0xf9, 0x7f, 0x66, 0xe5, 0xee, 0x4a, 0x74, 0xec, 0x0f, 0xde, 0x65,
-    0x2f, 0xcf, 0xce, 0xf5, 0xab, 0x83, 0xad, 0xe9, 0x3b, 0x19, 0xf6, 0xee, 0xd7, 0xe5, 0x97, 0xf8, 0xcc, 0x92, 0x6b, 0xf4,
-    0x1c, 0xb4, 0x70, 0xb5, 0x1a, 0x69, 0x6f, 0xfc, 0xe7, 0xd2, 0xf9, 0x7f, 0x6b, 0xfc, 0xf6, 0x8c, 0xad, 0x74, 0xbe, 0xb6,
-    0xe2, 0xbb, 0xd6, 0x5f, 0x99, 0xd3, 0x1e, 0xbb, 0xa6, 0x1e, 0xed, 0xcc, 0x6f, 0xe8, 0xc8, 0x84, 0x7e, 0x9b, 0x93, 0x4f,
-    0x48, 0x31, 0x07, 0xef, 0xcd, 0xfc, 0x7b, 0xc6, 0xd9, 0x74, 0xdc, 0x19, 0xc8, 0xc1, 0x18, 0x98, 0x45, 0x39, 0xf8, 0xcc,
-    0x79, 0x30, 0x53, 0xf3, 0xb5, 0x75, 0x77, 0x17, 0x57, 0x66, 0x74, 0xd7, 0xbc, 0xdb, 0xb3, 0xda, 0xe4, 0xca, 0xf9, 0x3f,
-    0xdd, 0xd9, 0xee, 0xc8, 0xfc, 0x9f, 0xe1, 0x3a, 0x99, 0x67, 0xb9, 0xdf, 0x76, 0xbb, 0xf9, 0xbf, 0x2d, 0x36, 0x73, 0x79,
-    0xfe, 0x2f, 0xfe, 0xaf, 0x7e, 0xd2, 0xb2, 0xbd, 0x44, 0xfc, 0xaf, 0xbd, 0xfe, 0x9f, 0x33, 0x82, 0x54, 0x67, 0xd9, 0x6d,
-    0xf1, 0x33, 0x82, 0xfa, 0xab, 0xd6, 0xc5, 0xff, 0xf6, 0xad, 0xaa, 0x35, 0xbe, 0x5b, 0x6d, 0xca, 0x67, 0xc4, 0x7f, 0x4b,
-    0xfe, 0x9f, 0xe2, 0xfd, 0xff, 0xf6, 0x6f, 0xd8, 0x4a, 0x57, 0xce, 0xd2, 0x9e, 0x77, 0xbc, 0x67, 0x8f, 0xe0, 0x9d, 0x47,
-    0xb4, 0xb1, 0xe7, 0xff, 0x77, 0x1b, 0x83, 0xc7, 0xbe, 0x53, 0xe5, 0x3b, 0xf7, 0x7f, 0x75, 0x97, 0xf1, 0xd7, 0x72, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcf, 0x5e, 0xb7, 0x90, 0x4b, 0xd7, 0xcb, 0x57, 0xeb,
-    0xd6, 0xf5, 0xaf, 0x58, 0xcd, 0xa4, 0x96, 0xc9, 0xa4, 0xb3, 0xb5, 0x37, 0xac, 0xce, 0xcf, 0x60, 0xbb, 0x67, 0xf8, 0x77,
-    0xd2, 0xb0, 0x22, 0x2c, 0xa5, 0x33, 0x7c, 0xbf, 0x75, 0xab, 0x23, 0xab, 0x06, 0x33, 0xb5, 0x75, 0x67, 0x9e, 0xef, 0x9c,
-    0x56, 0xc3, 0xfe, 0x6a, 0x2f, 0x56, 0x5f, 0x49, 0x69, 0xc5, 0x64, 0xca, 0xb5, 0x09, 0x7a, 0xc7, 0xbb, 0x74, 0xb6, 0x59,
-    0xef, 0xb9, 0xfe, 0xda, 0xc6, 0xad, 0x55, 0x66, 0xbe, 0x6e, 0xdf, 0xcb, 0x3d, 0xfb, 0x73, 0xa8, 0xdf, 0xa7, 0x3c, 0x42,
-    0x6f, 0x53, 0xea, 0x7a, 0xb7, 0x54, 0xe3, 0xd8, 0x1b, 0xd6, 0xd0, 0x67, 0xb8, 0x36, 0x58, 0xca, 0xed, 0x30, 0xb3, 0xe7,
-    0x8d, 0x7d, 0xda, 0xd9, 0x5a, 0xed, 0x0c, 0xcc, 0xa0, 0x29, 0xd6, 0xbf, 0xcb, 0x60, 0x65, 0x88, 0x1c, 0xac, 0xf1, 0xce,
-    0xb2, 0xf5, 0xed, 0xe9, 0x8c, 0xfe, 0xf6, 0xe3, 0x4d, 0x73, 0x9b, 0xb6, 0xd5, 0xab, 0xfe, 0xb8, 0x7d, 0x56, 0xba, 0x97,
-    0x66, 0x9b, 0x5a, 0x9b, 0x5e, 0x9d, 0x6f, 0xdc, 0xbd, 0xb6, 0xc7, 0x79, 0xf5, 0xb1, 0xf5, 0xd7, 0x06, 0x95, 0x5a, 0x2c,
-    0xf3, 0xe2, 0x3f, 0xdd, 0x15, 0xa5, 0xf6, 0xa1, 0xab, 0x8e, 0xbe, 0xf8, 0x6f, 0x7b, 0xdf, 0x94, 0xe6, 0xff, 0xb3, 0xf8,
-    0x5b, 0xbb, 0x7d, 0xb4, 0xda, 0x7e, 0x35, 0xfe, 0xfb, 0xfb, 0x40, 0x16, 0xd4, 0x48, 0x3d, 0xcb, 0x4a, 0x2a, 0x5b, 0xc6,
-    0xaf, 0xb1, 0xd7, 0xc5, 0xff, 0x51, 0xeb, 0x8d, 0xe6, 0x1b, 0xb3, 0xe6, 0xff, 0x0c, 0xe7, 0xff, 0xad, 0x7d, 0x35, 0xcd,
-    0xdf, 0x2f, 0xd4, 0x92, 0x53, 0x1d, 0x55, 0x77, 0xde, 0x4f, 0xbf, 0x29, 0x63, 0x2b, 0x7e, 0x1b, 0xc6, 0xea, 0xed, 0x3d,
-    0xe3, 0xf6, 0x9c, 0xca, 0x9d, 0xd7, 0x6f, 0x9d, 0x31, 0xf6, 0xdd, 0x23, 0xfe, 0xb3, 0x28, 0xd3, 0xc9, 0x50, 0xbb, 0xa6,
-    0x6b, 0xdf, 0xaf, 0xb9, 0xbb, 0x94, 0xcb, 0xaa, 0x8a, 0xce, 0xf9, 0x2e, 0x8c, 0x75, 0xdb, 0x5b, 0xc7, 0xc1, 0xfd, 0x82,
-    0xf9, 0x7f, 0xe5, 0x1d, 0xd0, 0x7a, 0x55, 0xf8, 0x6b, 0xe2, 0xff, 0x59, 0xd7, 0x46, 0xeb, 0xfa, 0x76, 0x4f, 0xfc, 0xe7,
-    0xe2, 0xe3, 0xb8, 0x3e, 0xfe, 0x3f, 0xfe, 0x18, 0x41, 0xe9, 0xc8, 0x6b, 0xd7, 0x6c, 0x9f, 0x39, 0xbe, 0x8c, 0x8e, 0x75,
-    0xcf, 0xc9, 0xff, 0x9f, 0x1f, 0xff, 0x77, 0xbe, 0x43, 0x91, 0x37, 0xba, 0x43, 0x93, 0x5f, 0xee, 0xb2, 0x5c, 0xf7, 0x3c,
-    0xb6, 0x7e, 0xfd, 0xfd, 0x5a, 0xdb, 0xc7, 0xee, 0xff, 0x7f, 0xdf, 0xfc, 0xff, 0xce, 0x7f, 0xb3, 0xe0, 0xef, 0x36, 0x56,
-    0xb6, 0xd3, 0xfd, 0xe3, 0x7b, 0xfe, 0xc8, 0xfc, 0xe8, 0x6e, 0xc8, 0xf7, 0xcd, 0xff, 0xef, 0xff, 0x17, 0x4b, 0xaa, 0xad,
-    0x5e, 0x71, 0x4d, 0x72, 0xc7, 0xfb, 0x7b, 0xb3, 0xfb, 0xfb, 0xd1, 0xbd, 0xd0, 0x55, 0xf9, 0xff, 0x76, 0xf3, 0xfb, 0xff,
-    0xf0, 0x9a, 0xe3, 0x13, 0x20, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xf7, 0xaf, 0x29,
-    0x53, 0x5e, 0x37, 0x91, 0x1f, 0x6b, 0xc8, 0x3f, 0x06, 0x56, 0x23, 0xad, 0xab, 0x22, 0x7b, 0x5c, 0x03, 0xeb, 0x73, 0xe0,
-    0xef, 0x47, 0xd7, 0x1e, 0x57, 0x26, 0xad, 0x1f, 0x7d, 0xc6, 0x91, 0xa5, 0x78, 0xae, 0xc6, 0xd7, 0x32, 0xe7, 0xb7, 0xff,
-    0x9f, 0xbb, 0xd6, 0xf7, 0x71, 0xbd, 0x84, 0xb1, 0x33, 0xd2, 0xb3, 0xf2, 0xf0, 0x5e, 0x15, 0x0a, 0xf7, 0x1f, 0xed, 0x91,
-    0xc3, 0x3a, 0x92, 0x77, 0xae, 0x22, 0xbb, 0x97, 0xaa, 0xcb, 0xac, 0x3f, 0xae, 0xb1, 0x78, 0xc8, 0x49, 0x5d, 0xcf, 0xe7,
-    0x54, 0xbb, 0x3d, 0x3b, 0x57, 0xc7, 0xb5, 0xb0, 0xf7, 0xc6, 0xf5, 0xf2, 0xc7, 0x55, 0x91, 0x33, 0x38, 0xfa, 0x9d, 0x47,
-    0x7f, 0x4a, 0x47, 0xf7, 0xa7, 0x33, 0x96, 0xe6, 0x4f, 0xc8, 0xa4, 0xea, 0x5c, 0xc7, 0xad, 0xb1, 0xbf, 0xf0, 0x4a, 0xe1,
-    0xbd, 0x2b, 0xfe, 0xf3, 0x25, 0x43, 0xc8, 0x0b, 0xae, 0x6e, 0x69, 0x1d, 0x8f, 0x5f, 0x6b, 0xe5, 0x4c, 0x9a, 0x57, 0x84,
-    0x8f, 0x54, 0xdc, 0xb1, 0x9e, 0xaa, 0xbd, 0x27, 0x9e, 0xf7, 0xd3, 0x7b, 0x57, 0x91, 0xfd, 0x3c, 0x39, 0xae, 0xbc, 0x6c,
-    0xf5, 0x9b, 0xb6, 0x56, 0x49, 0x21, 0x3b, 0x78, 0xd6, 0xd6, 0xe3, 0x2c, 0xee, 0x4f, 0xf3, 0x7f, 0x2d, 0xb3, 0x79, 0xd6,
-    0xb1, 0xb7, 0xee, 0x6b, 0x96, 0x5c, 0x09, 0xb6, 0xf4, 0x84, 0x1c, 0x66, 0x5b, 0xaf, 0x18, 0x27, 0x2d, 0x99, 0xe8, 0x3d,
-    0xf7, 0x3b, 0x8d, 0xbd, 0x21, 0x07, 0x79, 0xf8, 0x5d, 0xeb, 0xde, 0x8e, 0x8c, 0x4a, 0x69, 0xb8, 0xc2, 0x6f, 0xc9, 0x0e,
-    0xf6, 0x4b, 0xb7, 0xde, 0x35, 0xfe, 0x5b, 0xe6, 0xff, 0xbc, 0x69, 0xfc, 0xef, 0x37, 0xdf, 0xef, 0xfa, 0xf7, 0x9f, 0xb5,
-    0xce, 0xff, 0xf7, 0xda, 0x3a, 0x33, 0x83, 0x7f, 0x7c, 0xbf, 0x6d, 0x5d, 0xa5, 0xcf, 0xfe, 0xfb, 0x8e, 0xaf, 0x11, 0xff,
-    0x77, 0x9e, 0x27, 0xeb, 0xf1, 0xff, 0xf9, 0x02, 0xfb, 0x9d, 0xd2, 0x1d, 0x9d, 0xb3, 0xf8, 0xdf, 0x6f, 0xb9, 0x75, 0x66,
-    0x65, 0xab, 0xea, 0xb3, 0xaa, 0x95, 0x95, 0x3e, 0xef, 0x1b, 0xff, 0x2d, 0xf9, 0xd2, 0xab, 0x5e, 0x27, 0x6f, 0xc3, 0x11,
-    0x74, 0xd7, 0xf8, 0x3f, 0x7f, 0xf2, 0x50, 0x7f, 0x42, 0xf6, 0x8c, 0xad, 0xfb, 0x45, 0x77, 0xf0, 0x9e, 0x77, 0xec, 0xcf,
-    0x8b, 0xff, 0x91, 0x0a, 0x85, 0xc7, 0xf7, 0x5d, 0xee, 0x70, 0x9d, 0xbc, 0xa2, 0x1e, 0xe2, 0x3d, 0xf6, 0x3b, 0x8b, 0xe6,
-    0xa2, 0x7b, 0x3f, 0xc9, 0x71, 0x07, 0xff, 0x9e, 0x4f, 0x0d, 0x6a, 0xfd, 0x78, 0xf5, 0xf6, 0x67, 0xf5, 0xc6, 0x2b, 0xf6,
-    0x5b, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xa7, 0x95, 0x3c, 0x39, 0x59,
-    0x05, 0xbc, 0x75, 0xd5, 0x2e, 0xfa, 0x55, 0xb5, 0x66, 0x6a, 0x26, 0x54, 0xc9, 0x7c, 0xb4, 0xf6, 0x66, 0xbf, 0xfc, 0x68,
-    0xb6, 0xc1, 0x77, 0x86, 0xab, 0x56, 0xf3, 0xfd, 0x69, 0xbd, 0x78, 0xb5, 0xcf, 0x57, 0x6b, 0xa6, 0x8e, 0xd5, 0x8b, 0x3d,
-    0x5b, 0x29, 0xfd, 0x59, 0x1c, 0xcf, 0x56, 0x55, 0x80, 0x3d, 0x7a, 0xe7, 0xf3, 0x7a, 0xbe, 0xd6, 0x03, 0x32, 0x1e, 0xfd,
-    0xdf, 0xa1, 0x07, 0x65, 0x59, 0x5e, 0xb1, 0x72, 0x9f, 0x3f, 0xf4, 0x50, 0x9e, 0x94, 0x03, 0xb4, 0xad, 0x65, 0xdf, 0x6f,
-    0xb5, 0xf5, 0xf1, 0x3e, 0xa7, 0xb1, 0xb2, 0xdf, 0xbd, 0xb6, 0x8e, 0xd4, 0xf3, 0x85, 0x96, 0x2b, 0xe2, 0xf3, 0xaa, 0xa8,
-    0xaf, 0xb3, 0xb5, 0xe5, 0x68, 0xb7, 0x37, 0x39, 0x56, 0xf1, 0xcf, 0xac, 0x39, 0xff, 0x9d, 0x7a, 0x7d, 0x86, 0xda, 0x42,
-    0xfc, 0x23, 0xfe, 0xbf, 0x5b, 0xaf, 0x17, 0xff, 0x7c, 0xdf, 0xfc, 0x7f, 0x1f, 0xaa, 0x17, 0xbf, 0xdd, 0x6a, 0xeb, 0xf8,
-    0xb5, 0xd0, 0x7d, 0xb6, 0xde, 0xb5, 0x0a, 0x22, 0x70, 0xc5, 0x1d, 0xd8, 0xb1, 0x27, 0xb8, 0xc0, 0x6b, 0xe4, 0x60, 0x95,
-    0xb8, 0x36, 0xfb, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xdd, 0xdd, 0x72, 0xe3, 0x36, 0x12, 0x06, 0x50, 0x96, 0x6b, 0xdf, 0xff, 0x85, 0x1d, 0xec, 0xb7, 0xb5, 0x93, 0x4c, 0xe2,
+    0xa9, 0x8c, 0x88, 0x7f, 0x8a, 0x92, 0xcf, 0x9c, 0xaa, 0xb9, 0x30, 0x2d, 0x89, 0x04, 0xd1, 0x40, 0x93, 0x34, 0x5a, 0x39,
+    0x00, 0x00, 0x00, 0x00, 0x16, 0xcb, 0xc5, 0x9f, 0x96, 0xe6, 0x9f, 0xae, 0x3b, 0xa2, 0x9f, 0xff, 0x9e, 0x7b, 0xec, 0xfa,
+    0x57, 0x7f, 0xbb, 0xe7, 0xe1, 0xb9, 0xd3, 0x9e, 0x5f, 0x7f, 0x92, 0xd3, 0xdf, 0xcd, 0x50, 0xeb, 0xa6, 0x7a, 0xc6, 0xce,
+    0xcf, 0x4c, 0x5f, 0xbc, 0x3d, 0xde, 0x87, 0x74, 0xbf, 0x62, 0xe7, 0xc8, 0xf8, 0xf3, 0x5f, 0xe9, 0xea, 0xef, 0xe7, 0xbd,
+    0xb8, 0xad, 0x97, 0xa7, 0xb2, 0x47, 0x69, 0xf8, 0x8d, 0xda, 0xeb, 0x8f, 0x89, 0xbd, 0x2f, 0x97, 0xce, 0x45, 0xa9, 0x8e,
+    0x19, 0xc7, 0x86, 0xa3, 0xc9, 0xe4, 0xb9, 0xcc, 0xf4, 0x88, 0xd6, 0x1e, 0xff, 0xb5, 0x7e, 0x91, 0x7f, 0xfd, 0x7f, 0x0c,
+    0x45, 0x5a, 0x36, 0xc6, 0xff, 0xd1, 0x35, 0xbe, 0xec, 0xcf, 0x8b, 0xd2, 0x10, 0xe9, 0xfd, 0xaf, 0x69, 0x3d, 0xda, 0xb1,
+    0x11, 0x2f, 0xcd, 0xbf, 0x9b, 0xa1, 0x23, 0x5e, 0x75, 0x46, 0x7a, 0x47, 0xce, 0x5a, 0x6b, 0xec, 0x38, 0x9a, 0xaf, 0x3f,
+    0xfb, 0x9c, 0x7c, 0x7d, 0xd9, 0x3e, 0x46, 0x96, 0x86, 0x96, 0x2a, 0xc3, 0x9f, 0x98, 0x81, 0x6c, 0x6e, 0xd5, 0xd9, 0xdf,
+    0x9d, 0x17, 0x66, 0x38, 0x96, 0x32, 0x75, 0xf6, 0xce, 0x3f, 0x77, 0x74, 0xfe, 0xde, 0x3b, 0xe2, 0xf5, 0xe7, 0xfa, 0x59,
+    0x34, 0x9a, 0x8f, 0x8d, 0x66, 0x3f, 0x5b, 0x2a, 0x1b, 0xe2, 0x77, 0xdd, 0x58, 0xbc, 0x32, 0x47, 0x9a, 0xc9, 0x3b, 0xcf,
+    0xb6, 0x7d, 0xfe, 0xd0, 0xbe, 0x57, 0xaf, 0x32, 0xff, 0x67, 0xe2, 0xfc, 0x65, 0xa8, 0x9d, 0x6b, 0x39, 0x65, 0xeb, 0x11,
+    0x9f, 0x7f, 0x7a, 0x19, 0x38, 0xe6, 0x8f, 0x94, 0xbf, 0x1d, 0x4b, 0x72, 0xda, 0x2c, 0x3c, 0x97, 0xe9, 0xbe, 0x56, 0xfa,
+    0x33, 0x6e, 0xc7, 0x73, 0xe6, 0xe4, 0xbf, 0x3f, 0x94, 0xcd, 0xb9, 0xd8, 0x15, 0xf1, 0xff, 0xcf, 0x28, 0x96, 0x89, 0x88,
+    0xcd, 0xe4, 0x0c, 0xb3, 0xf6, 0xfa, 0x7f, 0x5e, 0x99, 0x3a, 0x7f, 0x59, 0x7e, 0x55, 0xfb, 0x4f, 0xf4, 0x66, 0x28, 0x7f,
+    0xc8, 0xb2, 0x51, 0xb6, 0x6c, 0xbc, 0x97, 0x9c, 0x4d, 0x59, 0xda, 0xa3, 0xf9, 0xff, 0xba, 0x6c, 0x67, 0xcf, 0xd5, 0x52,
+    0xcf, 0xbd, 0xd3, 0xd1, 0xeb, 0xff, 0x34, 0x8e, 0x2e, 0xfb, 0xe2, 0xff, 0x7a, 0x7f, 0x4c, 0x9e, 0xbf, 0x5c, 0x7c, 0x7d,
+    0xbe, 0xe6, 0x0a, 0x7d, 0x3c, 0xe3, 0xd9, 0x7b, 0xfd, 0x7f, 0xd5, 0x88, 0xb1, 0x23, 0xff, 0xcf, 0x8b, 0xe7, 0xff, 0x3d,
+    0xf7, 0xff, 0xf3, 0x26, 0xf1, 0x9f, 0xe9, 0x27, 0x52, 0x59, 0x7e, 0x7f, 0xbe, 0x2d, 0xdb, 0x9d, 0x7b, 0xbe, 0x90, 0xa9,
+    0x23, 0x5e, 0x75, 0x2f, 0x26, 0x8b, 0x7e, 0x37, 0x8d, 0xb3, 0xe2, 0xd8, 0xfd, 0xdd, 0xe7, 0xde, 0xff, 0xf7, 0xfc, 0x1f,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x9e, 0xd5, 0x9d, 0xb9, 0xe0, 0x75,
+    0x23, 0xab, 0x68, 0x67, 0x56, 0xdd, 0xce, 0xac, 0xd9, 0x1d, 0x7d, 0x65, 0xbd, 0x0e, 0xd1, 0xf8, 0x3a, 0xe3, 0xf6, 0x55,
+    0xce, 0x7d, 0xeb, 0x86, 0xe7, 0xaa, 0xa3, 0xa4, 0xba, 0x8e, 0xbe, 0xad, 0x1e, 0x41, 0xbd, 0x76, 0x6b, 0xad, 0x92, 0xd5,
+    0xfe, 0xda, 0xaf, 0x3b, 0x6b, 0x53, 0x66, 0xb8, 0x05, 0x7b, 0x56, 0xbf, 0xa7, 0xab, 0x3e, 0xc1, 0xe8, 0x3a, 0xf4, 0xeb,
+    0xaa, 0x02, 0xce, 0xaf, 0x90, 0x3f, 0x7f, 0xe7, 0xb2, 0xfd, 0x28, 0xda, 0xf7, 0x38, 0x0b, 0xce, 0xd9, 0x8e, 0xf8, 0xcf,
+    0x69, 0x45, 0x80, 0x5c, 0x58, 0xdb, 0x75, 0x5f, 0x25, 0xdc, 0x7d, 0x95, 0x76, 0xea, 0xf1, 0xdf, 0x5e, 0xb3, 0x39, 0x8b,
+    0xa3, 0x35, 0xc3, 0xd1, 0xb3, 0x6a, 0x04, 0x18, 0x19, 0xb7, 0x56, 0xc4, 0xff, 0x68, 0xee, 0xf0, 0xda, 0xf1, 0x3f, 0x5a,
+    0x4f, 0x2c, 0x27, 0xe7, 0x3d, 0x8d, 0x7d, 0xfb, 0x0e, 0xdb, 0x8f, 0x9b, 0xc6, 0x7f, 0x3d, 0xa6, 0x32, 0x5d, 0xa5, 0x78,
+    0x5d, 0x7f, 0x5a, 0xa7, 0x34, 0x45, 0xe9, 0x58, 0x4d, 0xcf, 0x32, 0x3c, 0xb2, 0xec, 0xca, 0xff, 0x53, 0xb9, 0xf2, 0x48,
+    0x53, 0x2e, 0xbb, 0x76, 0x8c, 0x6d, 0x1d, 0xb5, 0x1e, 0x57, 0x14, 0xab, 0xd5, 0x2b, 0x7c, 0xfe, 0xfc, 0x3e, 0x57, 0x4f,
+    0xf9, 0x9a, 0xf8, 0x9f, 0x8f, 0xc4, 0x3c, 0x65, 0xce, 0x5a, 0x73, 0x6f, 0x22, 0x83, 0xf3, 0xd6, 0x9e, 0xd7, 0x5e, 0x31,
+    0xff, 0xe7, 0xa5, 0xe6, 0xff, 0x96, 0xeb, 0xff, 0x2c, 0xb8, 0x7a, 0x1d, 0x6b, 0xb5, 0xd2, 0x7c, 0x2e, 0x77, 0xe4, 0xff,
+    0x2d, 0x33, 0xfb, 0xfc, 0xfc, 0x3f, 0xb3, 0x35, 0xcd, 0xb3, 0xf0, 0xf5, 0xf1, 0xdf, 0x76, 0x4c, 0xb9, 0xf4, 0xb5, 0xaf,
+    0x17, 0xff, 0xb9, 0xc5, 0xfd, 0xbf, 0x6c, 0xac, 0xcd, 0x9c, 0x25, 0xa3, 0xdb, 0x9e, 0xf8, 0x6f, 0xab, 0x87, 0x3e, 0x73,
+    0xfd, 0x3f, 0x17, 0xe1, 0xf5, 0x08, 0xf8, 0x43, 0xfc, 0x2f, 0xca, 0xff, 0x57, 0xbd, 0xba, 0xff, 0x5e, 0xc9, 0xcc, 0x0c,
+    0xda, 0x7e, 0x4c, 0x59, 0x9e, 0x7f, 0xcf, 0xd6, 0xf6, 0xdd, 0x9d, 0xff, 0xaf, 0x7a, 0xfd, 0x31, 0x7c, 0xde, 0xeb, 0x5b,
+    0xe6, 0x7a, 0xe5, 0xf1, 0x84, 0x6f, 0x73, 0xd8, 0xfd, 0x7c, 0xcf, 0x77, 0x94, 0xbe, 0xdf, 0x53, 0x6c, 0x6d, 0x70, 0xef,
+    0xe7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x5e, 0x65, 0x75, 0xbf,
+    0x15, 0x53, 0x69, 0x58, 0x1f, 0xda, 0x52, 0x3f, 0xf3, 0xbc, 0xfa, 0xe6, 0xa3, 0x1a, 0x12, 0xa5, 0xa9, 0xf6, 0xe6, 0xaa,
+    0xd6, 0x6c, 0xad, 0x83, 0x30, 0x5b, 0xa7, 0x73, 0xe4, 0xdd, 0x5b, 0x5a, 0xb9, 0xbe, 0xe6, 0x31, 0x95, 0x75, 0xc0, 0xc7,
+    0xc2, 0x1e, 0x58, 0x5f, 0xf3, 0x9c, 0x89, 0x9a, 0x0b, 0x33, 0x55, 0x04, 0xb3, 0x71, 0x85, 0x69, 0x86, 0x5a, 0xa5, 0xbd,
+    0x02, 0x47, 0xeb, 0xd6, 0x2c, 0xaa, 0xf2, 0xb0, 0xbb, 0x5f, 0xaf, 0xd8, 0xda, 0x5f, 0x47, 0xe1, 0x19, 0xf1, 0x7f, 0xde,
+    0x43, 0xb2, 0xf8, 0x6c, 0xf7, 0xfd, 0xb4, 0x16, 0xfb, 0x8f, 0x6b, 0x2b, 0x7f, 0x34, 0xf6, 0x9b, 0x9e, 0xed, 0x99, 0x3e,
+    0xcb, 0x99, 0xac, 0x84, 0x50, 0x3a, 0xe7, 0x9b, 0xf6, 0xe8, 0x9f, 0xeb, 0x79, 0xfb, 0x56, 0xc0, 0xbf, 0x53, 0xfc, 0x1f,
+    0x83, 0x15, 0x0b, 0x77, 0xc7, 0xff, 0xd1, 0xb4, 0x4f, 0x59, 0x76, 0x44, 0xeb, 0xaa, 0x6f, 0x7f, 0x74, 0xe7, 0x58, 0x6b,
+    0x6a, 0x59, 0xe5, 0xc2, 0x5a, 0x0f, 0xe9, 0xe8, 0x5d, 0xa3, 0x23, 0x40, 0x06, 0xe7, 0xff, 0xfe, 0x96, 0x5e, 0x95, 0x63,
+    0xbc, 0x66, 0xfc, 0x8f, 0xb5, 0x66, 0x26, 0xce, 0xf0, 0xde, 0xf9, 0x7f, 0x65, 0xe5, 0xee, 0x91, 0xe8, 0x28, 0x0f, 0xde,
+    0xa5, 0x0c, 0xcf, 0xce, 0xe3, 0x57, 0x07, 0x47, 0xd3, 0x77, 0x32, 0x94, 0xee, 0xd7, 0xe5, 0x97, 0xf8, 0xcc, 0x96, 0x6b,
+    0xf4, 0x9c, 0xb4, 0xf0, 0x68, 0x35, 0xd2, 0xde, 0xf8, 0xcf, 0xa5, 0xf3, 0xff, 0xd1, 0xf8, 0xed, 0x19, 0xc7, 0xd0, 0xf9,
+    0x3a, 0x06, 0xdf, 0x75, 0xfc, 0x95, 0xa9, 0xf6, 0xd8, 0x3d, 0xf5, 0x68, 0x57, 0x7e, 0x43, 0x47, 0x16, 0xf4, 0xdb, 0x54,
+    0x3e, 0x21, 0x83, 0x39, 0x78, 0x6f, 0xe6, 0xdf, 0x33, 0xce, 0xa6, 0xe3, 0xce, 0x40, 0x4e, 0xc6, 0xc0, 0x6c, 0xca, 0xc1,
+    0x57, 0xce, 0x83, 0x59, 0x9a, 0xaf, 0xed, 0xbb, 0xbb, 0xb8, 0x33, 0xa3, 0xbb, 0xe6, 0xdd, 0x9e, 0xd5, 0x26, 0x57, 0xce,
+    0xff, 0xe9, 0xce, 0x76, 0x67, 0xe6, 0xff, 0x4c, 0xd7, 0xc9, 0xac, 0xe5, 0x7e, 0xc7, 0xed, 0xe6, 0xff, 0xb6, 0xd8, 0xcc,
+    0xe5, 0xf9, 0xbf, 0xf8, 0xbf, 0xfa, 0x49, 0xcb, 0xf1, 0x12, 0xf1, 0xbf, 0xf7, 0xfa, 0x7f, 0xcd, 0x08, 0x32, 0x3a, 0xcb,
+    0x1e, 0x9b, 0x9f, 0x11, 0x8c, 0xbf, 0x6a, 0x5f, 0xfc, 0x1f, 0xdf, 0xaa, 0x5a, 0xe3, 0xbb, 0xd5, 0xa6, 0x7c, 0x46, 0xfc,
+    0xb7, 0xe4, 0xff, 0x19, 0xbc, 0xff, 0xdf, 0xfe, 0x0d, 0x5b, 0xe9, 0xca, 0x59, 0xda, 0xf3, 0x8e, 0xf7, 0xec, 0x11, 0xbc,
+    0xf3, 0x88, 0x36, 0xf7, 0xfc, 0xff, 0x6e, 0x63, 0xf0, 0xdc, 0x77, 0xaa, 0x7c, 0xe7, 0xfe, 0xaf, 0xee, 0x32, 0xfe, 0x5a,
+    0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xd9, 0xeb, 0x16, 0x72, 0xe9, 0x7a, 0xf9,
+    0xd1, 0xba, 0x75, 0xfd, 0x2b, 0x56, 0xb3, 0xa8, 0x65, 0xb2, 0xe8, 0x6c, 0x95, 0x86, 0xd5, 0xf9, 0x99, 0x6c, 0xf7, 0x4c,
+    0xff, 0x4e, 0x1a, 0x56, 0x84, 0x65, 0xe8, 0x0c, 0xdf, 0x6f, 0xdd, 0xea, 0xcc, 0xaa, 0xc1, 0x2c, 0x6d, 0xdd, 0x95, 0xe7,
+    0x3b, 0xd5, 0x6a, 0xd8, 0x5f, 0x95, 0xc1, 0xea, 0x2b, 0x19, 0x5a, 0x31, 0x99, 0xe1, 0xda, 0x04, 0xbd, 0xe3, 0x5d, 0x3a,
+    0xdb, 0xac, 0xf7, 0x5c, 0x7f, 0x6d, 0xe3, 0xd6, 0x2a, 0x33, 0x5f, 0xb7, 0x97, 0xc1, 0x33, 0xfe, 0x91, 0xcf, 0xa9, 0x7e,
+    0x9f, 0xe1, 0x11, 0xfa, 0x58, 0x52, 0xd7, 0xbb, 0xa5, 0x1a, 0x47, 0x69, 0x58, 0x43, 0x9f, 0xe9, 0xda, 0x60, 0x19, 0x6e,
+    0x87, 0x95, 0x3d, 0x6f, 0xee, 0xd3, 0x6a, 0x6b, 0xb5, 0x33, 0x31, 0x83, 0x66, 0xb0, 0xfe, 0x5d, 0x26, 0x2b, 0x43, 0xe4,
+    0x64, 0x8d, 0x77, 0xb6, 0xad, 0x6f, 0x4f, 0x67, 0xf4, 0xb7, 0x1f, 0x6f, 0x9a, 0xdb, 0xb4, 0xad, 0x5e, 0xf5, 0xc7, 0xed,
+    0xb3, 0xd2, 0x32, 0x34, 0xdb, 0x8c, 0xb5, 0xe9, 0xd5, 0xf9, 0xc6, 0xdd, 0x6b, 0x7b, 0xd4, 0xab, 0x8f, 0xed, 0xbf, 0x36,
+    0x18, 0xa9, 0xc5, 0xb2, 0x2e, 0xfe, 0xd3, 0x5d, 0x51, 0xaa, 0x4c, 0x5d, 0x75, 0xf4, 0xc5, 0x7f, 0xdb, 0xfb, 0x66, 0x68,
+    0xfe, 0xaf, 0xc5, 0xdf, 0xde, 0xed, 0xb3, 0xd5, 0xf6, 0x47, 0xe3, 0xbf, 0xbf, 0x0f, 0x64, 0x43, 0x8d, 0xd4, 0x5a, 0x56,
+    0x32, 0xb2, 0x65, 0xfe, 0x1a, 0x7b, 0x5f, 0xfc, 0x9f, 0xb5, 0xde, 0x6c, 0xbe, 0xb1, 0x6a, 0xfe, 0xcf, 0x74, 0xfe, 0xdf,
+    0xda, 0x57, 0xd3, 0xfc, 0xfd, 0x42, 0x2d, 0x39, 0x55, 0x1e, 0xc6, 0x7e, 0xc9, 0x67, 0xf5, 0x9b, 0x32, 0x8e, 0xc1, 0x6f,
+    0xc3, 0xd8, 0xbd, 0xbd, 0x67, 0xdc, 0x5e, 0x53, 0xb9, 0xf3, 0xfa, 0xad, 0x2b, 0xc6, 0xbe, 0x7b, 0xc4, 0x7f, 0x36, 0x65,
+    0x3a, 0x99, 0x6a, 0xd7, 0x74, 0xed, 0xfb, 0x35, 0x77, 0x97, 0x72, 0x59, 0x55, 0xd1, 0xff, 0xc7, 0xfe, 0xc7, 0x70, 0x4e,
+    0xbc, 0x7f, 0x7b, 0xeb, 0x38, 0x58, 0x2e, 0x98, 0xff, 0x77, 0xde, 0x01, 0x1d, 0xaf, 0x0a, 0x7f, 0x4d, 0xfc, 0x3f, 0xeb,
+    0xda, 0xe8, 0xd8, 0x32, 0x36, 0xf6, 0xc6, 0x7f, 0x2e, 0x3e, 0x8e, 0xeb, 0xe2, 0xbf, 0x9c, 0x5e, 0x03, 0xa4, 0x23, 0xaf,
+    0xdd, 0xb3, 0x7d, 0xe5, 0xf8, 0x32, 0xfb, 0xbd, 0x3f, 0xcf, 0xc9, 0xff, 0x9f, 0x1f, 0xff, 0x77, 0xbe, 0x43, 0x91, 0x37,
+    0xba, 0x43, 0x93, 0x5f, 0xee, 0xb2, 0x5c, 0xf3, 0x3c, 0xf6, 0x3f, 0x29, 0x3f, 0x7c, 0x3e, 0xf8, 0x1e, 0xad, 0x7b, 0xe7,
+    0xff, 0xbd, 0xf1, 0x3f, 0x73, 0xff, 0xff, 0xfb, 0xe6, 0xff, 0x77, 0xfe, 0x9b, 0x05, 0x7f, 0xb7, 0xb1, 0xb3, 0x9d, 0xee,
+    0x1f, 0xdf, 0xeb, 0x47, 0xe6, 0xf2, 0xe0, 0x7a, 0xe8, 0xfb, 0xe6, 0xff, 0xf7, 0xff, 0x8b, 0x25, 0xd5, 0x56, 0xaf, 0xb8,
+    0x26, 0xb9, 0xe3, 0xfd, 0xbd, 0xd5, 0xfd, 0xbd, 0x9c, 0xdc, 0x0b, 0xdd, 0x95, 0xff, 0x1f, 0x37, 0xbf, 0xff, 0x0f, 0xaf,
+    0x39, 0x3e, 0x01, 0xe2, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7d, 0xff, 0x9a, 0x32, 0xc3,
+    0xeb, 0x26, 0xf2, 0xa3, 0x96, 0xcc, 0xc7, 0xc4, 0x6a, 0xa4, 0x7d, 0x55, 0x64, 0xcf, 0x6b, 0x60, 0x7d, 0x4e, 0xfc, 0xfd,
+    0xe8, 0xde, 0xe3, 0xca, 0xa2, 0xf5, 0xa3, 0xcf, 0x38, 0xb2, 0x0c, 0x9e, 0xab, 0xb9, 0xcf, 0xfc, 0x77, 0x55, 0xa4, 0x63,
+    0xf9, 0x5a, 0xdf, 0xe3, 0xaf, 0xba, 0x49, 0xab, 0xcf, 0x48, 0xcf, 0xca, 0xc3, 0x7b, 0x55, 0x28, 0x2c, 0x3f, 0xda, 0x23,
+    0xa7, 0x75, 0x24, 0xef, 0x5c, 0x45, 0xb6, 0x0c, 0x55, 0x97, 0xd9, 0x7f, 0x5c, 0x73, 0xf1, 0x90, 0x4a, 0x5d, 0xcf, 0xe7,
+    0x54, 0xbb, 0xad, 0x9d, 0xab, 0xf3, 0x5a, 0xd8, 0xa5, 0x71, 0xbd, 0xfc, 0x79, 0x55, 0xe4, 0x4c, 0x8e, 0x7e, 0xf5, 0xe8,
+    0xcf, 0xd0, 0xd1, 0xfd, 0xee, 0x8c, 0xa5, 0xf9, 0x13, 0xb2, 0xa8, 0x3a, 0xd7, 0x79, 0x6b, 0x94, 0x17, 0x5e, 0x29, 0x5c,
+    0xba, 0xe2, 0x3f, 0x5f, 0x32, 0x84, 0xbc, 0xe0, 0xea, 0x96, 0xd6, 0xf1, 0xf8, 0xb5, 0x56, 0xce, 0xa4, 0x79, 0x45, 0xf8,
+    0x4c, 0xc5, 0x1d, 0xeb, 0xa9, 0xda, 0x7b, 0x62, 0xbd, 0x9f, 0xde, 0xbb, 0x8a, 0xec, 0x67, 0xe5, 0xb8, 0xf2, 0xb2, 0xd5,
+    0x6f, 0xda, 0x5a, 0x25, 0x03, 0xd9, 0xc1, 0xb3, 0xb6, 0x9e, 0x67, 0x71, 0xbf, 0x9b, 0xff, 0xc7, 0x32, 0x9b, 0x67, 0x1d,
+    0x7b, 0xeb, 0xbe, 0x66, 0xcb, 0x95, 0x60, 0x4b, 0x4f, 0xc8, 0x69, 0xb6, 0xf5, 0x8a, 0x71, 0xd2, 0x92, 0x89, 0xde, 0x73,
+    0xbf, 0xd3, 0xd8, 0x1b, 0x72, 0x92, 0x87, 0xdf, 0xb5, 0xee, 0xed, 0xcc, 0xa8, 0x94, 0x86, 0x2b, 0xfc, 0x96, 0xec, 0xa0,
+    0x5c, 0xba, 0xf5, 0xae, 0xf1, 0xdf, 0x32, 0xff, 0xe7, 0x4d, 0xe3, 0xbf, 0xdc, 0x7c, 0xbf, 0xc7, 0xbf, 0xff, 0xac, 0x75,
+    0xfe, 0xbf, 0xd7, 0xd6, 0x95, 0x19, 0xfc, 0xe3, 0xfb, 0x6d, 0xfb, 0x2a, 0x7d, 0xf6, 0xdf, 0x77, 0x7c, 0x8d, 0xf8, 0xbf,
+    0xf3, 0x3c, 0x39, 0x1e, 0xff, 0x9f, 0x2f, 0xb0, 0xdf, 0x19, 0xba, 0xa3, 0x53, 0x8b, 0xff, 0x72, 0xcb, 0xad, 0x2b, 0x2b,
+    0x5b, 0x8d, 0x3e, 0xab, 0xda, 0x59, 0xe9, 0xf3, 0xbe, 0xf1, 0xdf, 0x92, 0x2f, 0xbd, 0xea, 0x75, 0xf2, 0x31, 0x1d, 0x41,
+    0x77, 0x8d, 0xff, 0xfa, 0x93, 0x87, 0xf1, 0x27, 0x64, 0xcf, 0xd8, 0x5a, 0x2e, 0xba, 0x83, 0xf7, 0xbc, 0x63, 0x7f, 0x5e,
+    0xfc, 0xcf, 0x54, 0x28, 0x3c, 0xbf, 0xef, 0x72, 0x87, 0xeb, 0xe4, 0x1d, 0xf5, 0x10, 0xef, 0xb1, 0xdf, 0xd9, 0x34, 0x17,
+    0xdd, 0xfb, 0x49, 0x8e, 0x3b, 0xf8, 0xf7, 0x7c, 0x6a, 0x30, 0xd6, 0x8f, 0x77, 0x6f, 0x7f, 0x56, 0x6f, 0xbc, 0x62, 0xbf,
+    0x45, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x77, 0x5a, 0xc9, 0x93, 0xca, 0x2a,
+    0xe0, 0xa3, 0xab, 0x76, 0xd1, 0xaf, 0x46, 0x6b, 0xa6, 0x66, 0x41, 0x95, 0xcc, 0x47, 0x6b, 0x6f, 0xca, 0xe5, 0x47, 0x73,
+    0x4c, 0xbe, 0x33, 0x5c, 0xb5, 0x9a, 0xef, 0x77, 0xeb, 0xc5, 0x47, 0xfb, 0xfc, 0x68, 0xcd, 0xd4, 0xb9, 0x7a, 0xb1, 0xb5,
+    0x95, 0xd2, 0x9f, 0x83, 0xe3, 0xd9, 0xae, 0x0a, 0xb0, 0x67, 0xef, 0x5c, 0xaf, 0xe7, 0x6b, 0x3d, 0x20, 0xf3, 0xd1, 0xff,
+    0x1d, 0x7a, 0x50, 0xb6, 0xe5, 0x15, 0x3b, 0xf7, 0xf9, 0x43, 0x0f, 0xe5, 0x49, 0x39, 0x40, 0xdb, 0x5a, 0xf6, 0x72, 0xab,
+    0xad, 0x8f, 0xf7, 0x39, 0x8d, 0x95, 0xfd, 0xee, 0xb5, 0x75, 0xa6, 0x9e, 0x2f, 0xb4, 0x5c, 0x11, 0xd7, 0xab, 0xa2, 0xbe,
+    0xce, 0xd6, 0x96, 0xa3, 0x3d, 0xde, 0xe4, 0x58, 0xc5, 0x3f, 0xab, 0xe6, 0xfc, 0x77, 0xea, 0xf5, 0x99, 0x6a, 0x0b, 0xf1,
+    0x8f, 0xf8, 0xff, 0x6e, 0xbd, 0x5e, 0xfc, 0xf3, 0x7d, 0xf3, 0xff, 0x32, 0x55, 0x2f, 0xfe, 0xb8, 0xd5, 0xd6, 0xf9, 0x6b,
+    0xa1, 0xfb, 0x6c, 0xbd, 0x6b, 0x15, 0x44, 0xe0, 0x8a, 0x3b, 0xb0, 0x73, 0x4f, 0x70, 0x81, 0xd7, 0xc8, 0xc1, 0x46, 0xe2,
+    0xda, 0xec, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xe2, 0xdf, 0x7f, 0xda, 0x01, 0xc4, 0x3f,
-    0xf0, 0xed, 0xe2, 0xff, 0x1f };
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x8b, 0x3f, 0xff, 0x69, 0x07, 0x10, 0xff, 0xc0,
+    0xb7, 0x8b, 0xff, 0xff, 0x01 };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle cyberFontRecs[189] = {
@@ -352,7 +353,7 @@
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo cyberFontGlyphs[189] = {
-    { 32, 0, 11, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 0, 3, 2, { 0 }},
     { 34, 0, 3, 4, { 0 }},
     { 35, 0, 3, 8, { 0 }},
@@ -460,7 +461,7 @@
     { 171, 0, 6, 6, { 0 }},
     { 172, 0, 7, 6, { 0 }},
     { 174, 0, 3, 9, { 0 }},
-    { 175, 0, 11, 3, { 0 }},
+    { 175, 0, 0, 0, { 0 }},
     { 176, 0, 3, 4, { 0 }},
     { 177, 0, 6, 8, { 0 }},
     { 178, 0, 3, 4, { 0 }},
diff --git a/raygui/styles/dark/style_dark.h b/raygui/styles/dark/style_dark.h
--- a/raygui/styles/dark/style_dark.h
+++ b/raygui/styles/dark/style_dark.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,144 +15,144 @@
 
 // Custom style name: Dark
 static const GuiStyleProp darkStyleProps[DARK_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x878787ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x2c2c2cff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xc3c3c3ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xe1e1e1ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x848484ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0x181818ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0x000000ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xefefefff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x202020ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x6a6a6aff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x818181ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x606060ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0x9d9d9dff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x3c3c3cff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
-    { 1, 5, 0xf7f7f7ff },    // LABEL_TEXT_COLOR_FOCUSED 
-    { 1, 8, 0x898989ff },    // LABEL_TEXT_COLOR_PRESSED 
-    { 4, 5, 0xb0b0b0ff },    // SLIDER_TEXT_COLOR_FOCUSED 
-    { 5, 5, 0x848484ff },    // PROGRESSBAR_TEXT_COLOR_FOCUSED 
-    { 9, 5, 0xf5f5f5ff },    // TEXTBOX_TEXT_COLOR_FOCUSED 
-    { 10, 5, 0xf6f6f6ff },    // VALUEBOX_TEXT_COLOR_FOCUSED 
+    { 0, 0, (int)0x878787ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x2c2c2cff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xc3c3c3ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xe1e1e1ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x848484ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0x181818ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x000000ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xefefefff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x202020ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x6a6a6aff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x818181ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x606060ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x9d9d9dff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x3c3c3cff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 1, 5, (int)0xf7f7f7ff },    // LABEL_TEXT_COLOR_FOCUSED 
+    { 1, 8, (int)0x898989ff },    // LABEL_TEXT_COLOR_PRESSED 
+    { 4, 5, (int)0xb0b0b0ff },    // SLIDER_TEXT_COLOR_FOCUSED 
+    { 5, 5, (int)0x848484ff },    // PROGRESSBAR_TEXT_COLOR_FOCUSED 
+    { 9, 5, (int)0xf5f5f5ff },    // TEXTBOX_TEXT_COLOR_FOCUSED 
+    { 10, 5, (int)0xf6f6f6ff },    // VALUEBOX_TEXT_COLOR_FOCUSED 
 };
 
 // WARNING: This style uses a custom font: "PixelOperator.ttf" (size: 16, spacing: 0)
 
-#define DARK_STYLE_FONT_ATLAS_COMP_SIZE 2126
+#define DARK_STYLE_FONT_ATLAS_COMP_SIZE 2138
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char darkFontData[DARK_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0xdb, 0x72, 0xa4, 0x3a, 0x12, 0x05, 0x50, 0xfd, 0xff, 0x4f, 0xe7, 0x3c, 0x4c, 0x4c, 0x4c, 0x74, 0x9c, 0xd3, 0x20,
-    0xa5, 0x52, 0x17, 0xec, 0xd5, 0xeb, 0xcd, 0xe5, 0x76, 0x51, 0xa0, 0x94, 0x84, 0x28, 0x36, 0xd1, 0x00, 0x00, 0x00, 0x80,
-    0x5f, 0x2f, 0xfe, 0xf5, 0x27, 0xf1, 0xd7, 0xdf, 0x8c, 0xee, 0xbf, 0xf3, 0xfc, 0xf3, 0xff, 0xbd, 0x1a, 0x0f, 0xef, 0xd5,
-    0xb7, 0xad, 0xa3, 0xef, 0x1b, 0x03, 0x7b, 0xe2, 0xdf, 0xb7, 0x2f, 0xba, 0xff, 0xee, 0xdf, 0x3e, 0xdf, 0xf8, 0xef, 0x3f,
-    0xfd, 0xa5, 0xe7, 0xad, 0x8f, 0xa1, 0xfd, 0x3e, 0xfe, 0x7f, 0xc6, 0x8e, 0x62, 0x2c, 0xd9, 0xf7, 0xef, 0x5b, 0x37, 0xbe,
-    0xed, 0x95, 0xff, 0x27, 0x1e, 0x3e, 0x4f, 0xe6, 0x28, 0xf5, 0xd4, 0xd6, 0x0d, 0xf5, 0x1f, 0x5d, 0x95, 0x18, 0xa5, 0x2d,
-    0xe7, 0xff, 0xef, 0x1a, 0x85, 0x3d, 0x55, 0x76, 0x4f, 0x3e, 0x55, 0xfa, 0xf8, 0xfe, 0x78, 0x6a, 0xfd, 0xe3, 0x3d, 0x60,
-    0x94, 0x7c, 0x96, 0xf9, 0xd6, 0x18, 0xd3, 0xbf, 0xdd, 0xd7, 0x92, 0x6a, 0x3e, 0xd5, 0xcc, 0x2b, 0xad, 0xe4, 0x28, 0x9d,
-    0xa8, 0xff, 0x48, 0xf7, 0x54, 0x31, 0x58, 0xd1, 0x15, 0x7b, 0x30, 0xd3, 0x0f, 0xb7, 0x8d, 0xf5, 0x1f, 0xa5, 0x73, 0x97,
-    0x28, 0x6b, 0x73, 0x27, 0xea, 0xff, 0x79, 0xd4, 0x6e, 0x25, 0xed, 0x26, 0x5e, 0xf6, 0x51, 0x6d, 0x2d, 0xaf, 0x9d, 0xa7,
-    0xae, 0xae, 0xff, 0xe7, 0xdf, 0x1c, 0x1d, 0x6f, 0x63, 0x70, 0xec, 0xae, 0xd8, 0x4f, 0xb9, 0xf1, 0xbf, 0x7e, 0x3f, 0xc6,
-    0x5f, 0xc7, 0xe6, 0xec, 0x3c, 0x64, 0x64, 0x0f, 0xc7, 0xe0, 0xd9, 0xd5, 0xec, 0x08, 0x7b, 0x72, 0xfc, 0xef, 0x9b, 0x0b,
-    0xaa, 0xff, 0xb5, 0xf5, 0x9f, 0xf9, 0x24, 0xd1, 0xb9, 0x05, 0x99, 0x31, 0xb1, 0x5d, 0x50, 0xff, 0xa3, 0xfd, 0xd0, 0xf3,
-    0xbe, 0xaa, 0x9a, 0xcb, 0x8f, 0xae, 0x4b, 0xf4, 0x9d, 0x61, 0xef, 0x6b, 0x85, 0xe3, 0x73, 0xa3, 0xf1, 0xfa, 0x1f, 0xef,
-    0x3d, 0xdf, 0xfe, 0xda, 0xe8, 0x5e, 0xfd, 0xfb, 0x99, 0xdd, 0xfc, 0x1e, 0xdf, 0x57, 0xff, 0x91, 0x18, 0x53, 0xce, 0xd7,
-    0x73, 0x4d, 0xfd, 0x47, 0x62, 0xe5, 0xa2, 0x95, 0xad, 0xf2, 0xe5, 0xd6, 0x25, 0xea, 0xd6, 0x0c, 0xce, 0x1f, 0x81, 0xf7,
-    0xfa, 0x7f, 0x3b, 0x1f, 0x3c, 0x3f, 0xfe, 0xc7, 0x15, 0xe3, 0x7f, 0xa4, 0xd7, 0x86, 0x5b, 0xa2, 0xf7, 0xfd, 0x5a, 0xfd,
-    0x47, 0xa2, 0x66, 0xa2, 0xe4, 0xbc, 0xbc, 0x2d, 0x59, 0xcb, 0xab, 0x5d, 0x33, 0xdc, 0x73, 0xb4, 0xde, 0x46, 0xd1, 0xaa,
-    0xb3, 0x9a, 0x9f, 0x55, 0xff, 0xad, 0x60, 0xfc, 0xef, 0x9b, 0x03, 0xcc, 0x7e, 0x8e, 0xf8, 0xd8, 0xe8, 0x5f, 0x59, 0xff,
-    0xbb, 0x56, 0xec, 0xd7, 0xb7, 0xc6, 0xb8, 0xe2, 0xc8, 0x58, 0xff, 0xaf, 0x9c, 0xff, 0xe7, 0xd7, 0x00, 0xc6, 0xce, 0x63,
-    0xe2, 0xd2, 0xea, 0xcf, 0x9c, 0x33, 0x57, 0x5d, 0xb1, 0xaf, 0xfd, 0x36, 0x41, 0x24, 0xbe, 0xab, 0x51, 0xb3, 0x8a, 0xb3,
-    0xfa, 0x18, 0x8c, 0x8f, 0xff, 0xbb, 0xae, 0xff, 0xbf, 0xcf, 0xb9, 0xef, 0x18, 0xff, 0xdb, 0xeb, 0xe8, 0x7e, 0xcb, 0xf6,
-    0x00, 0x7b, 0x7b, 0x80, 0x50, 0xfd, 0xe0, 0x7b, 0xc4, 0x80, 0xfa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x0a, 0xbf, 0x9f, 0x5f, 0x9f, 0x63, 0x30, 0x9e, 0x84, 0xbd, 0x3e, 0x81, 0x7d, 0x26, 0x13, 0x38, 0xbb,
-    0x87, 0xaa, 0xfe, 0xe2, 0x73, 0x76, 0xfc, 0x48, 0xd2, 0x42, 0x7f, 0xcb, 0x88, 0xe1, 0xa7, 0x1f, 0x54, 0x26, 0x0a, 0x64,
-    0x32, 0x0d, 0xda, 0x60, 0x9b, 0xa8, 0xcd, 0x52, 0x1a, 0xdb, 0xfa, 0x3d, 0x2d, 0xfe, 0x0b, 0xf5, 0x7f, 0xfe, 0x95, 0x96,
-    0xc8, 0xef, 0xd9, 0x59, 0xff, 0xa3, 0x6d, 0x2f, 0x97, 0x8f, 0x3b, 0x37, 0x96, 0xec, 0x4e, 0x14, 0x8b, 0xc2, 0x63, 0xb8,
-    0x6f, 0x5c, 0xfd, 0x33, 0x43, 0x67, 0x47, 0xbb, 0x9e, 0xa9, 0xff, 0xd1, 0xec, 0xd3, 0x78, 0xe8, 0x85, 0xef, 0xa9, 0xff,
-    0xb6, 0xa5, 0xfe, 0xe3, 0x23, 0xf5, 0x5f, 0x9d, 0x24, 0x53, 0x95, 0x42, 0x9e, 0x49, 0xbe, 0x19, 0x3f, 0x86, 0xf9, 0x84,
-    0xdf, 0x6c, 0x8b, 0xfb, 0x4e, 0xfd, 0x67, 0xda, 0xd6, 0x73, 0xce, 0xaa, 0xfa, 0x7f, 0xdf, 0xb7, 0x99, 0xd1, 0x64, 0xfe,
-    0xe7, 0x35, 0x95, 0xbf, 0x27, 0x47, 0xf7, 0xed, 0x39, 0x5a, 0x6b, 0xdb, 0xca, 0xec, 0x58, 0xf9, 0x95, 0xfa, 0x8f, 0xe4,
-    0x5f, 0x89, 0x97, 0x1e, 0xa5, 0x36, 0x81, 0xbd, 0x26, 0xb5, 0x6d, 0x6f, 0xfd, 0x57, 0x3f, 0xf7, 0xe7, 0x8e, 0xfa, 0x3f,
-    0x9f, 0xa3, 0xbb, 0xea, 0x1c, 0xae, 0x76, 0xac, 0xfc, 0xd9, 0xe3, 0xff, 0x7b, 0x2f, 0xb9, 0xa3, 0x7d, 0xcc, 0x8c, 0xf4,
-    0x27, 0xc7, 0xff, 0x73, 0x3f, 0x3f, 0x5b, 0xff, 0x55, 0xad, 0x3d, 0x36, 0xce, 0x4c, 0xb2, 0x63, 0xe5, 0xce, 0x55, 0xb2,
-    0xdd, 0xe7, 0xff, 0xab, 0x3e, 0x59, 0x1b, 0x9e, 0x4d, 0xc4, 0xb5, 0xf5, 0x3f, 0xb3, 0x0a, 0xb9, 0x6e, 0xbe, 0x70, 0x66,
-    0xfe, 0x9f, 0x7b, 0xbe, 0x6d, 0xe6, 0xa9, 0x80, 0xb7, 0x8d, 0xff, 0xbb, 0xae, 0x92, 0xb5, 0xcd, 0x35, 0xb9, 0xf7, 0xbd,
-    0x2a, 0x9f, 0x52, 0x37, 0x9e, 0xdf, 0x1f, 0xc5, 0x33, 0xbc, 0xaf, 0xd7, 0x7f, 0x7e, 0x95, 0xbf, 0x15, 0xad, 0x4a, 0x9e,
-    0x9b, 0xff, 0xef, 0x7e, 0xd2, 0x49, 0xe6, 0x5d, 0x6a, 0xfa, 0xdf, 0xbe, 0xab, 0xc2, 0xb5, 0xa3, 0xc0, 0xdf, 0xaf, 0x36,
-    0x44, 0xd1, 0xc8, 0x51, 0x95, 0xdf, 0xff, 0xe7, 0xb6, 0x8d, 0x3f, 0xf1, 0x6c, 0xfc, 0x4a, 0x7c, 0x0c, 0x3e, 0x4f, 0xff,
-    0x44, 0xfd, 0x67, 0xde, 0x39, 0xf3, 0xbf, 0x46, 0x8f, 0x61, 0x65, 0xfd, 0x9f, 0xeb, 0x01, 0xe4, 0x6e, 0xc2, 0x8d, 0xb3,
-    0x18, 0xe0, 0xe7, 0xf4, 0x00, 0x9e, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xec, 0xbe, 0x13, 0xfb, 0xef, 0xd9, 0x5c, 0x99, 0xd4, 0xf8, 0xf1, 0xfb, 0xa4, 0x77, 0x26, 0xe6, 0xb7, 0x97, 0x14, 0xb2,
-    0xe7, 0xdf, 0xfa, 0xf3, 0x95, 0x8a, 0x3c, 0xde, 0x28, 0xbc, 0x6b, 0xfd, 0x29, 0x93, 0xe4, 0x39, 0x4b, 0x71, 0x34, 0x77,
-    0xe0, 0x7d, 0x0f, 0x8f, 0x1f, 0x97, 0x99, 0xd7, 0x5a, 0x61, 0x9a, 0x42, 0x24, 0xd2, 0x5e, 0xde, 0xf6, 0x7a, 0x7f, 0x36,
-    0x70, 0x94, 0x6d, 0xf1, 0x58, 0x1d, 0x55, 0x65, 0x27, 0xdf, 0x9e, 0x98, 0x3f, 0x9f, 0xbc, 0x13, 0x2f, 0xad, 0xae, 0xbf,
-    0xfe, 0x7b, 0x92, 0x23, 0xd6, 0xe7, 0x18, 0xf5, 0xfc, 0x34, 0x93, 0x7a, 0x31, 0x96, 0xc1, 0x36, 0xd7, 0x06, 0x56, 0x8f,
-    0x50, 0x6d, 0xb2, 0x42, 0x62, 0xaa, 0xa5, 0xcc, 0x67, 0xff, 0xbd, 0x6f, 0x69, 0x1b, 0x1c, 0xb9, 0x6e, 0x48, 0xcc, 0x8c,
-    0xce, 0xfa, 0xaa, 0xfe, 0x69, 0x1b, 0xec, 0x6f, 0x32, 0x79, 0x75, 0x51, 0x9a, 0x44, 0x91, 0xff, 0x3f, 0x91, 0x48, 0x6d,
-    0x6e, 0xc9, 0x2c, 0xbe, 0x7c, 0x46, 0x7c, 0x26, 0xa7, 0x2a, 0x16, 0xef, 0xd9, 0x4c, 0xfd, 0xb7, 0x43, 0xf5, 0x9f, 0x49,
-    0x3d, 0x38, 0x9f, 0x98, 0x1d, 0xa9, 0xda, 0xdd, 0x5b, 0xff, 0xd1, 0xd1, 0x7f, 0x65, 0x66, 0xd7, 0x51, 0x78, 0xce, 0xf0,
-    0x3e, 0xe3, 0xad, 0x1e, 0xff, 0xdb, 0xe3, 0x7e, 0x1a, 0x4f, 0x75, 0x5b, 0x3f, 0x42, 0x65, 0x7a, 0x93, 0xb1, 0xfa, 0x5f,
-    0xdd, 0x63, 0xcd, 0x3c, 0x07, 0x29, 0x3e, 0x37, 0xfe, 0x9f, 0x49, 0xcc, 0x8e, 0xc4, 0xd9, 0x75, 0xe6, 0x7d, 0xeb, 0xd7,
-    0x5e, 0xa2, 0xb0, 0xfe, 0x33, 0x3d, 0x40, 0xbe, 0xfe, 0x77, 0xce, 0xa6, 0xb3, 0x79, 0x93, 0x31, 0xdd, 0x86, 0xce, 0xd4,
-    0x7f, 0xe5, 0xd3, 0x96, 0x56, 0x24, 0xe6, 0x8e, 0x3f, 0xd9, 0xf1, 0xd4, 0xf8, 0xdf, 0x52, 0xe3, 0x7f, 0x6e, 0x9c, 0xa8,
-    0x3b, 0xf6, 0xb5, 0x73, 0xe5, 0xf7, 0xfa, 0x8f, 0x0d, 0xef, 0xb6, 0xaf, 0xfe, 0x33, 0x3d, 0x61, 0x2c, 0x9b, 0xff, 0x67,
-    0xab, 0x25, 0x86, 0xe7, 0xd7, 0x3b, 0x13, 0x73, 0x33, 0xeb, 0xcc, 0xf7, 0xd7, 0x7f, 0xe5, 0x33, 0x4c, 0xda, 0x54, 0xfa,
-    0x72, 0x94, 0x8e, 0xc8, 0xfb, 0xc6, 0xff, 0x15, 0xb3, 0xe9, 0x4c, 0xfa, 0xfe, 0x3d, 0xeb, 0x7f, 0xad, 0xf8, 0x88, 0xb5,
-    0xe3, 0x79, 0x88, 0x2b, 0x66, 0x85, 0x27, 0xce, 0xff, 0xab, 0x12, 0xd8, 0x73, 0xe7, 0xff, 0xd5, 0x2b, 0xf2, 0x73, 0xc7,
-    0xf2, 0xde, 0xf1, 0x3f, 0xd7, 0x42, 0x62, 0x68, 0x7c, 0x6e, 0x9f, 0xaa, 0xff, 0x5b, 0xbe, 0xd1, 0x50, 0xd1, 0x6f, 0x45,
-    0x49, 0xef, 0x96, 0x79, 0x6e, 0x4b, 0xe5, 0x3c, 0xaa, 0x72, 0xfd, 0x6a, 0xe6, 0xfa, 0xff, 0xcc, 0xb7, 0x53, 0x56, 0xbf,
-    0x5b, 0xfe, 0xfa, 0xff, 0xda, 0xfa, 0x8f, 0xe2, 0x2d, 0xe6, 0x9e, 0x5e, 0xea, 0xe6, 0xf7, 0xd3, 0x7e, 0xee, 0x38, 0x82,
-    0x6a, 0x59, 0xeb, 0x39, 0x71, 0x3e, 0xc4, 0xef, 0x9a, 0x33, 0xf3, 0xd3, 0xbf, 0xe1, 0x6d, 0x3f, 0x18, 0x9b, 0x01, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xef, 0xde, 0xc3, 0x14, 0xaf, 0x77, 0x38, 0xbc, 0x65, 0x4b,
-    0x64, 0x72, 0x29, 0x5b, 0x69, 0xce, 0x5d, 0x4b, 0x24, 0xf3, 0x67, 0xb7, 0xae, 0x77, 0xaf, 0xac, 0xff, 0x64, 0xb3, 0x69,
-    0xe6, 0x99, 0xc4, 0xf9, 0xcc, 0x3e, 0x3e, 0x7b, 0x34, 0xdf, 0x32, 0x3c, 0x22, 0x91, 0xc6, 0x5a, 0x79, 0xbc, 0x5a, 0x6a,
-    0xdf, 0x65, 0x5b, 0x59, 0x6f, 0x0f, 0xd0, 0x9f, 0x0f, 0x53, 0x7f, 0x8f, 0x69, 0xa4, 0xf6, 0x79, 0x4d, 0x32, 0xff, 0x4c,
-    0x5b, 0x6a, 0xe9, 0x04, 0xe2, 0x6c, 0xab, 0xe9, 0xfd, 0x5b, 0x31, 0xfd, 0x97, 0xdf, 0xb3, 0x5a, 0xe3, 0x73, 0x47, 0x33,
-    0x9b, 0x17, 0x5f, 0x7d, 0xbc, 0x6a, 0xd3, 0xdb, 0x6a, 0x7a, 0x80, 0x8a, 0xfa, 0xaf, 0xdb, 0xca, 0xfd, 0xf7, 0xc6, 0x46,
-    0xf2, 0x7e, 0xfa, 0xf8, 0xfc, 0xfd, 0xa7, 0xf1, 0xd1, 0x24, 0x81, 0x5c, 0xca, 0xe4, 0xae, 0x4f, 0x95, 0xcb, 0xe5, 0xca,
-    0xa7, 0xb7, 0xed, 0x1c, 0xff, 0x33, 0x49, 0x52, 0x99, 0xb4, 0xd0, 0xea, 0x57, 0xb2, 0xbd, 0x65, 0xae, 0xfe, 0x33, 0x99,
-    0x4d, 0x51, 0x94, 0x67, 0x5c, 0xf7, 0xdc, 0x93, 0x48, 0xce, 0xaf, 0x4f, 0x1e, 0xcd, 0x78, 0x4d, 0x25, 0xaf, 0xca, 0xd8,
-    0xdd, 0xfb, 0x4a, 0x4c, 0x26, 0x3e, 0x44, 0xd7, 0xf1, 0x8e, 0xe9, 0xb1, 0x70, 0xd7, 0xd3, 0x02, 0x33, 0xcf, 0xa4, 0x68,
-    0xc9, 0xd4, 0xcc, 0x28, 0x9b, 0xe1, 0x57, 0xbf, 0xb2, 0xa2, 0xfe, 0xe3, 0xe1, 0x3c, 0xb9, 0xfa, 0x99, 0x65, 0xd5, 0x47,
-    0xb3, 0xa7, 0x67, 0xf8, 0x62, 0xfd, 0xaf, 0x9d, 0xa7, 0xd5, 0x9c, 0xff, 0x47, 0xfa, 0x59, 0x3c, 0x77, 0xb6, 0x98, 0xe8,
-    0x4c, 0x77, 0xfe, 0x69, 0xf5, 0xbf, 0x77, 0x1f, 0x57, 0xf7, 0xe6, 0xcf, 0x33, 0xec, 0xf6, 0xd1, 0xfa, 0x9f, 0x49, 0x8b,
-    0xaf, 0x39, 0x5f, 0x79, 0x5f, 0xff, 0xaf, 0x5c, 0xd9, 0xb8, 0xa3, 0xfe, 0x6f, 0xa9, 0xf2, 0xb1, 0x7d, 0xbe, 0xb6, 0xfe,
-    0xcf, 0xd7, 0xd0, 0x6c, 0xf2, 0xfb, 0xf9, 0x6d, 0x8f, 0xb2, 0xf3, 0xff, 0x9a, 0x75, 0xa1, 0xea, 0x79, 0xc4, 0x6d, 0x3d,
-    0xe8, 0xaa, 0xa7, 0xab, 0xbd, 0xcd, 0x1d, 0xaa, 0xcf, 0x77, 0xef, 0x18, 0xff, 0x43, 0xfd, 0x2f, 0x58, 0x0b, 0xaf, 0x1d,
-    0x25, 0xf3, 0x2b, 0xcc, 0x33, 0xfd, 0xc9, 0xf8, 0xb3, 0xec, 0x6b, 0x57, 0xc9, 0x32, 0x7d, 0x68, 0xcf, 0x2b, 0xf9, 0x27,
-    0x6e, 0x7c, 0x75, 0xfe, 0xdf, 0xf3, 0x54, 0xa0, 0x6f, 0xd5, 0x7f, 0x24, 0xd7, 0xb7, 0xee, 0xa8, 0xff, 0xb5, 0x57, 0x06,
-    0xd6, 0x5f, 0xcb, 0xbf, 0x79, 0xfe, 0xbf, 0xa2, 0xcf, 0xfb, 0x76, 0xfd, 0x7f, 0x61, 0x0e, 0xbd, 0xe6, 0x5b, 0x28, 0xe7,
-    0xea, 0x7f, 0x6e, 0xdc, 0x56, 0xff, 0x2b, 0x56, 0x8c, 0xf3, 0x9f, 0x7a, 0xc5, 0xf9, 0x7f, 0xef, 0x4c, 0xe3, 0xe7, 0xd7,
-    0x7f, 0x7d, 0xd2, 0xf9, 0x9a, 0xf3, 0xcd, 0x76, 0xe9, 0x77, 0x61, 0xee, 0xfa, 0x4e, 0xf2, 0x9e, 0x16, 0xb3, 0x66, 0xed,
-    0x23, 0xf3, 0x94, 0xde, 0xda, 0xa7, 0x39, 0xc5, 0xa2, 0x4a, 0xcf, 0x5d, 0xe5, 0xdd, 0x73, 0xfd, 0x7f, 0xef, 0x37, 0x8a,
-    0xaa, 0xb7, 0x50, 0xfd, 0x7f, 0xa3, 0x0f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xdb,
-    0xee, 0xd0, 0xdb, 0x9b, 0xa8, 0x5f, 0x9f, 0x82, 0x71, 0x7b, 0xa2, 0xfe, 0xdb, 0x5d, 0x33, 0xed, 0xda, 0x44, 0xfd, 0xd6,
-    0x95, 0x4c, 0xf4, 0xbd, 0x44, 0xfd, 0xdf, 0x78, 0xc7, 0xc7, 0x6d, 0x89, 0xfa, 0x75, 0x77, 0x4e, 0xdf, 0x9e, 0xa8, 0xdf,
-    0x5e, 0xf2, 0x87, 0x6e, 0x4e, 0xd4, 0xcf, 0xdf, 0x3b, 0x75, 0x6b, 0xa2, 0x3e, 0x6b, 0xef, 0x98, 0xec, 0xed, 0xfb, 0xe3,
-    0xe0, 0x31, 0x5b, 0x75, 0x77, 0x69, 0x7c, 0xf4, 0xce, 0xcf, 0xf1, 0x8c, 0xa6, 0x15, 0x3d, 0x4a, 0xe5, 0x5d, 0x9a, 0xeb,
-    0x13, 0xf5, 0x7f, 0x4a, 0xbd, 0xee, 0x4a, 0xd4, 0x7f, 0xcf, 0x19, 0x8b, 0x4d, 0xdb, 0x1f, 0x43, 0xf3, 0xc6, 0xb5, 0x89,
-    0x5a, 0xcf, 0xfd, 0xe8, 0xbd, 0xcf, 0x47, 0xf8, 0x76, 0xa2, 0xf6, 0x6f, 0xab, 0xff, 0x55, 0xfb, 0x29, 0x8a, 0xaa, 0xae,
-    0xf7, 0x95, 0x7d, 0x59, 0x41, 0x75, 0xf5, 0x1f, 0xe5, 0xaf, 0xed, 0x4d, 0xd4, 0xca, 0xae, 0x27, 0xa8, 0xff, 0xef, 0xac,
-    0x01, 0xe4, 0xd6, 0xad, 0xe6, 0x12, 0x75, 0x4f, 0xb6, 0x80, 0x9d, 0x89, 0x5a, 0x5f, 0xaf, 0xff, 0xcc, 0xac, 0x5c, 0xfd,
-    0xdf, 0x39, 0xff, 0x8f, 0xb2, 0x57, 0x32, 0xbd, 0x46, 0x7e, 0xfd, 0xff, 0xcb, 0x4f, 0xd4, 0xfa, 0x99, 0xf5, 0x7f, 0x47,
-    0xa2, 0xae, 0xfa, 0x3f, 0x33, 0xff, 0x5f, 0xf7, 0x54, 0x82, 0x9d, 0x89, 0xba, 0xc6, 0xff, 0x55, 0x15, 0x71, 0x77, 0x95,
-    0xdf, 0x95, 0x9b, 0xfb, 0xcd, 0xf3, 0xff, 0xdc, 0x93, 0x40, 0xb3, 0x3d, 0xc0, 0xbe, 0x84, 0xd6, 0x13, 0x89, 0xfa, 0x12,
-    0x75, 0x67, 0x8e, 0xd7, 0xf3, 0xec, 0xef, 0x9b, 0xd7, 0x63, 0x6e, 0x9f, 0xff, 0xd7, 0x5e, 0xab, 0xf9, 0xe7, 0x9a, 0xd2,
-    0xea, 0x15, 0xef, 0x55, 0xf3, 0xff, 0xda, 0x2b, 0x5b, 0x12, 0x75, 0xab, 0x8f, 0xda, 0x6f, 0x5f, 0xff, 0x3b, 0xdb, 0xd3,
-    0xc0, 0xee, 0x79, 0xed, 0xf9, 0x9e, 0x10, 0xf8, 0xda, 0x37, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xdd, 0xdb, 0x92, 0xa5, 0xba, 0x0d, 0x00, 0x50, 0xff, 0xff, 0x4f, 0x2b, 0x0f, 0xa9, 0x54, 0x32, 0x95, 0xd3, 0x80, 0x64,
+    0xd9, 0x98, 0x9e, 0x35, 0xeb, 0xad, 0x77, 0x4f, 0xc3, 0x36, 0x96, 0x6f, 0x80, 0x1c, 0x03, 0x00, 0x00, 0x00, 0xf8, 0xeb,
+    0xc5, 0x3f, 0xfe, 0x24, 0x7e, 0xfc, 0xcd, 0x78, 0xfc, 0x77, 0xae, 0x7f, 0xfe, 0x9f, 0x4f, 0xe3, 0xe2, 0x58, 0xcf, 0xce,
+    0x35, 0x7b, 0xdc, 0x48, 0x94, 0xc4, 0x3f, 0x9f, 0x5f, 0x3c, 0xfe, 0xbb, 0x3f, 0x7d, 0xbf, 0xfc, 0xef, 0x5f, 0xfd, 0xa5,
+    0xeb, 0xb3, 0x8f, 0x54, 0xb9, 0xe7, 0xff, 0x4f, 0xee, 0x2a, 0xc6, 0x92, 0xb2, 0xbf, 0x3f, 0xbb, 0xfc, 0xb9, 0x77, 0xfe,
+    0x9f, 0xb8, 0xf8, 0x3e, 0x95, 0xab, 0xf4, 0x24, 0xb6, 0x4e, 0x88, 0xff, 0x78, 0x14, 0x89, 0xd1, 0x5a, 0x73, 0xfe, 0x7b,
+    0xd4, 0x68, 0x6c, 0xa9, 0xaa, 0x25, 0x79, 0x15, 0xe9, 0xf9, 0xf2, 0xb8, 0xaa, 0xfd, 0xf9, 0x16, 0x30, 0x5a, 0xbe, 0xcb,
+    0x7c, 0x6d, 0x8c, 0xe9, 0xdf, 0x7e, 0x56, 0x93, 0x7a, 0xbe, 0xd5, 0xcc, 0x27, 0xa3, 0xe5, 0x2a, 0xbd, 0x11, 0xff, 0x51,
+    0x6e, 0xa9, 0x22, 0x19, 0xd1, 0x1d, 0x25, 0x58, 0x69, 0x87, 0xc7, 0xc6, 0xf8, 0x8f, 0xd6, 0xb1, 0x4b, 0xb4, 0xd5, 0xb9,
+    0x37, 0xe2, 0xff, 0xba, 0xd7, 0x1e, 0x2d, 0xf5, 0x26, 0x6e, 0xca, 0xa8, 0x37, 0x96, 0xd7, 0x8e, 0x53, 0x57, 0xc7, 0xff,
+    0xf5, 0x6f, 0x66, 0xfb, 0xdb, 0x48, 0xf6, 0xdd, 0x1d, 0xe5, 0x54, 0xeb, 0xff, 0xfb, 0xcb, 0x31, 0x7e, 0xec, 0x9b, 0xab,
+    0xe3, 0x90, 0x4c, 0x09, 0x47, 0x72, 0x76, 0x35, 0xdb, 0xc3, 0xbe, 0xd9, 0xff, 0x3f, 0x1b, 0x0b, 0x8a, 0xff, 0xb5, 0xf1,
+    0x5f, 0xf9, 0x26, 0xf1, 0xf0, 0x0c, 0x2a, 0x7d, 0xe2, 0x38, 0x20, 0xfe, 0xb3, 0xed, 0xd0, 0x75, 0x59, 0x75, 0x8d, 0xe5,
+    0xb3, 0xeb, 0x12, 0xcf, 0x66, 0xd8, 0xfb, 0x6a, 0x61, 0x7e, 0x6c, 0x94, 0x8f, 0xff, 0x7c, 0xeb, 0x79, 0xf7, 0xd7, 0xb2,
+    0xa5, 0xfa, 0xf3, 0xcc, 0x6e, 0xbe, 0xc4, 0xf7, 0xc5, 0x7f, 0x14, 0xfa, 0x94, 0xf7, 0xe3, 0xb9, 0x27, 0xfe, 0xa3, 0xb0,
+    0x72, 0x31, 0xda, 0x56, 0xf9, 0x6a, 0xeb, 0x12, 0x7d, 0x6b, 0x06, 0xef, 0x5f, 0x81, 0xfb, 0xf8, 0xbf, 0x9b, 0x0f, 0xbe,
+    0xdf, 0xff, 0xc7, 0x11, 0xfd, 0x7f, 0x94, 0xd7, 0x86, 0x47, 0xa1, 0xf5, 0xfd, 0x5a, 0xfc, 0x47, 0x21, 0x66, 0xa2, 0x65,
+    0x5e, 0x3e, 0x96, 0xac, 0xe5, 0xf5, 0xae, 0x19, 0xee, 0xb9, 0x5a, 0x77, 0xbd, 0x68, 0xd7, 0xac, 0xe6, 0x77, 0xc5, 0xff,
+    0x68, 0xe8, 0xff, 0x9f, 0x8d, 0x01, 0x66, 0xbf, 0x47, 0x7c, 0xac, 0xf7, 0xef, 0x8c, 0xff, 0x5d, 0x2b, 0xf6, 0xeb, 0x6b,
+    0x63, 0x1c, 0x71, 0x65, 0xac, 0xff, 0x77, 0x8e, 0xff, 0xeb, 0x6b, 0x00, 0xb9, 0x79, 0x4c, 0x1c, 0x1a, 0xfd, 0x95, 0x39,
+    0x73, 0xd7, 0x1d, 0xfb, 0xde, 0xa7, 0x09, 0xa2, 0xf0, 0xac, 0x46, 0xcf, 0x2a, 0xce, 0xea, 0x6b, 0x90, 0xef, 0xff, 0x77,
+    0xdd, 0xff, 0xbf, 0x1f, 0x73, 0x9f, 0xd1, 0xff, 0x8f, 0xdb, 0xde, 0xfd, 0x94, 0xf3, 0x01, 0xf6, 0xb6, 0x00, 0x21, 0xfa,
+    0xc1, 0x73, 0xc4, 0x80, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9f, 0xcf,
+    0xef, 0xcf, 0x63, 0x90, 0xcf, 0x84, 0xbd, 0x3e, 0x03, 0xfb, 0x4c, 0x4e, 0xe0, 0x6a, 0x09, 0x75, 0xfd, 0xc5, 0xeb, 0xdc,
+    0xf1, 0x99, 0x4c, 0x0b, 0xcf, 0x6b, 0x46, 0xa4, 0x77, 0x3f, 0xe8, 0xcc, 0x28, 0x50, 0xc9, 0x69, 0x30, 0x92, 0x75, 0xa2,
+    0x37, 0x97, 0x52, 0xee, 0xec, 0xf7, 0xd4, 0xf8, 0x2f, 0xc4, 0xff, 0xfb, 0x9f, 0x8c, 0x42, 0xfe, 0x9e, 0x9d, 0xf1, 0x9f,
+    0xad, 0x7b, 0xb5, 0xfc, 0xb8, 0x73, 0x7d, 0xc9, 0xee, 0x8c, 0x62, 0xd1, 0x78, 0x0d, 0xf7, 0xf5, 0xab, 0x7f, 0xe6, 0xd0,
+    0xd9, 0x51, 0xaf, 0x67, 0xe2, 0x3f, 0x9b, 0xfb, 0x34, 0x2e, 0x5a, 0xe1, 0x73, 0xe2, 0x7f, 0x6c, 0x89, 0xff, 0xf8, 0x48,
+    0xfc, 0x77, 0x67, 0x92, 0xe9, 0xca, 0x42, 0x5e, 0xc9, 0x7c, 0x93, 0xbf, 0x86, 0xf5, 0x0c, 0xbf, 0xd5, 0x1a, 0xf7, 0x9d,
+    0xf8, 0xaf, 0xd4, 0xad, 0xeb, 0x3c, 0xab, 0xe2, 0xff, 0xbe, 0x6c, 0x2b, 0xbd, 0xc9, 0xfc, 0xcf, 0x7b, 0x22, 0x7f, 0x4f,
+    0x1e, 0xdd, 0xbb, 0x7d, 0xb4, 0xd6, 0xd6, 0x95, 0xd9, 0xbe, 0xf2, 0x2b, 0xf1, 0x1f, 0xc5, 0xbf, 0x12, 0x37, 0x2d, 0x4a,
+    0x6f, 0x06, 0xf6, 0x9e, 0xac, 0x6d, 0x7b, 0xe3, 0xbf, 0x7b, 0xdf, 0x9f, 0x33, 0xe2, 0xff, 0xfd, 0x3c, 0xba, 0xab, 0xe6,
+    0x70, 0xbd, 0x7d, 0xe5, 0xef, 0xee, 0xff, 0xef, 0x5b, 0xc9, 0x1d, 0xf5, 0x63, 0xa6, 0xa7, 0x7f, 0xb3, 0xff, 0x7f, 0xef,
+    0xe7, 0xef, 0xc6, 0x7f, 0x57, 0x6d, 0x8f, 0x8d, 0x23, 0x93, 0x6a, 0x5f, 0xb9, 0x73, 0x95, 0x6c, 0xf7, 0xfc, 0x7f, 0xd5,
+    0x37, 0x1b, 0xe9, 0xd1, 0x44, 0x1c, 0x1b, 0xff, 0x33, 0xab, 0x90, 0xeb, 0xc6, 0x0b, 0xef, 0x8c, 0xff, 0x6b, 0xfb, 0xdb,
+    0x56, 0x76, 0x05, 0x3c, 0xad, 0xff, 0xdf, 0x75, 0x97, 0x6c, 0x6c, 0x8e, 0xc9, 0xbd, 0xc7, 0xea, 0xdc, 0xa5, 0x2e, 0x9f,
+    0xbf, 0x3f, 0x9a, 0x47, 0x78, 0x5f, 0x8f, 0xff, 0xfa, 0x2a, 0xff, 0x68, 0x5a, 0x95, 0x7c, 0x6f, 0xfc, 0xbf, 0x7b, 0xa7,
+    0x93, 0xca, 0x51, 0x7a, 0xda, 0xdf, 0x67, 0x77, 0x85, 0x7b, 0x7b, 0x81, 0x9f, 0xef, 0x36, 0x44, 0x53, 0xcf, 0xd1, 0x95,
+    0xbf, 0xff, 0xcf, 0x73, 0xcb, 0xef, 0x78, 0x96, 0xbf, 0x13, 0x1f, 0xc9, 0xfd, 0xf4, 0xdf, 0x88, 0xff, 0xca, 0x91, 0x2b,
+    0xff, 0x2b, 0x7b, 0x0d, 0x3b, 0xe3, 0xff, 0xbd, 0x16, 0x40, 0xde, 0x4d, 0x38, 0x71, 0x14, 0x03, 0xfc, 0x9e, 0x16, 0xc0,
+    0x0e, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xee, 0x37, 0xb1, 0x7f, 0xce,
+    0xcd, 0x55, 0xc9, 0x1a, 0x9f, 0x7f, 0x4f, 0x7a, 0x67, 0xc6, 0xfc, 0x71, 0x93, 0x85, 0xec, 0xfa, 0xb7, 0xfe, 0xfc, 0xa4,
+    0x23, 0x1f, 0x6f, 0x34, 0xbe, 0xb5, 0x9e, 0xc9, 0xe5, 0x1d, 0x85, 0x6c, 0x03, 0x57, 0x6f, 0xc6, 0xe6, 0xae, 0x41, 0xee,
+    0x18, 0xd1, 0x9e, 0xcf, 0x2c, 0xd2, 0xd9, 0x63, 0xfe, 0xf7, 0x9c, 0x9f, 0x67, 0xfa, 0x8d, 0xf2, 0x9b, 0xc6, 0x77, 0xb9,
+    0xcc, 0xf2, 0xf9, 0x11, 0x2a, 0x39, 0xf3, 0xf2, 0x79, 0x36, 0x4f, 0xcf, 0x98, 0x3f, 0x9f, 0x79, 0x27, 0x6e, 0xea, 0xe4,
+    0xf3, 0x5a, 0xfa, 0x24, 0x73, 0x44, 0x4f, 0x06, 0xa1, 0xda, 0x37, 0x8e, 0xe9, 0xda, 0x5a, 0xb9, 0xde, 0x99, 0x23, 0xc4,
+    0xf4, 0xfb, 0xfc, 0x95, 0xfa, 0x1e, 0x93, 0xd7, 0xbd, 0xfe, 0x8d, 0x67, 0xb2, 0x10, 0x44, 0xa9, 0xa7, 0xeb, 0x2c, 0xc1,
+    0xeb, 0x4f, 0xf2, 0x19, 0x33, 0xe3, 0x61, 0x7c, 0x75, 0xff, 0x74, 0x24, 0xdb, 0x9b, 0x4a, 0xbe, 0xba, 0x99, 0x5d, 0x33,
+    0xe6, 0xc6, 0x88, 0xe3, 0x22, 0xbb, 0x52, 0x3e, 0xfa, 0x7f, 0xee, 0x6f, 0x62, 0x32, 0xfe, 0xa3, 0x98, 0x59, 0xa8, 0x1e,
+    0x23, 0xd9, 0xf8, 0x9f, 0xbf, 0x0a, 0xbb, 0xe2, 0xbf, 0x92, 0xf5, 0xe0, 0xfd, 0x8c, 0xd9, 0xb5, 0x9e, 0x70, 0x6f, 0xfc,
+    0xc7, 0x83, 0xf6, 0x6b, 0xa4, 0xe7, 0x2f, 0xa3, 0x90, 0xe1, 0xfc, 0xf9, 0xec, 0x22, 0x5a, 0xfb, 0xff, 0x71, 0x59, 0x26,
+    0xf1, 0xc2, 0xf8, 0xbf, 0xb6, 0xdf, 0x5b, 0x2e, 0xfe, 0x67, 0x5a, 0xa7, 0xfd, 0xfd, 0x7f, 0xad, 0x5f, 0x3b, 0xb9, 0xff,
+    0x7f, 0x27, 0x63, 0x76, 0x14, 0xe6, 0xd9, 0x95, 0xe3, 0xc6, 0xe4, 0xc8, 0xe0, 0xd9, 0x5c, 0x30, 0x1a, 0xe6, 0xab, 0x1d,
+    0xf1, 0xdf, 0x3f, 0xc2, 0xae, 0xcc, 0xad, 0x6a, 0xfd, 0x7f, 0xb4, 0xce, 0xff, 0x23, 0xdd, 0xce, 0xcf, 0xc5, 0x7f, 0xe7,
+    0x6e, 0x4b, 0x2b, 0x32, 0xe6, 0xe6, 0x77, 0x76, 0x7c, 0xab, 0xff, 0x1f, 0xa5, 0xfe, 0xbf, 0xd6, 0x7b, 0xcd, 0xd4, 0xca,
+    0xd8, 0x90, 0xeb, 0x2e, 0x37, 0xfe, 0x5f, 0x3d, 0xca, 0xce, 0xcf, 0x28, 0x63, 0x53, 0xfb, 0xd4, 0x3d, 0xf7, 0xab, 0x45,
+    0x4b, 0xa4, 0xc7, 0xd7, 0x3b, 0x33, 0xe6, 0x56, 0xd6, 0x9e, 0xcf, 0x8f, 0xff, 0xce, 0x3d, 0x4c, 0x2a, 0x77, 0x17, 0xa2,
+    0xa1, 0x6f, 0x5e, 0xdd, 0xff, 0xcf, 0xce, 0xff, 0x2b, 0xb9, 0xf4, 0xf7, 0xac, 0xff, 0x8d, 0xa5, 0x6b, 0x3f, 0xd1, 0x7a,
+    0x3f, 0x71, 0x1c, 0xd0, 0x7e, 0x77, 0x66, 0x89, 0x7f, 0x63, 0xfe, 0xdf, 0x95, 0x81, 0x7d, 0x94, 0xef, 0x09, 0xd5, 0xd7,
+    0xff, 0x67, 0x6b, 0x60, 0x1c, 0xd3, 0xf7, 0x8f, 0xc7, 0x7b, 0xe8, 0xc4, 0x4b, 0xd1, 0x7f, 0x5a, 0xfc, 0x9f, 0xf2, 0x44,
+    0x43, 0x47, 0xbb, 0x15, 0x2d, 0xad, 0x5b, 0x65, 0xdf, 0x96, 0xce, 0x71, 0xd4, 0xfc, 0xfc, 0x7f, 0xcd, 0xba, 0xfa, 0x1b,
+    0xc7, 0x98, 0xb9, 0x23, 0x3b, 0x1f, 0xff, 0xb1, 0xbc, 0xe4, 0xe4, 0x23, 0xfe, 0x4e, 0x2b, 0x75, 0xde, 0xf1, 0xfe, 0xe6,
+    0xda, 0x13, 0x1b, 0x9f, 0xb5, 0x13, 0xa1, 0xc4, 0xd1, 0xf3, 0x21, 0xd7, 0x03, 0xfe, 0xe6, 0xf9, 0xd0, 0xdf, 0xfa, 0x1c,
+    0xbc, 0x92, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x7d, 0x3b, 0xa4, 0x92, 0x59,
+    0x72, 0x34, 0xbf, 0x8b, 0x9e, 0xcf, 0xad, 0x5f, 0x3d, 0xbb, 0xbb, 0xf7, 0xa5, 0x6b, 0xf9, 0x79, 0x6b, 0xe5, 0x34, 0x97,
+    0x8f, 0x28, 0x97, 0x53, 0xed, 0x7e, 0xff, 0x83, 0x38, 0xf2, 0x6a, 0xde, 0x65, 0x75, 0x8d, 0x42, 0x3e, 0xd5, 0xce, 0xeb,
+    0x35, 0x4a, 0x65, 0xb7, 0xab, 0x96, 0x3d, 0x7f, 0x3b, 0x34, 0x36, 0xbc, 0x4f, 0x59, 0x8b, 0x85, 0x5a, 0x6e, 0xc3, 0xb9,
+    0xdc, 0x61, 0xb1, 0xf8, 0x9b, 0xe5, 0xca, 0x6f, 0x7e, 0x3f, 0x89, 0xfb, 0x6c, 0xab, 0xf1, 0xb9, 0xab, 0x59, 0xcd, 0xf8,
+    0xde, 0x7d, 0xbd, 0xea, 0xb5, 0xe5, 0x84, 0x5a, 0xb6, 0x7e, 0x67, 0x89, 0x37, 0xde, 0x6e, 0xad, 0x66, 0xc4, 0x8d, 0xcf,
+    0xbf, 0x73, 0x1a, 0x1f, 0xcd, 0x05, 0x50, 0xcb, 0x13, 0xb9, 0xeb, 0x5b, 0x55, 0x8e, 0x14, 0xa5, 0x3d, 0x9b, 0xc6, 0xf6,
+    0xec, 0x10, 0xf9, 0xec, 0xe7, 0xa3, 0x94, 0xef, 0xb3, 0xfb, 0x93, 0xca, 0x15, 0xa9, 0xc7, 0xff, 0x55, 0x39, 0x55, 0x3e,
+    0x99, 0xed, 0xff, 0xc7, 0xf2, 0x7d, 0x18, 0x4e, 0xb9, 0x9a, 0x77, 0xfd, 0x7f, 0xb4, 0xed, 0x98, 0xb4, 0xf7, 0x93, 0x1d,
+    0xe5, 0xda, 0x11, 0x0b, 0xbb, 0xf6, 0xfb, 0x8b, 0x74, 0xad, 0x18, 0xe9, 0x1c, 0x7b, 0xf5, 0xd9, 0xd7, 0xae, 0x4f, 0x56,
+    0xc4, 0x7f, 0x5c, 0xcc, 0x93, 0xbb, 0x77, 0x1d, 0xeb, 0xbe, 0x9a, 0x4f, 0x5a, 0x86, 0x2f, 0xc6, 0xff, 0x8e, 0xf8, 0x99,
+    0x9d, 0xff, 0x47, 0x79, 0x37, 0x9d, 0x33, 0x6b, 0x4c, 0x3c, 0xcc, 0xe8, 0xfc, 0xdb, 0xe2, 0x7f, 0x6f, 0x19, 0x77, 0xb7,
+    0xe6, 0xd7, 0x23, 0xec, 0xf1, 0xd1, 0xf8, 0x9f, 0xdd, 0xef, 0x6d, 0x7e, 0xee, 0x7a, 0xbf, 0xfe, 0xdf, 0xb9, 0xb2, 0x71,
+    0x46, 0xfc, 0x9f, 0x12, 0xe5, 0xb9, 0x32, 0x5f, 0x1b, 0xff, 0xef, 0xc7, 0x50, 0x57, 0x1f, 0xf6, 0xde, 0xb9, 0x47, 0xdb,
+    0xfc, 0xff, 0x7e, 0x97, 0xe4, 0xdc, 0xdd, 0x9d, 0xbd, 0xab, 0x49, 0x3b, 0x4b, 0xbd, 0x73, 0xcf, 0xe1, 0xa7, 0x63, 0x87,
+    0xee, 0xf9, 0xee, 0x19, 0xfd, 0x7f, 0x88, 0xff, 0x05, 0x6b, 0xe1, 0x27, 0xf4, 0x92, 0xab, 0xee, 0xe5, 0xe5, 0xb3, 0xd8,
+    0xf7, 0x7e, 0x32, 0x5b, 0x52, 0xf9, 0xfb, 0xff, 0x5f, 0x1f, 0xff, 0x3f, 0xd9, 0xe1, 0xe7, 0x5b, 0xf1, 0x1f, 0xc5, 0x15,
+    0xc5, 0x33, 0xe2, 0x3f, 0x36, 0x8e, 0x6a, 0x57, 0xdc, 0xcb, 0x3f, 0x79, 0xfc, 0xdf, 0xbf, 0x13, 0xd4, 0xd7, 0xe3, 0xff,
+    0x0b, 0x63, 0xe8, 0x35, 0x4f, 0xa1, 0xbc, 0x17, 0xff, 0x73, 0x77, 0x86, 0x4f, 0x1e, 0xff, 0x9f, 0x1e, 0xff, 0xb3, 0x57,
+    0x25, 0x36, 0xcd, 0xff, 0x9f, 0x8e, 0x34, 0x7e, 0x7f, 0xfc, 0x8f, 0xad, 0x7b, 0xf7, 0xd7, 0xe7, 0x9b, 0xe3, 0xd0, 0x67,
+    0x61, 0xce, 0x79, 0x8a, 0xe3, 0xfd, 0x99, 0xcd, 0xfc, 0xae, 0xa8, 0xeb, 0xef, 0xff, 0x8f, 0x65, 0xfd, 0x7f, 0xef, 0x93,
+    0x2d, 0xbb, 0xee, 0xff, 0xef, 0x7d, 0xa2, 0xa8, 0xfb, 0x0c, 0xc5, 0xff, 0x37, 0xda, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe2, 0x73, 0xf2, 0x2b, 0x72, 0xeb, 0xf7, 0xe7, 0xc3, 0x38, 0x3d, 0xb7, 0xfe,
+    0xdd, 0xfb, 0x33, 0xe3, 0xd8, 0xdc, 0xfa, 0xe3, 0x51, 0x8e, 0xa2, 0xef, 0xe5, 0xd6, 0xf7, 0xee, 0xc7, 0xd3, 0xb7, 0x64,
+    0xd6, 0xe5, 0xd6, 0xef, 0x7b, 0x87, 0xfa, 0xf4, 0xdc, 0xfa, 0xe3, 0x26, 0x13, 0xd1, 0xc9, 0xb9, 0xf5, 0xeb, 0x6f, 0x51,
+    0x9d, 0x9a, 0x5b, 0x9f, 0xb5, 0xef, 0x4e, 0x3e, 0x6d, 0xfb, 0xe3, 0xc5, 0x6b, 0xf6, 0x46, 0x36, 0x84, 0x93, 0x6b, 0x64,
+    0x3e, 0x5b, 0xd3, 0x8a, 0x16, 0xa5, 0xf3, 0x7d, 0xcd, 0xb3, 0x73, 0xeb, 0x7f, 0x3d, 0xc6, 0xeb, 0xd9, 0x58, 0xa3, 0x9c,
+    0xf3, 0xbe, 0xef, 0x5d, 0xed, 0x48, 0x8d, 0x1b, 0xd7, 0xe6, 0xd6, 0xba, 0x6e, 0x47, 0xcf, 0xdd, 0x29, 0xe1, 0xdb, 0xb9,
+    0xb5, 0xc5, 0x7f, 0x4f, 0xab, 0x1b, 0x4d, 0x51, 0xf7, 0xf4, 0x93, 0x7d, 0x59, 0x83, 0xfa, 0xe2, 0x3f, 0xda, 0x3f, 0xdb,
+    0x9b, 0x5b, 0xab, 0xba, 0x9e, 0x20, 0xfe, 0x7f, 0x57, 0x3b, 0x90, 0x99, 0xd1, 0x3e, 0x29, 0xf7, 0x37, 0x6b, 0xc0, 0xce,
+    0xdc, 0x5a, 0x5f, 0x8f, 0xff, 0x4a, 0xff, 0x20, 0xfe, 0x7f, 0xcf, 0xf8, 0xbf, 0xaf, 0x2f, 0xaf, 0xaf, 0xff, 0x7f, 0x79,
+    0x6f, 0xad, 0xdf, 0x19, 0xff, 0x67, 0xe4, 0xd6, 0x15, 0xff, 0xef, 0x8c, 0xff, 0x6b, 0xfb, 0x13, 0xac, 0x58, 0xcd, 0xed,
+    0x9d, 0x4d, 0x88, 0xff, 0xd5, 0x75, 0xe7, 0xa4, 0x9d, 0x21, 0xc4, 0x78, 0x7d, 0xfe, 0x5f, 0xdb, 0x13, 0xb4, 0xda, 0x02,
+    0xec, 0xcb, 0xd5, 0xfa, 0x46, 0x6e, 0x7d, 0xb9, 0x75, 0x67, 0xae, 0xd7, 0xf5, 0xe8, 0xef, 0x9b, 0xf7, 0x63, 0x4e, 0x1f,
+    0xff, 0xf7, 0xde, 0xab, 0xf9, 0xff, 0x35, 0xa5, 0xd5, 0x2b, 0xde, 0xab, 0xc6, 0xff, 0x2b, 0xda, 0xd8, 0x37, 0x4b, 0x63,
+    0xcd, 0xbd, 0xa3, 0xfe, 0x33, 0x8c, 0xa6, 0xd1, 0x1f, 0xef, 0xb4, 0x1a, 0xb0, 0x66, 0x9c, 0xf1, 0x7e, 0x4b, 0x08, 0x7c,
+    0xed, 0x49, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0xf9, 0xef, 0x3f, 0xfb, 0x01, 0xd4, 0x3f,
-    0xf0, 0xeb, 0xea, 0xff, 0x3f };
+    0x00, 0x00, 0x00, 0x80, 0x93, 0xfc, 0xfb, 0x9f, 0x72, 0x00, 0xf1, 0x0f, 0xfc, 0x75, 0xf1, 0xff, 0x2f };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle darkFontRecs[189] = {
@@ -257,100 +257,100 @@
     { 218, 52, 6 , 9 },
     { 232, 52, 5 , 9 },
     { 245, 52, 5 , 12 },
-    { 258, 52, 5 , 9 },
-    { 271, 52, 5 , 10 },
-    { 284, 52, 7 , 9 },
-    { 299, 52, 5 , 9 },
-    { 312, 52, 6 , 5 },
-    { 326, 52, 5 , 3 },
-    { 339, 52, 7 , 9 },
-    { 354, 52, 5 , 9 },
-    { 367, 52, 4 , 4 },
-    { 379, 52, 5 , 7 },
-    { 392, 52, 5 , 9 },
-    { 405, 52, 5 , 9 },
-    { 418, 52, 5 , 12 },
-    { 431, 52, 5 , 9 },
-    { 444, 52, 7 , 9 },
-    { 459, 52, 1 , 1 },
-    { 468, 52, 5 , 10 },
-    { 481, 52, 5 , 9 },
-    { 494, 52, 5 , 9 },
-    { 4, 76, 6 , 5 },
-    { 18, 76, 9 , 9 },
-    { 35, 76, 9 , 7 },
-    { 52, 76, 5 , 11 },
-    { 65, 76, 5 , 9 },
-    { 78, 76, 5 , 12 },
-    { 91, 76, 5 , 12 },
-    { 104, 76, 5 , 12 },
-    { 117, 76, 6 , 12 },
-    { 131, 76, 5 , 11 },
-    { 144, 76, 5 , 13 },
-    { 157, 76, 9 , 9 },
-    { 174, 76, 5 , 12 },
-    { 187, 76, 5 , 12 },
-    { 200, 76, 5 , 12 },
-    { 213, 76, 5 , 12 },
-    { 226, 76, 5 , 11 },
-    { 239, 76, 2 , 12 },
-    { 249, 76, 2 , 12 },
-    { 259, 76, 3 , 12 },
-    { 270, 76, 3 , 11 },
-    { 281, 76, 6 , 9 },
-    { 295, 76, 6 , 12 },
-    { 309, 76, 5 , 12 },
-    { 322, 76, 5 , 12 },
-    { 335, 76, 5 , 12 },
-    { 348, 76, 6 , 12 },
-    { 362, 76, 5 , 11 },
-    { 375, 76, 5 , 5 },
-    { 388, 76, 7 , 9 },
-    { 403, 76, 5 , 12 },
-    { 416, 76, 5 , 12 },
-    { 429, 76, 5 , 12 },
-    { 442, 76, 5 , 11 },
-    { 455, 76, 5 , 12 },
-    { 468, 76, 5 , 9 },
-    { 481, 76, 5 , 9 },
-    { 494, 76, 5 , 10 },
-    { 4, 100, 5 , 10 },
-    { 17, 100, 5 , 10 },
-    { 30, 100, 6 , 10 },
-    { 44, 100, 5 , 9 },
-    { 57, 100, 5 , 11 },
-    { 70, 100, 9 , 7 },
+    { 258, 52, 0 , 0 },
+    { 266, 52, 5 , 10 },
+    { 279, 52, 7 , 9 },
+    { 294, 52, 0 , 0 },
+    { 302, 52, 6 , 5 },
+    { 316, 52, 5 , 3 },
+    { 329, 52, 7 , 9 },
+    { 344, 52, 0 , 0 },
+    { 352, 52, 4 , 4 },
+    { 364, 52, 5 , 7 },
+    { 377, 52, 0 , 0 },
+    { 385, 52, 0 , 0 },
+    { 393, 52, 5 , 12 },
+    { 406, 52, 5 , 9 },
+    { 419, 52, 7 , 9 },
+    { 434, 52, 1 , 1 },
+    { 443, 52, 5 , 10 },
+    { 456, 52, 0 , 0 },
+    { 464, 52, 0 , 0 },
+    { 472, 52, 6 , 5 },
+    { 486, 52, 9 , 9 },
+    { 4, 76, 9 , 7 },
+    { 21, 76, 5 , 11 },
+    { 34, 76, 5 , 9 },
+    { 47, 76, 5 , 12 },
+    { 60, 76, 5 , 12 },
+    { 73, 76, 5 , 12 },
+    { 86, 76, 6 , 12 },
+    { 100, 76, 5 , 11 },
+    { 113, 76, 5 , 13 },
+    { 126, 76, 9 , 9 },
+    { 143, 76, 5 , 12 },
+    { 156, 76, 5 , 12 },
+    { 169, 76, 5 , 12 },
+    { 182, 76, 5 , 12 },
+    { 195, 76, 5 , 11 },
+    { 208, 76, 2 , 12 },
+    { 218, 76, 2 , 12 },
+    { 228, 76, 3 , 12 },
+    { 239, 76, 3 , 11 },
+    { 250, 76, 6 , 9 },
+    { 264, 76, 6 , 12 },
+    { 278, 76, 5 , 12 },
+    { 291, 76, 5 , 12 },
+    { 304, 76, 5 , 12 },
+    { 317, 76, 6 , 12 },
+    { 331, 76, 5 , 11 },
+    { 344, 76, 5 , 5 },
+    { 357, 76, 7 , 9 },
+    { 372, 76, 5 , 12 },
+    { 385, 76, 5 , 12 },
+    { 398, 76, 5 , 12 },
+    { 411, 76, 5 , 11 },
+    { 424, 76, 5 , 12 },
+    { 437, 76, 5 , 9 },
+    { 450, 76, 5 , 9 },
+    { 463, 76, 5 , 10 },
+    { 476, 76, 5 , 10 },
+    { 489, 76, 5 , 10 },
+    { 4, 100, 6 , 10 },
+    { 18, 100, 5 , 9 },
+    { 31, 100, 5 , 11 },
+    { 44, 100, 9 , 7 },
+    { 61, 100, 5 , 10 },
+    { 74, 100, 5 , 10 },
     { 87, 100, 5 , 10 },
     { 100, 100, 5 , 10 },
-    { 113, 100, 5 , 10 },
-    { 126, 100, 5 , 10 },
-    { 139, 100, 5 , 9 },
-    { 152, 100, 2 , 10 },
-    { 162, 100, 2 , 10 },
-    { 172, 100, 3 , 10 },
-    { 183, 100, 3 , 9 },
-    { 194, 100, 6 , 9 },
-    { 208, 100, 6 , 10 },
+    { 113, 100, 5 , 9 },
+    { 126, 100, 2 , 10 },
+    { 136, 100, 2 , 10 },
+    { 146, 100, 3 , 10 },
+    { 157, 100, 3 , 9 },
+    { 168, 100, 6 , 9 },
+    { 182, 100, 6 , 10 },
+    { 196, 100, 5 , 10 },
+    { 209, 100, 5 , 10 },
     { 222, 100, 5 , 10 },
-    { 235, 100, 5 , 10 },
-    { 248, 100, 5 , 10 },
-    { 261, 100, 6 , 10 },
-    { 275, 100, 5 , 9 },
-    { 288, 100, 5 , 5 },
-    { 301, 100, 7 , 7 },
+    { 235, 100, 6 , 10 },
+    { 249, 100, 5 , 9 },
+    { 262, 100, 5 , 5 },
+    { 275, 100, 7 , 7 },
+    { 290, 100, 5 , 10 },
+    { 303, 100, 5 , 10 },
     { 316, 100, 5 , 10 },
-    { 329, 100, 5 , 10 },
-    { 342, 100, 5 , 10 },
-    { 355, 100, 5 , 9 },
-    { 368, 100, 5 , 12 },
-    { 381, 100, 5 , 11 },
-    { 394, 100, 5 , 11 },
+    { 329, 100, 5 , 9 },
+    { 342, 100, 5 , 12 },
+    { 355, 100, 5 , 11 },
+    { 368, 100, 5 , 11 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo darkFontGlyphs[189] = {
-    { 32, 0, 13, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 2, 4, 5, { 0 }},
     { 34, 2, 4, 7, { 0 }},
     { 35, 1, 4, 8, { 0 }},
@@ -451,25 +451,25 @@
     { 8364, 1, 4, 8, { 0 }},
     { 165, 1, 4, 7, { 0 }},
     { 352, 1, 1, 7, { 0 }},
-    { 167, 2, 4, 9, { 0 }},
+    { 167, 0, 0, 0, { 0 }},
     { 353, 1, 3, 7, { 0 }},
     { 169, 1, 4, 9, { 0 }},
-    { 170, 2, 4, 9, { 0 }},
+    { 170, 0, 0, 0, { 0 }},
     { 171, 1, 6, 8, { 0 }},
     { 172, 1, 8, 7, { 0 }},
     { 174, 1, 4, 9, { 0 }},
-    { 175, 2, 4, 9, { 0 }},
+    { 175, 0, 0, 0, { 0 }},
     { 176, 1, 4, 6, { 0 }},
     { 177, 1, 6, 7, { 0 }},
-    { 178, 2, 4, 9, { 0 }},
-    { 179, 2, 4, 9, { 0 }},
+    { 178, 0, 0, 0, { 0 }},
+    { 179, 0, 0, 0, { 0 }},
     { 381, 1, 1, 7, { 0 }},
     { 181, 1, 6, 7, { 0 }},
     { 182, 1, 4, 9, { 0 }},
     { 183, 2, 8, 5, { 0 }},
     { 382, 1, 3, 7, { 0 }},
-    { 185, 2, 4, 9, { 0 }},
-    { 186, 2, 4, 9, { 0 }},
+    { 185, 0, 0, 0, { 0 }},
+    { 186, 0, 0, 0, { 0 }},
     { 187, 1, 6, 8, { 0 }},
     { 338, 1, 4, 11, { 0 }},
     { 339, 1, 6, 11, { 0 }},
diff --git a/raygui/styles/enefete/style_enefete.h b/raygui/styles/enefete/style_enefete.h
--- a/raygui/styles/enefete/style_enefete.h
+++ b/raygui/styles/enefete/style_enefete.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,155 +15,153 @@
 
 // Custom style name: Enefete
 static const GuiStyleProp enefeteStyleProps[ENEFETE_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x1980d5ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x4df3ebff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0x103e60ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xe7e2f7ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x23d4ddff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xf1f1f1ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0x6413a6ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xea66d9ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x9f00bbff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x4b909eff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x73c7d0ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x448894ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0x1d3f6cff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x29c9e5ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0x1980d5ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x4df3ebff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x103e60ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xe7e2f7ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x23d4ddff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xf1f1f1ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x6413a6ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xea66d9ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x9f00bbff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x4b909eff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x73c7d0ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x448894ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x1d3f6cff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x29c9e5ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
-// WARNING: This style uses a custom font: "GenericMobileSystemNuevo.ttf" (size: 16, spacing: 0)
+// WARNING: This style uses a custom font: "GMSN.ttf" (size: 16, spacing: 0)
 
-#define ENEFETE_STYLE_FONT_ATLAS_COMP_SIZE 2462
+#define ENEFETE_STYLE_FONT_ATLAS_COMP_SIZE 2434
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char enefeteFontData[ENEFETE_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0x59, 0xb2, 0xeb, 0x34, 0x10, 0x00, 0x50, 0x6d, 0x83, 0xfd, 0xef, 0x8d, 0x6d, 0x88, 0xa2, 0x28, 0x0a, 0xb8, 0x8f,
-    0x58, 0x52, 0x77, 0xcb, 0x53, 0x0e, 0xa7, 0xf8, 0xb9, 0x7e, 0x49, 0x6c, 0xd9, 0x6d, 0x0d, 0xb6, 0x5a, 0xbd, 0x01, 0x00,
-    0x00, 0x00, 0x5f, 0xef, 0xcf, 0xff, 0xfe, 0xff, 0x6f, 0xff, 0xb7, 0xe5, 0x78, 0xdb, 0xdf, 0xff, 0x62, 0xb4, 0xa5, 0x7f,
-    0xdc, 0x93, 0x7e, 0xb0, 0x6d, 0xee, 0xb7, 0x62, 0xfb, 0xd5, 0x97, 0xca, 0xa6, 0x1f, 0x94, 0x5a, 0x4b, 0xff, 0xbd, 0x1d,
-    0x96, 0x7d, 0x0f, 0x94, 0x5e, 0x3b, 0xdc, 0xe7, 0xcf, 0x9f, 0x1c, 0x6f, 0xa9, 0xdc, 0xcf, 0x95, 0x32, 0xad, 0x3c, 0x3f,
-    0x2b, 0x25, 0xd8, 0x96, 0xb7, 0x1e, 0x97, 0x54, 0x9b, 0x2a, 0xc7, 0xf1, 0x37, 0xf7, 0x9b, 0xc4, 0xff, 0xf1, 0xd1, 0xfc,
-    0x1d, 0x83, 0x47, 0x67, 0x7f, 0xfe, 0xda, 0x68, 0xd3, 0xf7, 0x84, 0x7f, 0x7e, 0xbb, 0x2e, 0x42, 0xb3, 0xdf, 0xd2, 0x87,
-    0x77, 0xc2, 0x95, 0x12, 0x3a, 0x2e, 0xbd, 0x16, 0x8a, 0x89, 0xd1, 0x77, 0x46, 0xf6, 0x65, 0x66, 0xdb, 0xdc, 0xfe, 0xc5,
-    0xae, 0xf9, 0xf1, 0xf7, 0x1d, 0x5f, 0x69, 0x47, 0x67, 0x78, 0x74, 0xe5, 0x1e, 0x1f, 0xdf, 0xfa, 0x59, 0xcd, 0x44, 0xfd,
-    0x9e, 0xf8, 0xef, 0xff, 0x8a, 0xb3, 0x1e, 0x8c, 0xc1, 0x7c, 0xdd, 0xb0, 0x5e, 0x2b, 0x1c, 0xdf, 0xb3, 0xf6, 0x95, 0xf0,
-    0x51, 0x39, 0xf5, 0xc0, 0x35, 0xbc, 0x7e, 0xce, 0xa2, 0xbf, 0xb3, 0x23, 0xfe, 0x2b, 0xa2, 0xb8, 0x4d, 0xd4, 0x88, 0x91,
-    0x36, 0xc5, 0x71, 0x8b, 0x33, 0x1f, 0xe1, 0xa3, 0x7d, 0x89, 0xb4, 0x67, 0xf2, 0x65, 0xb9, 0xda, 0xe6, 0xd8, 0x17, 0xff,
-    0x15, 0xf5, 0xed, 0x7a, 0x5d, 0xb2, 0xaf, 0x7c, 0x67, 0xea, 0xeb, 0xfb, 0xc7, 0xff, 0xfa, 0xb1, 0x55, 0xc4, 0x7f, 0xcd,
-    0x59, 0x59, 0x8f, 0xff, 0x2e, 0xfe, 0xa7, 0xeb, 0xff, 0xcf, 0xa5, 0xde, 0x83, 0xfd, 0xaf, 0x7c, 0xaf, 0xbd, 0xaa, 0x9d,
-    0x7f, 0x6d, 0xfc, 0x8f, 0xfa, 0x83, 0x6d, 0x39, 0xfe, 0x63, 0xbd, 0xcf, 0x48, 0x6f, 0xa3, 0xaa, 0xfd, 0xbf, 0xb7, 0xf5,
-    0x70, 0xdc, 0x7b, 0x8c, 0x44, 0xf8, 0xda, 0x98, 0x47, 0xcd, 0x37, 0x5f, 0x11, 0xff, 0x6d, 0x62, 0x6f, 0xfa, 0xb0, 0xff,
-    0x3f, 0x7b, 0x87, 0x5f, 0x8b, 0xff, 0xba, 0xba, 0xe3, 0xca, 0xf8, 0xef, 0xc1, 0x11, 0xb1, 0xea, 0x7e, 0x7c, 0xfc, 0xd8,
-    0xd6, 0xc6, 0xe7, 0xce, 0xeb, 0xff, 0x8f, 0xda, 0x81, 0x99, 0xf8, 0xff, 0x96, 0xf6, 0xff, 0x4c, 0x4b, 0x7b, 0x54, 0x8a,
-    0xf3, 0xf7, 0xb3, 0x2b, 0x6a, 0xf3, 0x6b, 0xe3, 0xbf, 0x4f, 0x8d, 0x61, 0xdf, 0x3b, 0xfe, 0xdb, 0x72, 0x2f, 0xaf, 0xf2,
-    0xda, 0x8d, 0x5e, 0x51, 0xa3, 0x2b, 0x77, 0xe6, 0xae, 0x76, 0xcf, 0xf8, 0x8f, 0x5d, 0xcf, 0x7d, 0xd8, 0xca, 0xef, 0x89,
-    0xf8, 0x6f, 0xaf, 0x89, 0xff, 0x1e, 0x1c, 0xed, 0xf8, 0xdc, 0xde, 0x8b, 0x8d, 0xc7, 0x45, 0x5a, 0xf2, 0x99, 0x36, 0x4a,
-    0x55, 0xfc, 0xdf, 0xe3, 0x4c, 0xaa, 0xff, 0x2b, 0xc6, 0xff, 0xdb, 0x61, 0x0b, 0xff, 0xbb, 0xe2, 0xff, 0xf8, 0xf9, 0x5f,
-    0xbb, 0xfd, 0xb3, 0xba, 0x68, 0xfc, 0xd7, 0x8c, 0xff, 0x9f, 0x7f, 0xb7, 0x9e, 0xe9, 0xb9, 0xe6, 0xe2, 0xff, 0x4e, 0xcf,
-    0xff, 0xf6, 0xc6, 0xff, 0xe8, 0xa9, 0xf7, 0x9b, 0xda, 0xff, 0x95, 0xe3, 0xf8, 0xf1, 0x77, 0x6e, 0xf6, 0xbd, 0x53, 0xd4,
-    0x42, 0x6f, 0x72, 0xdd, 0x33, 0xfa, 0xa3, 0xcf, 0x81, 0x72, 0xa3, 0x74, 0x57, 0xbf, 0xff, 0x53, 0x5f, 0x82, 0xe7, 0xed,
-    0x4f, 0xbf, 0x71, 0x9b, 0x91, 0x77, 0xbd, 0x25, 0xeb, 0xea, 0x3a, 0xf3, 0xf9, 0x6c, 0xfc, 0xb7, 0x9c, 0x1f, 0x76, 0xd6,
-    0x6c, 0x4a, 0xe2, 0x19, 0x63, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xe4, 0xf9,
-    0x0b, 0x91, 0xb9, 0x86, 0xd1, 0x3c, 0x04, 0xb5, 0xf9, 0x10, 0xfa, 0x8f, 0x3c, 0xac, 0xab, 0xdf, 0x3d, 0x3b, 0xd7, 0x32,
-    0x33, 0x4f, 0xb3, 0xf6, 0xb3, 0x55, 0x99, 0x25, 0x6a, 0x67, 0x9e, 0x8f, 0xf3, 0xdd, 0xac, 0x66, 0x6b, 0x8e, 0x65, 0x41,
-    0x5b, 0xcf, 0x69, 0x9e, 0xc9, 0xc3, 0x57, 0x3d, 0xb7, 0xba, 0x26, 0x3f, 0xda, 0xfc, 0x9c, 0xf7, 0x9a, 0x4c, 0x23, 0x75,
-    0xf1, 0xbf, 0x7e, 0x1e, 0xfa, 0x8f, 0xfd, 0xe8, 0x17, 0x64, 0x62, 0xa8, 0xdf, 0x3a, 0xf7, 0xf7, 0x3b, 0xc5, 0xff, 0xec,
-    0x3d, 0x38, 0x7b, 0xf5, 0xc6, 0x67, 0xdc, 0x8d, 0xb2, 0x4f, 0x5c, 0x9f, 0x5b, 0xa5, 0x15, 0xe5, 0x47, 0xad, 0x8f, 0xff,
-    0x76, 0x52, 0xfd, 0x1f, 0x8d, 0xff, 0x3e, 0x5c, 0xcd, 0xe6, 0xbe, 0xf1, 0xbf, 0x9e, 0x77, 0x22, 0xfb, 0xab, 0xb9, 0x95,
-    0x1c, 0xe2, 0xb9, 0xc6, 0xf3, 0xf1, 0x9f, 0xcb, 0x14, 0x74, 0x6e, 0xdd, 0x7a, 0x7d, 0xfc, 0x47, 0x4b, 0xe4, 0xaa, 0xf8,
-    0x8f, 0x5f, 0x25, 0x7d, 0x98, 0xcd, 0xfa, 0x8e, 0xf1, 0xdf, 0xbf, 0x3e, 0xfe, 0xeb, 0x73, 0xf9, 0x46, 0x23, 0x20, 0x92,
-    0x07, 0x7d, 0xa6, 0x5f, 0xbd, 0xda, 0xb3, 0x8b, 0xf7, 0xbb, 0xa2, 0xf5, 0xff, 0xe8, 0xf3, 0x33, 0x47, 0x72, 0x75, 0xfc,
-    0xb7, 0x61, 0x84, 0xf7, 0x44, 0x94, 0xc6, 0x47, 0x40, 0x32, 0x39, 0xaf, 0xae, 0x89, 0xff, 0x16, 0x5a, 0x31, 0x30, 0x5e,
-    0xcf, 0xdf, 0x3d, 0xfe, 0xab, 0xfb, 0x06, 0xd1, 0x11, 0x83, 0xea, 0x7d, 0xbc, 0x5f, 0xff, 0xbf, 0x17, 0xb4, 0x12, 0x77,
-    0xc4, 0x7f, 0xb6, 0x7c, 0xa2, 0xd1, 0x14, 0x8b, 0xff, 0xf9, 0xda, 0x3d, 0xb6, 0x9a, 0x40, 0x6c, 0xdb, 0x7b, 0xe3, 0xbf,
-    0x36, 0x7f, 0xea, 0xfa, 0x58, 0xf1, 0x9b, 0xe2, 0x3f, 0xda, 0xff, 0x9f, 0xef, 0x71, 0xdd, 0x2b, 0xfe, 0xdb, 0xc4, 0xfa,
-    0x3c, 0x67, 0xc7, 0xff, 0x28, 0xdb, 0xea, 0xdb, 0xe2, 0x3f, 0x1a, 0x5b, 0xd1, 0xf5, 0x8c, 0x5a, 0xb8, 0xa6, 0x7b, 0x7b,
-    0xfd, 0xdf, 0x5e, 0x1a, 0xff, 0xf1, 0xf5, 0x65, 0x9e, 0x15, 0xff, 0xd5, 0xad, 0x86, 0xeb, 0xf3, 0xea, 0x45, 0x47, 0x1b,
-    0xe3, 0xa3, 0x94, 0xef, 0x6e, 0xff, 0xf7, 0x0d, 0xad, 0xf0, 0xec, 0xf3, 0xff, 0xab, 0xe2, 0x7f, 0xd7, 0xf3, 0xbf, 0x36,
-    0xbd, 0xe6, 0xdf, 0xae, 0xbd, 0x12, 0xff, 0xd5, 0xf1, 0x5f, 0xb9, 0x76, 0xe2, 0xda, 0xbf, 0xa8, 0x7e, 0xff, 0xa7, 0x7e,
-    0x24, 0x6d, 0xff, 0xfb, 0x3f, 0x7b, 0xca, 0xa7, 0x4d, 0xad, 0x98, 0xb6, 0xfe, 0xab, 0x6d, 0x72, 0x55, 0xe3, 0xd8, 0x3e,
-    0xd7, 0xbf, 0x7d, 0x73, 0xf7, 0xf8, 0x8f, 0xbd, 0x6d, 0x10, 0xfd, 0x64, 0xfc, 0x13, 0xf7, 0x59, 0x0b, 0xec, 0x3b, 0xdf,
-    0xa2, 0x54, 0x0a, 0xd5, 0x25, 0xa4, 0x54, 0x71, 0x07, 0x78, 0xd7, 0x7b, 0xe6, 0xbb, 0xfe, 0x3d, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf7, 0xcd, 0x5e, 0x99, 0xcd, 0xb9, 0xfd, 0xeb, 0x5f, 0x7b, 0x68, 0xbe,
-    0x6b, 0x4f, 0x7e, 0xae, 0x2f, 0xe7, 0xc1, 0x58, 0x9b, 0x77, 0x5a, 0x91, 0x83, 0xa1, 0x3a, 0x8f, 0xd4, 0xb8, 0x5c, 0x66,
-    0xcf, 0xf4, 0x78, 0x26, 0xf1, 0xea, 0x15, 0x12, 0x99, 0xd7, 0x93, 0xc9, 0xa8, 0x93, 0x59, 0x4b, 0x20, 0x9e, 0x09, 0xb1,
-    0x2d, 0x7e, 0x6b, 0xec, 0xac, 0xfd, 0x3a, 0xbf, 0x3a, 0x9a, 0xef, 0xb3, 0x26, 0xfe, 0x63, 0xb9, 0x25, 0xaf, 0xc8, 0xb4,
-    0x16, 0xbf, 0x9a, 0x6a, 0xf7, 0xb0, 0xbe, 0x54, 0xe6, 0xf6, 0x36, 0xf7, 0xf7, 0xcc, 0x9c, 0xfe, 0x5c, 0xee, 0x81, 0xd5,
-    0x92, 0xe8, 0x53, 0xd9, 0x52, 0x56, 0x3f, 0xf9, 0xf9, 0x73, 0xc7, 0xdb, 0xfa, 0x54, 0xe6, 0x88, 0xd5, 0x6f, 0xfd, 0x6d,
-    0xf0, 0xf9, 0x7e, 0x78, 0xe7, 0xad, 0x9d, 0xd1, 0xfa, 0x94, 0xf8, 0x5f, 0xbf, 0x77, 0x9f, 0x1d, 0xff, 0xf1, 0xfb, 0x54,
-    0x2b, 0xcc, 0xd2, 0xb3, 0xbe, 0x36, 0x46, 0x64, 0xe6, 0x6e, 0x3e, 0x47, 0xd0, 0x19, 0x19, 0x35, 0x7e, 0x8d, 0xa6, 0x95,
-    0xb3, 0x36, 0x17, 0xfd, 0x9f, 0xe2, 0xf7, 0xa8, 0xb5, 0xb5, 0x2f, 0xfe, 0x6b, 0x32, 0xf2, 0x3d, 0x27, 0xfe, 0x67, 0x7a,
-    0x1b, 0xe7, 0xc5, 0xff, 0xf8, 0xef, 0x75, 0x59, 0xb5, 0x2b, 0x73, 0xed, 0x8c, 0x72, 0x2d, 0xb7, 0xd0, 0x19, 0xe9, 0xc1,
-    0xfb, 0x4a, 0x2f, 0xce, 0x67, 0xde, 0x7f, 0xfc, 0x3f, 0x5b, 0x53, 0xcf, 0xc6, 0xff, 0xfa, 0x2f, 0xfe, 0x36, 0xd1, 0x7e,
-    0xa8, 0x6e, 0xff, 0xaf, 0x5c, 0x2f, 0x4f, 0x8a, 0xff, 0x36, 0x95, 0x8b, 0xec, 0xde, 0xf5, 0x7f, 0x65, 0x4e, 0xad, 0xfa,
-    0xf8, 0x8f, 0x45, 0x78, 0xa4, 0x26, 0x9f, 0x5f, 0xf1, 0x60, 0xed, 0xbc, 0x8d, 0x5b, 0xf9, 0x3d, 0xd1, 0xfe, 0xdf, 0x11,
-    0xff, 0xd5, 0xe3, 0x7f, 0xf1, 0x35, 0x3c, 0xf6, 0x8c, 0x74, 0x45, 0xc6, 0xff, 0xda, 0x09, 0xfd, 0xff, 0x51, 0x3b, 0x31,
-    0x5a, 0x2a, 0xf5, 0x6b, 0x53, 0x55, 0x66, 0xdb, 0x5e, 0x8d, 0xff, 0x4c, 0xfd, 0x1f, 0x1d, 0x87, 0xc8, 0xb4, 0x10, 0xdb,
-    0x60, 0xe4, 0x60, 0xf4, 0x9b, 0xeb, 0x59, 0x43, 0x73, 0xed, 0xff, 0x1d, 0xf1, 0x7f, 0x56, 0xff, 0xbf, 0x25, 0x47, 0x35,
-    0xeb, 0xda, 0xff, 0x2d, 0xd1, 0xca, 0xcc, 0xdc, 0x6d, 0x22, 0xa5, 0xbd, 0x5e, 0x66, 0x57, 0xd6, 0xff, 0xb9, 0xd5, 0xe6,
-    0xda, 0xb6, 0x7c, 0xa7, 0x91, 0xf6, 0xff, 0x9e, 0xa7, 0x70, 0xcf, 0xae, 0xff, 0xcf, 0x7c, 0x3a, 0xb9, 0x73, 0x7f, 0xee,
-    0x93, 0x77, 0x6e, 0x4f, 0x1e, 0xe9, 0x3b, 0xf4, 0xff, 0x23, 0xe3, 0xff, 0xd1, 0x9c, 0xc7, 0xe3, 0xda, 0xb8, 0x0f, 0x46,
-    0xd3, 0xbe, 0x35, 0xfe, 0xef, 0x91, 0x9b, 0xf9, 0x7b, 0xe3, 0xbf, 0xa5, 0xf2, 0x4f, 0x5f, 0x11, 0xff, 0x73, 0x2d, 0xab,
-    0xd8, 0x8a, 0x88, 0xb9, 0x15, 0x93, 0xa3, 0x7d, 0xba, 0x76, 0xea, 0x95, 0x9e, 0x79, 0xfe, 0xb7, 0xe7, 0xfd, 0x9f, 0xda,
-    0x37, 0x0d, 0x9e, 0xfb, 0x3e, 0xd4, 0x3b, 0x8e, 0xe0, 0x8c, 0xfb, 0xf9, 0x78, 0xd5, 0x82, 0xbe, 0xe9, 0x98, 0x62, 0x4f,
-    0x75, 0xa3, 0xcf, 0x89, 0xf7, 0xdd, 0xef, 0xa3, 0xef, 0xff, 0x44, 0xcb, 0x54, 0xa6, 0xe5, 0x6f, 0xb9, 0x83, 0x5d, 0x19,
-    0xff, 0xfb, 0x5b, 0x49, 0x55, 0xef, 0x30, 0x7c, 0xdb, 0x95, 0xa2, 0x64, 0xbe, 0xe5, 0xbc, 0xae, 0x8f, 0xf4, 0xbe, 0xa5,
-    0x24, 0x5c, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3b, 0xe7, 0x8a, 0xf5, 0xb2,
-    0x4c, 0x0b, 0xb3, 0x79, 0xae, 0xfa, 0xc2, 0xcc, 0xcd, 0x36, 0xcc, 0xdd, 0x9c, 0xfd, 0x9e, 0x9f, 0xab, 0x1a, 0xf4, 0xc1,
-    0xbf, 0x68, 0x81, 0x3d, 0xed, 0xe1, 0x79, 0xc8, 0xeb, 0x6b, 0x34, 0xfc, 0xdc, 0xe3, 0x96, 0x28, 0x91, 0x36, 0x31, 0x4f,
-    0x75, 0x6d, 0xae, 0xde, 0xee, 0x63, 0xae, 0xc9, 0x96, 0x1e, 0xc9, 0xfb, 0x37, 0x9b, 0xbd, 0x64, 0x47, 0xa9, 0xe4, 0x66,
-    0xbb, 0xf7, 0xa5, 0xbf, 0xc7, 0x72, 0x3f, 0xf4, 0xc3, 0x18, 0xaa, 0xb9, 0x3f, 0xcd, 0x65, 0xe8, 0x5b, 0x9f, 0xb3, 0x3e,
-    0x97, 0xdf, 0xb3, 0x6d, 0xcc, 0x89, 0xbd, 0x7e, 0xb5, 0xc7, 0x23, 0x25, 0xb7, 0x75, 0x6e, 0xae, 0xfe, 0x8e, 0x63, 0x8e,
-    0xe7, 0xb3, 0x9d, 0xff, 0x9e, 0x4c, 0xed, 0x37, 0x9e, 0x85, 0x99, 0xa9, 0x27, 0xda, 0xd4, 0xdd, 0x7a, 0x2d, 0xca, 0xa3,
-    0x77, 0x99, 0xf5, 0xf8, 0xaf, 0x99, 0x83, 0xde, 0x87, 0xc7, 0x13, 0x5b, 0x81, 0x63, 0x26, 0xbf, 0xf7, 0xfb, 0x5a, 0x8b,
-    0xf9, 0xf8, 0xff, 0xd6, 0x36, 0xf6, 0xfd, 0x4a, 0xa0, 0x2f, 0xd6, 0xf1, 0x9f, 0x23, 0x6c, 0xa6, 0x15, 0x5f, 0x13, 0xff,
-    0xeb, 0xf7, 0xad, 0xfd, 0xf1, 0xdf, 0x43, 0xdf, 0x10, 0x6d, 0xcf, 0xde, 0x73, 0x6b, 0x2e, 0xfe, 0xc7, 0x57, 0xd0, 0x9e,
-    0xad, 0x55, 0xab, 0x28, 0xe5, 0x72, 0x94, 0xb7, 0x60, 0xef, 0x21, 0x9b, 0x11, 0x68, 0x35, 0x1b, 0x5b, 0x0f, 0xf5, 0x46,
-    0xf6, 0xd4, 0xff, 0xf9, 0xfe, 0xff, 0x7c, 0xbb, 0x2d, 0x3e, 0x7e, 0x90, 0xb9, 0x96, 0x76, 0xb4, 0xc3, 0xcf, 0xd8, 0x1a,
-    0xc9, 0x1b, 0x77, 0xd5, 0xd6, 0xdd, 0xf1, 0xdf, 0xff, 0xb3, 0x8e, 0xe2, 0x51, 0x44, 0xad, 0xdf, 0x1d, 0xee, 0xd7, 0xb2,
-    0x88, 0x8f, 0xff, 0xad, 0x1e, 0x43, 0xc5, 0x11, 0xcf, 0xf5, 0x0b, 0x67, 0xa3, 0xb4, 0xae, 0xfe, 0xcf, 0x9c, 0xf5, 0x2b,
-    0xb7, 0x66, 0x46, 0xf7, 0xde, 0x1a, 0xff, 0x73, 0xe5, 0x76, 0x45, 0xfc, 0x57, 0xe7, 0x13, 0x9c, 0x69, 0x4d, 0x44, 0xb3,
-    0x34, 0x5f, 0xd1, 0x97, 0x9c, 0xcd, 0xce, 0x9f, 0x5b, 0x7f, 0xf6, 0x4d, 0xf1, 0xdf, 0x8a, 0xda, 0xff, 0x67, 0x6e, 0xbd,
-    0x47, 0xfb, 0xbf, 0xbf, 0xa0, 0xfe, 0xcf, 0x3e, 0xd7, 0x99, 0x3f, 0x86, 0x73, 0x8e, 0x36, 0x9e, 0x81, 0x3f, 0xf3, 0x04,
-    0xe3, 0xca, 0xbe, 0x70, 0x76, 0x6b, 0x36, 0xfe, 0xbf, 0xb5, 0xfe, 0x7f, 0x43, 0xfc, 0xe7, 0x9f, 0x65, 0xaf, 0x8c, 0xff,
-    0x9d, 0x75, 0x44, 0xf9, 0xf6, 0x51, 0xee, 0xd9, 0xff, 0x3b, 0xeb, 0xff, 0x9a, 0x35, 0xe7, 0x9f, 0x16, 0xff, 0x33, 0xa3,
-    0x4c, 0x4f, 0x8d, 0xff, 0x8a, 0x18, 0xa9, 0x1a, 0xff, 0xaf, 0xbe, 0x03, 0xb4, 0xad, 0x99, 0xeb, 0x9f, 0x17, 0xe1, 0xe3,
-    0xf8, 0x8f, 0xbc, 0xff, 0xf3, 0xee, 0xf8, 0x9f, 0x7b, 0x37, 0xe6, 0xd9, 0xf1, 0xdf, 0x93, 0x2b, 0xd6, 0xbc, 0xf3, 0xf9,
-    0x70, 0x6c, 0xfd, 0xe9, 0xe7, 0xc5, 0x7f, 0xfc, 0xed, 0x8c, 0xb7, 0x3f, 0xff, 0xcb, 0xbd, 0x1d, 0xb0, 0xf7, 0xf9, 0xdf,
-    0x13, 0xde, 0x2b, 0xe1, 0x4d, 0xf7, 0x42, 0xe7, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xa0, 0x6a, 0x46, 0x46, 0x3c, 0x67, 0xcf, 0xdc, 0x4c, 0xb1, 0x6b, 0x72, 0xdf, 0x47, 0x32, 0x15, 0xcc, 0xec, 0x4f, 0x3c,
-    0xff, 0x41, 0x24, 0xdb, 0xfe, 0xd1, 0xb9, 0x89, 0xe6, 0x1c, 0xcf, 0xe4, 0x32, 0xda, 0x75, 0x8c, 0xeb, 0x59, 0xe1, 0xe7,
-    0x7f, 0x61, 0x2e, 0xf7, 0x63, 0x2c, 0x2e, 0xee, 0x71, 0xae, 0x33, 0xf3, 0xee, 0x76, 0x67, 0x8a, 0xef, 0xc9, 0x5c, 0xef,
-    0xf5, 0x7b, 0x95, 0xcd, 0xd3, 0x1f, 0xdb, 0xe7, 0x48, 0x09, 0xf7, 0xc4, 0x4a, 0x2c, 0x6d, 0xe2, 0x4a, 0xaa, 0xce, 0xef,
-    0x93, 0x59, 0x99, 0x63, 0xfe, 0x8e, 0x5c, 0x99, 0xb3, 0xbf, 0x25, 0xf2, 0xf5, 0xdf, 0xe5, 0x5c, 0xdf, 0xb5, 0x65, 0x71,
-    0x9c, 0xf1, 0xe8, 0xbe, 0xad, 0xa4, 0x1e, 0xca, 0x6e, 0xfa, 0x9c, 0x7c, 0x04, 0xb1, 0x3a, 0xf7, 0xbd, 0xed, 0xe2, 0xf7,
-    0x1f, 0x73, 0x26, 0xb7, 0xc0, 0x4c, 0x1d, 0xde, 0x02, 0xf1, 0x7f, 0xc7, 0xfc, 0x55, 0xb9, 0xcc, 0x57, 0x33, 0xa5, 0x78,
-    0xd6, 0xb6, 0x23, 0xbf, 0x07, 0x56, 0x62, 0xbb, 0xf2, 0x18, 0xf7, 0xe7, 0xec, 0x38, 0xce, 0x26, 0xf2, 0xe4, 0x73, 0xdd,
-    0xb6, 0xe7, 0x82, 0x89, 0xd6, 0xff, 0x4f, 0x8d, 0xff, 0xd1, 0x6a, 0x04, 0x9f, 0xd7, 0x25, 0x3b, 0x6f, 0x5b, 0xc5, 0x5a,
-    0x06, 0xab, 0x3d, 0xa0, 0x1d, 0xdb, 0xae, 0xcc, 0xd9, 0x75, 0xf6, 0xb1, 0xc6, 0xcb, 0xe8, 0xca, 0xf8, 0x9f, 0xe9, 0xd7,
-    0x7d, 0x5b, 0xfd, 0xdf, 0x2f, 0xdf, 0x36, 0x7f, 0x17, 0xbb, 0xfb, 0xb5, 0xbd, 0x7f, 0xcd, 0x8e, 0x27, 0xc4, 0xf8, 0x5d,
-    0xe3, 0xff, 0xaa, 0x8c, 0xc7, 0x35, 0x2b, 0x2d, 0x45, 0xfb, 0xff, 0x4f, 0xb8, 0x26, 0x7a, 0x72, 0x2d, 0xe3, 0xd8, 0x0a,
-    0xce, 0xd5, 0xdb, 0xc4, 0xff, 0xdd, 0xeb, 0xff, 0x99, 0xb1, 0x95, 0xfb, 0xe5, 0xaf, 0xcc, 0xae, 0xd3, 0xfb, 0xee, 0xeb,
-    0xa5, 0x85, 0x9e, 0x79, 0xed, 0xd8, 0x26, 0xfe, 0xc5, 0xbf, 0xf8, 0xbf, 0xe2, 0x7a, 0xe9, 0x5f, 0xd1, 0xff, 0x17, 0xff,
-    0xe2, 0x3f, 0xf2, 0xcc, 0xec, 0xfe, 0xfd, 0xff, 0x5d, 0x4f, 0x2a, 0xdf, 0x36, 0xfe, 0x1f, 0x7d, 0xda, 0xfb, 0xa4, 0xf1,
-    0xff, 0xdd, 0xcf, 0xff, 0xae, 0xf8, 0xdd, 0xdc, 0x3e, 0xbf, 0xff, 0x19, 0xf7, 0x3b, 0xf2, 0xc5, 0xf3, 0xfc, 0xeb, 0x50,
-    0x19, 0x80, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe7, 0x00, 0xaf, 0xbe,
-    0xa7, 0x7f, 0x66, 0xbe, 0xf7, 0xc8, 0x9c, 0xcf, 0xb9, 0xef, 0x04, 0xf1, 0x1f, 0x9b, 0xa7, 0x73, 0x66, 0xbe, 0xf7, 0xfc,
-    0xdc, 0x7c, 0xe7, 0x1a, 0xde, 0x37, 0x53, 0x5c, 0x6c, 0x43, 0x2e, 0xc2, 0xa3, 0x39, 0x3a, 0xef, 0xb2, 0xed, 0x1d, 0x2b,
-    0x73, 0xc0, 0xbd, 0x6a, 0xd5, 0xa7, 0xc4, 0x7f, 0xdb, 0x96, 0xef, 0x12, 0xbe, 0xb1, 0xff, 0x2f, 0xfe, 0x41, 0xfc, 0x8b,
-    0x7f, 0xd0, 0xfe, 0x17, 0xff, 0x60, 0xfc, 0xff, 0xaa, 0x58, 0x9d, 0x59, 0x27, 0xe6, 0xc9, 0x4f, 0x31, 0xe0, 0x7e, 0x77,
-    0x80, 0xf8, 0xdd, 0xa1, 0x7a, 0x5b, 0x1b, 0xde, 0x37, 0x3c, 0x23, 0x84, 0xb3, 0xe2, 0xff, 0x9a, 0x51, 0x8a, 0xd5, 0xf8,
-    0xd7, 0xfe, 0x87, 0x37, 0xf5, 0x46, 0x22, 0xef, 0xff, 0x89, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xdd, 0x5b, 0x6e, 0xe4, 0x36, 0x10, 0x05, 0x50, 0xee, 0x7f, 0xa1, 0xd9, 0x06, 0x83, 0x20, 0x18, 0x24, 0xe3, 0xb1, 0x45,
+    0xb2, 0xaa, 0xa8, 0x57, 0x9f, 0x1c, 0xe4, 0xc7, 0x1a, 0xb7, 0xd5, 0x94, 0xae, 0x44, 0x3d, 0x58, 0xec, 0x0d, 0x00, 0x00,
+    0x00, 0xf8, 0x78, 0xff, 0xfc, 0xf7, 0xfd, 0xcf, 0xbe, 0x5b, 0x72, 0xbc, 0xec, 0xd7, 0xbf, 0x18, 0x2d, 0xe9, 0x3f, 0xae,
+    0x49, 0x3f, 0x58, 0x36, 0xf7, 0xb7, 0x62, 0xeb, 0xd5, 0x97, 0xda, 0xa6, 0x1f, 0xb4, 0x5a, 0x4b, 0xff, 0xbc, 0x1d, 0xb6,
+    0x7d, 0x0f, 0xb4, 0x5e, 0x3b, 0x5c, 0xe7, 0x9f, 0x7f, 0x73, 0xbc, 0xa4, 0x72, 0x3d, 0x57, 0xda, 0xb4, 0x72, 0xfb, 0xac,
+    0xb4, 0x60, 0x5b, 0x5e, 0x7a, 0xdc, 0x52, 0x6d, 0xaa, 0x1d, 0xc7, 0x9f, 0xdc, 0x6f, 0x92, 0xff, 0xe3, 0x6f, 0xf3, 0x2b,
+    0x83, 0x47, 0x5b, 0x7f, 0x7e, 0xdf, 0x68, 0xd3, 0xc7, 0x84, 0xff, 0xfe, 0x76, 0x5d, 0x42, 0xb3, 0x9f, 0xd2, 0x87, 0x47,
+    0xc2, 0x95, 0x16, 0x3a, 0x6e, 0xbd, 0x16, 0xca, 0xc4, 0xe8, 0x33, 0x23, 0xeb, 0x32, 0xb3, 0x6c, 0x6e, 0xfd, 0x62, 0xfb,
+    0xfc, 0xf8, 0xf3, 0x8e, 0xf7, 0xb4, 0xa3, 0x2d, 0x3c, 0xda, 0x73, 0x8f, 0xbf, 0xdf, 0xfa, 0x56, 0xcd, 0xa4, 0x7e, 0x4f,
+    0xfe, 0xfb, 0xff, 0x72, 0xd6, 0x83, 0x19, 0xcc, 0x9f, 0x1b, 0xd6, 0xcf, 0x0a, 0xc7, 0xc7, 0xac, 0x7d, 0x2d, 0x7c, 0xd4,
+    0x4e, 0x3d, 0xb0, 0x0f, 0xaf, 0x6f, 0xb3, 0xe8, 0xdf, 0xd9, 0x91, 0xff, 0x8a, 0x14, 0xb7, 0x89, 0x33, 0x62, 0xa4, 0x4f,
+    0x71, 0xdc, 0xe3, 0xcc, 0x27, 0x7c, 0xb4, 0x2e, 0x91, 0xfe, 0x4c, 0xbe, 0x2d, 0x57, 0xfb, 0x1c, 0xfb, 0xf2, 0x5f, 0x71,
+    0xbe, 0x5d, 0x3f, 0x97, 0xec, 0x6b, 0xdf, 0x99, 0xf3, 0xf5, 0xfd, 0xf3, 0xbf, 0xfe, 0xdd, 0x2a, 0xf2, 0x5f, 0xb3, 0x55,
+    0xd6, 0xf3, 0xdf, 0xe5, 0x7f, 0xfa, 0xfc, 0xff, 0x73, 0xab, 0xf7, 0xe0, 0xf5, 0x57, 0xfe, 0xaa, 0xbd, 0xaa, 0x9f, 0x7f,
+    0x6d, 0xfe, 0x47, 0xd7, 0x83, 0x6d, 0x39, 0xff, 0xb1, 0xab, 0xcf, 0xc8, 0xd5, 0x46, 0x55, 0xff, 0x7f, 0x6f, 0xef, 0xe1,
+    0xf8, 0xea, 0x31, 0x92, 0xf0, 0xb5, 0x7b, 0x1e, 0x35, 0x9f, 0x7c, 0x45, 0xfe, 0xdb, 0xc4, 0xda, 0xf4, 0xe1, 0xf5, 0xff,
+    0xec, 0x11, 0x7e, 0x2d, 0xff, 0x75, 0xe7, 0x8e, 0x2b, 0xf3, 0xdf, 0x83, 0x77, 0xc4, 0xaa, 0xaf, 0xe3, 0xe3, 0xdf, 0x6d,
+    0xed, 0xfe, 0xdc, 0x79, 0xd7, 0xff, 0xa3, 0x7e, 0x60, 0x26, 0xff, 0x9f, 0xd2, 0xff, 0x9f, 0xe9, 0x69, 0x8f, 0x5a, 0x71,
+    0xfe, 0x78, 0x76, 0xc5, 0xd9, 0xfc, 0xda, 0xfc, 0xf7, 0xa9, 0x7b, 0xd8, 0xf7, 0xce, 0x7f, 0x5b, 0xbe, 0xca, 0xab, 0xdc,
+    0x77, 0xa3, 0x7b, 0xd4, 0x68, 0xcf, 0x9d, 0x39, 0xaa, 0xdd, 0x33, 0xff, 0xb1, 0xfd, 0xb9, 0x0f, 0x7b, 0xf9, 0x3d, 0x91,
+    0xff, 0xf6, 0x9a, 0xfc, 0xf7, 0xe0, 0xdd, 0x8e, 0x9f, 0xfb, 0x7b, 0xb1, 0xfb, 0x71, 0x91, 0x9e, 0x7c, 0xa6, 0x8f, 0x52,
+    0x95, 0xff, 0x7b, 0x6c, 0x49, 0xe7, 0xff, 0x8a, 0xfb, 0xff, 0xed, 0xb0, 0x87, 0xff, 0x59, 0xf9, 0x3f, 0x7e, 0xfe, 0xd7,
+    0x6e, 0xff, 0xac, 0x2e, 0x9a, 0xff, 0x9a, 0xfb, 0xff, 0xe7, 0x1f, 0xad, 0x67, 0xae, 0x5c, 0x73, 0xf9, 0xbf, 0xd3, 0xf3,
+    0xbf, 0xbd, 0xf9, 0x1f, 0x3d, 0xf5, 0x7e, 0x53, 0xff, 0xbf, 0xf2, 0x3e, 0x7e, 0xfc, 0x9d, 0x9b, 0x7d, 0xef, 0x14, 0xb5,
+    0xd0, 0x9b, 0x5c, 0xf7, 0x4c, 0x7f, 0xf4, 0x39, 0x50, 0xee, 0x2e, 0xdd, 0xd5, 0xef, 0xff, 0xd4, 0xb7, 0xe0, 0x79, 0xeb,
+    0xd3, 0x6f, 0xdc, 0x67, 0xe4, 0x5d, 0x6f, 0xc9, 0xda, 0xbb, 0xce, 0x7c, 0x3e, 0x1b, 0xff, 0x5b, 0xb6, 0x0f, 0x3b, 0xcf,
+    0x6c, 0x5a, 0xe2, 0x19, 0xf7, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc9, 0xe3,
+    0x17, 0x22, 0x63, 0x0d, 0xa3, 0x75, 0x08, 0x6a, 0xeb, 0x21, 0xf4, 0x2f, 0x75, 0x58, 0x57, 0x3f, 0x7b, 0x76, 0xac, 0x65,
+    0x66, 0x9c, 0x66, 0xed, 0xef, 0x56, 0x55, 0x96, 0xa8, 0x1d, 0x79, 0x3e, 0xae, 0x77, 0xb3, 0x5a, 0xad, 0x39, 0x56, 0x05,
+    0x6d, 0xbd, 0xa6, 0x79, 0xa6, 0x0e, 0x5f, 0xf5, 0xd8, 0xea, 0x9a, 0xfa, 0x68, 0xf3, 0x63, 0xde, 0x6b, 0x2a, 0x8d, 0xd4,
+    0xe5, 0x7f, 0x7d, 0x3b, 0xf4, 0x2f, 0xeb, 0xd1, 0x2f, 0xa8, 0xc4, 0x50, 0xbf, 0x74, 0xee, 0xe7, 0x77, 0xca, 0xff, 0xec,
+    0x31, 0x38, 0xbb, 0xf7, 0xc6, 0x47, 0xdc, 0x8d, 0xaa, 0x4f, 0x5c, 0x5f, 0x5b, 0xa5, 0x15, 0xd5, 0x47, 0xad, 0xcf, 0x7f,
+    0x3b, 0xe9, 0xfc, 0x1f, 0xcd, 0x7f, 0x1f, 0xce, 0x66, 0x73, 0xdf, 0xfc, 0xaf, 0xd7, 0x9d, 0xc8, 0xfe, 0xd5, 0xdc, 0x4c,
+    0x0e, 0xf1, 0x5a, 0xe3, 0xf9, 0xfc, 0xe7, 0x2a, 0x05, 0x9d, 0x7b, 0x6e, 0xbd, 0x3e, 0xff, 0xd1, 0x16, 0xb9, 0x2a, 0xff,
+    0xf1, 0xbd, 0xa4, 0x0f, 0xab, 0x59, 0xdf, 0x31, 0xff, 0xfd, 0xe3, 0xf3, 0x5f, 0x5f, 0xcb, 0x37, 0x9a, 0x80, 0x48, 0x1d,
+    0xf4, 0x99, 0xeb, 0xea, 0xd5, 0x2b, 0xbb, 0xf8, 0x75, 0x57, 0xf4, 0xfc, 0x3f, 0xfa, 0xfd, 0x99, 0x6f, 0x72, 0x75, 0xfe,
+    0xdb, 0x30, 0xe1, 0x3d, 0x91, 0xd2, 0xf8, 0x1d, 0x90, 0x4c, 0xcd, 0xab, 0x6b, 0xf2, 0xdf, 0x42, 0x33, 0x06, 0xc6, 0xcf,
+    0xf3, 0x77, 0xcf, 0x7f, 0xf5, 0xb5, 0x41, 0xf4, 0x8e, 0x41, 0xf5, 0x3a, 0xde, 0xef, 0xfa, 0xbf, 0x17, 0xf4, 0x12, 0x77,
+    0xe4, 0x3f, 0xdb, 0x3e, 0xd1, 0x34, 0xc5, 0xf2, 0x3f, 0x7f, 0x76, 0x8f, 0xcd, 0x26, 0x10, 0x5b, 0xf6, 0xde, 0xfc, 0xd7,
+    0xd6, 0x4f, 0x5d, 0xbf, 0x57, 0xfc, 0xa6, 0xfc, 0x47, 0xaf, 0xff, 0xe7, 0xaf, 0xb8, 0xee, 0x95, 0xff, 0x36, 0x31, 0x3f,
+    0xcf, 0xd9, 0xf9, 0x1f, 0x55, 0x5b, 0x7d, 0x5b, 0xfe, 0xa3, 0xd9, 0x8a, 0xce, 0x67, 0xd4, 0xc2, 0x67, 0xba, 0xb7, 0x9f,
+    0xff, 0xdb, 0x4b, 0xf3, 0x1f, 0x9f, 0x5f, 0xe6, 0x59, 0xf9, 0xaf, 0xee, 0x35, 0x5c, 0x5f, 0x57, 0x2f, 0x7a, 0xb7, 0x31,
+    0x7e, 0x97, 0xf2, 0xdd, 0xfd, 0xff, 0xbe, 0xa1, 0x17, 0x9e, 0x7d, 0xfe, 0x7f, 0x55, 0xfe, 0x77, 0x3d, 0xff, 0x6b, 0xd3,
+    0x73, 0xfe, 0xed, 0x5a, 0x2b, 0xf9, 0xaf, 0xce, 0x7f, 0xe5, 0xdc, 0x89, 0x6b, 0xff, 0xa2, 0xfa, 0xfd, 0x9f, 0xfa, 0x3b,
+    0x69, 0xfb, 0xdf, 0xff, 0xd9, 0xd3, 0x3e, 0x6d, 0x6a, 0xc6, 0xb4, 0xf5, 0xbf, 0xda, 0x26, 0x67, 0x35, 0x8e, 0xad, 0x73,
+    0xfd, 0xdb, 0x37, 0x77, 0xcf, 0x7f, 0xec, 0x6d, 0x83, 0xe8, 0x6f, 0xc6, 0x7f, 0xe3, 0x3e, 0x73, 0x81, 0x7d, 0xe6, 0x5b,
+    0x94, 0x5a, 0xa1, 0xba, 0x85, 0xb4, 0x2a, 0x8e, 0x00, 0xef, 0x7a, 0xcf, 0x7c, 0xd7, 0xbf, 0x07, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xbc, 0xd1, 0x2b, 0xb3, 0x35, 0xb7, 0xff, 0xfc, 0x69, 0x0f, 0x8d, 0x77,
+    0xed, 0xc9, 0xdf, 0xeb, 0xcb, 0x75, 0x30, 0xd6, 0xc6, 0x9d, 0x56, 0xd4, 0x60, 0xa8, 0xae, 0x23, 0x35, 0x6e, 0x97, 0xd9,
+    0x2d, 0x3d, 0x1e, 0x49, 0xbc, 0xba, 0x87, 0x44, 0xc6, 0xf5, 0x64, 0x2a, 0xea, 0x64, 0xe6, 0x12, 0x88, 0x57, 0x42, 0x6c,
+    0x8b, 0x9f, 0x1a, 0xdb, 0x6a, 0x91, 0xf1, 0xe1, 0x3b, 0xf3, 0x1f, 0xab, 0x2d, 0x79, 0x45, 0xa5, 0xb5, 0xf8, 0xde, 0x54,
+    0xbb, 0x86, 0xf5, 0xad, 0x32, 0xb7, 0xb6, 0xb9, 0x9f, 0x67, 0xc6, 0xf4, 0xe7, 0x6a, 0x0f, 0xac, 0xb6, 0x44, 0x9f, 0xaa,
+    0x96, 0xb2, 0xfa, 0x9b, 0x3f, 0xff, 0xde, 0xf1, 0xb2, 0x3e, 0x55, 0x39, 0x62, 0xed, 0x53, 0xbf, 0xff, 0xb7, 0xfb, 0x2a,
+    0x27, 0xbc, 0x23, 0xff, 0x2d, 0x70, 0x94, 0x3d, 0x37, 0xff, 0xf1, 0xe3, 0x54, 0x2b, 0xac, 0xd2, 0xb3, 0x3e, 0x37, 0x46,
+    0x64, 0xe4, 0x6e, 0xbe, 0x46, 0xd0, 0x19, 0x15, 0x35, 0xfe, 0xcc, 0xd7, 0xca, 0x56, 0x9b, 0x4b, 0xff, 0xf7, 0xc7, 0xb8,
+    0x76, 0xd0, 0xdb, 0x9a, 0xcd, 0x7f, 0x0f, 0xd4, 0x1e, 0xa9, 0xa9, 0xc8, 0xf7, 0x9c, 0xfc, 0xcf, 0x5c, 0x6d, 0x9c, 0x97,
+    0xff, 0xf1, 0xcf, 0xeb, 0xaa, 0x6a, 0x57, 0xd6, 0xda, 0x19, 0xd5, 0x5a, 0x6e, 0xa1, 0x2d, 0xd2, 0x83, 0xc7, 0x95, 0x5e,
+    0x5c, 0xcf, 0xbc, 0x7f, 0xf9, 0x7f, 0xf6, 0x4c, 0x3d, 0x9b, 0xff, 0xd5, 0xbf, 0x78, 0x4d, 0xff, 0x7f, 0x65, 0x7f, 0x79,
+    0x52, 0xfe, 0xdb, 0x54, 0x2d, 0xb2, 0x7b, 0x9f, 0xff, 0x2b, 0x6b, 0x6a, 0xd5, 0xe7, 0x3f, 0x96, 0xf0, 0xc8, 0xfe, 0x3e,
+    0x3f, 0xe3, 0xc1, 0xda, 0x76, 0x1b, 0xf7, 0xf2, 0x7b, 0xa2, 0xff, 0x9f, 0xcf, 0x7f, 0x5d, 0x9f, 0x67, 0xed, 0xea, 0xf9,
+    0x8a, 0xfc, 0xf7, 0x44, 0x65, 0xf4, 0xfd, 0xd7, 0xff, 0xa3, 0x7e, 0x62, 0xb4, 0x55, 0xea, 0xe7, 0xa6, 0xaa, 0xac, 0xb6,
+    0xbd, 0x9a, 0xff, 0xcc, 0xf9, 0x3f, 0x7a, 0x1f, 0x22, 0xd3, 0x43, 0x5c, 0xbb, 0x1e, 0xcf, 0x1f, 0x01, 0xd6, 0xfa, 0xff,
+    0x67, 0xdc, 0xff, 0xef, 0xa7, 0x5d, 0xff, 0xb7, 0xd4, 0x2c, 0x46, 0x95, 0xfd, 0xff, 0x96, 0xe8, 0x65, 0x66, 0x8e, 0x36,
+    0x91, 0xd6, 0x5e, 0x6f, 0xb3, 0x2b, 0xcf, 0xff, 0xb9, 0xd9, 0xe6, 0xda, 0xb6, 0x7a, 0xa7, 0x91, 0xfe, 0xff, 0x9e, 0x14,
+    0x56, 0xfc, 0xc5, 0xca, 0xeb, 0xff, 0xeb, 0xe7, 0x66, 0xc8, 0xe4, 0xff, 0xac, 0xde, 0xd3, 0xfe, 0x6f, 0x5e, 0x5b, 0x47,
+    0xfa, 0x0e, 0xd7, 0xff, 0x91, 0xfb, 0xff, 0xd1, 0x9a, 0xc7, 0xe3, 0xb3, 0x71, 0xff, 0xf1, 0x0a, 0xff, 0xcc, 0xb3, 0xf1,
+    0xf9, 0x47, 0x9c, 0xda, 0xeb, 0x7f, 0xf9, 0xdf, 0xf3, 0xdd, 0x2b, 0x6b, 0xe7, 0xee, 0xce, 0xff, 0x5c, 0xcf, 0x2a, 0x36,
+    0x23, 0x62, 0x6e, 0xc6, 0xe4, 0xe8, 0x35, 0x5d, 0x3b, 0x75, 0x4f, 0x6f, 0x17, 0xf4, 0xfe, 0xab, 0x67, 0xe4, 0x79, 0xe7,
+    0xfb, 0x50, 0xef, 0xf8, 0x06, 0x67, 0x1c, 0xcf, 0x7b, 0x61, 0xcf, 0x34, 0x7f, 0xff, 0x2f, 0x7e, 0xb4, 0x6a, 0x17, 0xd4,
+    0x20, 0x3e, 0xfb, 0x2f, 0xc6, 0x9f, 0x56, 0x20, 0xff, 0x6b, 0xf9, 0xdf, 0xdf, 0x4b, 0xaa, 0x7a, 0x87, 0xe1, 0xd3, 0xf6,
+    0x14, 0x2d, 0xf3, 0x29, 0xdb, 0x75, 0xfd, 0x4e, 0xef, 0x5b, 0x5a, 0xc2, 0x3e, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x6b, 0xf4, 0x4f, 0xac, 0x9a, 0xc2, 0x6c, 0x2d, 0xab, 0xbe, 0x30, 0x3a, 0xb3, 0x0d,
+    0xeb, 0x33, 0x67, 0x3f, 0xe7, 0xeb, 0xcc, 0x05, 0x7d, 0xf0, 0x2f, 0x5a, 0x60, 0x4d, 0x7b, 0x78, 0xac, 0xf1, 0xfa, 0x3c,
+    0x0c, 0x5f, 0xd7, 0xb8, 0x25, 0x5a, 0x64, 0x3c, 0x92, 0x77, 0x7d, 0xc6, 0x86, 0xbd, 0xdf, 0xb9, 0xa6, 0x22, 0x7a, 0xa4,
+    0xb6, 0xdf, 0x6c, 0x85, 0x92, 0xb3, 0x5b, 0x25, 0x33, 0x62, 0xbb, 0x05, 0x8e, 0x0d, 0xa3, 0x7a, 0x42, 0x2b, 0xb5, 0xba,
+    0xd6, 0x8e, 0x4f, 0x73, 0x55, 0xf8, 0xd6, 0xc7, 0xa5, 0xcf, 0xd5, 0xf0, 0x6c, 0x1b, 0xeb, 0x5e, 0xaf, 0xef, 0xed, 0xf1,
+    0xa4, 0xe4, 0x96, 0xce, 0x8d, 0xc7, 0xdf, 0xf1, 0x9d, 0xe3, 0x35, 0x6b, 0xe7, 0x3f, 0x27, 0x73, 0xf6, 0x1b, 0xa7, 0x74,
+    0x4f, 0xab, 0xc4, 0xd2, 0x9f, 0xa9, 0x08, 0xb3, 0x9e, 0xff, 0x9a, 0xf5, 0x1e, 0xcf, 0x89, 0x10, 0x9b, 0x65, 0x63, 0xa6,
+    0x86, 0xf7, 0xfb, 0xaa, 0x07, 0xe4, 0xf3, 0xff, 0xa9, 0x35, 0x17, 0x9e, 0xd4, 0x02, 0x7d, 0x31, 0x61, 0x33, 0xbd, 0xf8,
+    0x9a, 0xfc, 0x8f, 0xe7, 0x4a, 0x3a, 0x3f, 0xff, 0x3d, 0xf4, 0x09, 0xf1, 0x9e, 0xdb, 0x1d, 0x97, 0xe6, 0xf2, 0x3f, 0xde,
+    0x83, 0xf6, 0x2c, 0xad, 0x9a, 0x29, 0x29, 0x57, 0x87, 0xbc, 0x05, 0xaf, 0x1e, 0x32, 0x4b, 0x23, 0xb5, 0x11, 0x7a, 0xe8,
+    0x6a, 0x64, 0xcf, 0xf9, 0x3f, 0x7f, 0xfd, 0x3f, 0xdf, 0x6f, 0x8b, 0xdf, 0x3f, 0xc8, 0xec, 0x4b, 0x3b, 0xfa, 0xe1, 0x67,
+    0x2c, 0x8d, 0xd4, 0x86, 0xbb, 0x6a, 0xe9, 0xee, 0xfc, 0xf7, 0xdf, 0xe6, 0x4a, 0x3c, 0x4a, 0xd4, 0xfa, 0xd1, 0x21, 0x77,
+    0x64, 0xd9, 0xdb, 0xd7, 0x59, 0xbd, 0xff, 0xb7, 0xba, 0x96, 0x75, 0x73, 0x99, 0x45, 0xab, 0x52, 0x7e, 0xfd, 0xfd, 0xba,
+    0xf3, 0xff, 0xbe, 0xad, 0xbe, 0x77, 0x69, 0xe6, 0xee, 0xde, 0x5b, 0xf3, 0x3f, 0xd7, 0x6e, 0xef, 0xc8, 0xff, 0xb8, 0x7e,
+    0x6b, 0x2f, 0xba, 0x4f, 0x71, 0xc6, 0x37, 0x9a, 0xad, 0xc0, 0x9f, 0x9b, 0x63, 0xf6, 0x4d, 0xf9, 0x6f, 0x45, 0xfd, 0xff,
+    0x33, 0x97, 0xde, 0xa3, 0xff, 0xdf, 0x5f, 0x90, 0xff, 0xec, 0x73, 0x9d, 0x8a, 0x19, 0x8e, 0xcf, 0xce, 0x7f, 0xf6, 0x98,
+    0x78, 0xaf, 0x6b, 0xe1, 0xec, 0xd2, 0x6c, 0xfe, 0x3f, 0xf5, 0xfc, 0xff, 0x86, 0xfc, 0xe7, 0x9f, 0x65, 0xaf, 0xdc, 0xff,
+    0x3b, 0xef, 0x1d, 0x88, 0xbe, 0x31, 0xff, 0xed, 0x43, 0xcf, 0xff, 0x35, 0xf3, 0xca, 0x3f, 0x2d, 0xff, 0x33, 0x77, 0x99,
+    0x9e, 0x9a, 0xff, 0x8a, 0x8c, 0x54, 0xdd, 0xff, 0xaf, 0x7f, 0x0b, 0x6a, 0x67, 0x75, 0xfa, 0xe7, 0x25, 0x7c, 0x9c, 0xff,
+    0xc8, 0xfb, 0x3f, 0xef, 0xce, 0xff, 0xdc, 0xbb, 0x31, 0xcf, 0xce, 0x7f, 0x4f, 0xce, 0x4a, 0xf3, 0xce, 0xe7, 0xc3, 0xb1,
+    0x39, 0xa6, 0x9f, 0x97, 0xff, 0xfc, 0x3b, 0x65, 0x6f, 0x7d, 0xfe, 0x97, 0x7b, 0x3b, 0xe0, 0x9a, 0xe7, 0x7f, 0x77, 0x7a,
+    0xaf, 0x84, 0xb7, 0xbf, 0x55, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x66, 0xec,
+    0xeb, 0xea, 0x88, 0x9d, 0xb9, 0x71, 0x62, 0xd7, 0x54, 0xbe, 0x8f, 0xd4, 0x29, 0x98, 0x59, 0x9f, 0x78, 0xf5, 0x83, 0x48,
+    0xad, 0xfd, 0xa3, 0x6d, 0x13, 0xad, 0x38, 0x9e, 0xa9, 0x64, 0xb4, 0xeb, 0x3b, 0xae, 0xd7, 0x84, 0x9f, 0xff, 0x0b, 0x73,
+    0x95, 0x1f, 0x63, 0xb9, 0xb8, 0xfb, 0xb6, 0x9e, 0x1b, 0xa5, 0xb9, 0xb3, 0x4e, 0x7c, 0x4f, 0xd6, 0x34, 0xaf, 0x5f, 0xab,
+    0x6c, 0x95, 0xfe, 0xd8, 0x3a, 0x47, 0x5a, 0xb8, 0x27, 0xe6, 0x61, 0x69, 0x13, 0x7b, 0x52, 0x75, 0x75, 0x9f, 0xcc, 0xbc,
+    0x1c, 0xf3, 0x47, 0xe4, 0xca, 0x8a, 0xfd, 0x2d, 0x51, 0xad, 0xff, 0x2e, 0xdb, 0xfa, 0xda, 0xf1, 0xb4, 0xe3, 0x75, 0x7e,
+    0xce, 0x78, 0xdf, 0xb9, 0xd1, 0xe3, 0xcf, 0xf8, 0x36, 0x33, 0xc7, 0xb2, 0xf5, 0xea, 0x3c, 0xef, 0xac, 0xd9, 0xf0, 0xd9,
+    0x63, 0x95, 0xb3, 0x95, 0x05, 0x46, 0xc7, 0xba, 0xe7, 0x54, 0xaf, 0xca, 0xd5, 0xbd, 0x9a, 0x69, 0xc5, 0xb3, 0x96, 0x1d,
+    0xf9, 0x2b, 0x30, 0x0f, 0xdb, 0x95, 0xdf, 0x71, 0x7f, 0xc5, 0x8e, 0xe3, 0x5a, 0x22, 0xf7, 0xde, 0xd6, 0xd9, 0x1a, 0x59,
+    0xd9, 0x4a, 0x30, 0xd1, 0xf3, 0xff, 0x53, 0xf3, 0x3f, 0x9a, 0x8b, 0xe0, 0xe7, 0x59, 0xc9, 0xce, 0x5b, 0x56, 0x31, 0x93,
+    0xc1, 0xea, 0x15, 0xd0, 0x8e, 0x65, 0x57, 0x56, 0xec, 0x3a, 0xfb, 0xbb, 0xc6, 0xdb, 0xa8, 0xaa, 0x9f, 0x18, 0x9d, 0xb3,
+    0x64, 0xb4, 0xec, 0xd3, 0xce, 0xff, 0xfd, 0xf2, 0x65, 0xf3, 0x47, 0xb1, 0xbb, 0xef, 0xdb, 0xfb, 0x67, 0xec, 0x78, 0x42,
+    0xc6, 0x33, 0xb3, 0x4b, 0xed, 0xcc, 0xff, 0x3d, 0xeb, 0x1d, 0xf7, 0xd4, 0xb9, 0x31, 0x5b, 0xab, 0xef, 0x3e, 0xb9, 0xc9,
+    0xcc, 0x64, 0x1c, 0x9b, 0xbf, 0xb9, 0x7a, 0x99, 0xfc, 0xdf, 0x3b, 0xff, 0x73, 0xf7, 0x56, 0xee, 0x57, 0xbd, 0x32, 0x3b,
+    0x4b, 0xef, 0xfb, 0xfb, 0x8b, 0x7d, 0xc3, 0xd3, 0x9a, 0xba, 0xe7, 0x89, 0xf2, 0x7f, 0x97, 0xfe, 0xbf, 0xfc, 0xbf, 0x6f,
+    0x7f, 0xe9, 0xa9, 0x4a, 0xe6, 0xfa, 0xff, 0xf2, 0xff, 0xe6, 0xfc, 0xf7, 0x47, 0x5c, 0xff, 0xef, 0xda, 0x5f, 0xde, 0x76,
+    0xff, 0x3f, 0xfa, 0xb4, 0xf7, 0x29, 0xcf, 0x7a, 0xce, 0x78, 0xfe, 0xb7, 0xe7, 0x93, 0x77, 0x56, 0x43, 0x7f, 0xff, 0xf3,
+    0xde, 0xa7, 0xd4, 0x83, 0xe7, 0xed, 0xfb, 0xa1, 0x36, 0x00, 0xf9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x76, 0xbe, 0xa9, 0x7f, 0x66, 0xc5, 0xf7, 0xc8, 0xa8, 0xcf, 0xb9, 0xcf, 0x04, 0x62, 0x23, 0x75, 0xce,
+    0xac, 0xf8, 0x9e, 0x1f, 0x9d, 0x6f, 0x5b, 0xc2, 0xfb, 0xc6, 0x8a, 0xcb, 0x36, 0xec, 0x49, 0x55, 0x7f, 0x48, 0x15, 0xa4,
+    0x77, 0xcc, 0xcd, 0x01, 0xf2, 0xff, 0x94, 0x0a, 0x69, 0x20, 0xff, 0xf2, 0x0f, 0xf2, 0x2f, 0xff, 0x20, 0xff, 0xf2, 0x0f,
+    0x6f, 0xbf, 0xff, 0x7f, 0xa7, 0x99, 0x6b, 0xf6, 0xd5, 0x29, 0x06, 0xcf, 0xfe, 0x63, 0x47, 0x87, 0xea, 0x65, 0x6d, 0x78,
+    0xdc, 0xf0, 0x8c, 0x10, 0xce, 0xca, 0xff, 0x35, 0xcf, 0xf8, 0x57, 0xf3, 0xaf, 0xff, 0x0f, 0x6f, 0xba, 0x1a, 0x89, 0xbc,
+    0xff, 0x27, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xe9, 0xaf, 0xff, 0x94, 0x03, 0x88, 0x7f, 0xe0, 0xeb, 0xe2, 0xff,
-    0x0f };
+    0x70, 0x95, 0x7f, 0xff, 0xd3, 0x0e, 0x20, 0xff, 0xc0, 0xc7, 0xe5, 0xff, 0x6f };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle enefeteFontRecs[189] = {
@@ -275,93 +273,93 @@
     { 429, 52, 7 , 6 },
     { 444, 52, 6 , 3 },
     { 458, 52, 7 , 10 },
-    { 473, 52, 9 , 10 },
-    { 490, 52, 4 , 4 },
-    { 4, 76, 6 , 7 },
-    { 18, 76, 4 , 5 },
-    { 30, 76, 4 , 5 },
-    { 42, 76, 7 , 11 },
-    { 57, 76, 6 , 9 },
-    { 71, 76, 7 , 12 },
-    { 86, 76, 2 , 2 },
-    { 96, 76, 6 , 10 },
-    { 110, 76, 3 , 5 },
-    { 121, 76, 4 , 5 },
-    { 133, 76, 7 , 6 },
-    { 148, 76, 9 , 10 },
-    { 165, 76, 8 , 7 },
+    { 473, 52, 0 , 0 },
+    { 481, 52, 4 , 4 },
+    { 493, 52, 6 , 7 },
+    { 4, 76, 4 , 5 },
+    { 16, 76, 4 , 5 },
+    { 28, 76, 7 , 11 },
+    { 43, 76, 6 , 9 },
+    { 57, 76, 7 , 12 },
+    { 72, 76, 2 , 2 },
+    { 82, 76, 6 , 10 },
+    { 96, 76, 3 , 5 },
+    { 107, 76, 4 , 5 },
+    { 119, 76, 7 , 6 },
+    { 134, 76, 9 , 10 },
+    { 151, 76, 8 , 7 },
+    { 167, 76, 6 , 11 },
     { 181, 76, 6 , 11 },
-    { 195, 76, 6 , 11 },
-    { 209, 76, 7 , 11 },
-    { 224, 76, 7 , 11 },
-    { 239, 76, 7 , 11 },
-    { 254, 76, 7 , 11 },
-    { 269, 76, 7 , 11 },
-    { 284, 76, 7 , 11 },
-    { 299, 76, 9 , 10 },
-    { 316, 76, 7 , 12 },
-    { 331, 76, 7 , 11 },
-    { 346, 76, 7 , 11 },
-    { 361, 76, 7 , 11 },
-    { 376, 76, 7 , 11 },
-    { 391, 76, 3 , 11 },
-    { 402, 76, 3 , 11 },
-    { 413, 76, 5 , 11 },
-    { 426, 76, 5 , 11 },
-    { 439, 76, 8 , 10 },
-    { 455, 76, 7 , 11 },
-    { 470, 76, 7 , 11 },
-    { 485, 76, 7 , 11 },
+    { 195, 76, 7 , 11 },
+    { 210, 76, 7 , 11 },
+    { 225, 76, 7 , 11 },
+    { 240, 76, 7 , 11 },
+    { 255, 76, 7 , 11 },
+    { 270, 76, 7 , 11 },
+    { 285, 76, 9 , 10 },
+    { 302, 76, 7 , 12 },
+    { 317, 76, 7 , 11 },
+    { 332, 76, 7 , 11 },
+    { 347, 76, 7 , 11 },
+    { 362, 76, 7 , 11 },
+    { 377, 76, 3 , 11 },
+    { 388, 76, 3 , 11 },
+    { 399, 76, 5 , 11 },
+    { 412, 76, 5 , 11 },
+    { 425, 76, 8 , 10 },
+    { 441, 76, 7 , 11 },
+    { 456, 76, 7 , 11 },
+    { 471, 76, 7 , 11 },
+    { 486, 76, 7 , 11 },
     { 4, 100, 7 , 11 },
     { 19, 100, 7 , 11 },
-    { 34, 100, 7 , 11 },
-    { 49, 100, 7 , 7 },
-    { 64, 100, 7 , 13 },
+    { 34, 100, 7 , 7 },
+    { 49, 100, 7 , 13 },
+    { 64, 100, 7 , 11 },
     { 79, 100, 7 , 11 },
     { 94, 100, 7 , 11 },
     { 109, 100, 7 , 11 },
-    { 124, 100, 7 , 11 },
-    { 139, 100, 6 , 11 },
+    { 124, 100, 6 , 11 },
+    { 138, 100, 7 , 10 },
     { 153, 100, 7 , 10 },
-    { 168, 100, 7 , 10 },
-    { 183, 100, 6 , 10 },
-    { 197, 100, 6 , 10 },
-    { 211, 100, 6 , 10 },
-    { 225, 100, 6 , 10 },
-    { 239, 100, 6 , 10 },
-    { 253, 100, 6 , 11 },
-    { 267, 100, 8 , 7 },
-    { 283, 100, 6 , 9 },
-    { 297, 100, 6 , 10 },
-    { 311, 100, 6 , 10 },
-    { 325, 100, 6 , 10 },
-    { 339, 100, 6 , 10 },
-    { 353, 100, 3 , 10 },
-    { 364, 100, 3 , 10 },
-    { 375, 100, 5 , 10 },
-    { 388, 100, 5 , 10 },
-    { 401, 100, 6 , 10 },
-    { 415, 100, 6 , 10 },
-    { 429, 100, 6 , 10 },
-    { 443, 100, 6 , 10 },
-    { 457, 100, 6 , 10 },
-    { 471, 100, 6 , 10 },
-    { 485, 100, 6 , 10 },
-    { 4, 124, 6 , 7 },
-    { 18, 124, 7 , 11 },
+    { 168, 100, 6 , 10 },
+    { 182, 100, 6 , 10 },
+    { 196, 100, 6 , 10 },
+    { 210, 100, 6 , 10 },
+    { 224, 100, 6 , 10 },
+    { 238, 100, 6 , 11 },
+    { 252, 100, 8 , 7 },
+    { 268, 100, 6 , 9 },
+    { 282, 100, 6 , 10 },
+    { 296, 100, 6 , 10 },
+    { 310, 100, 6 , 10 },
+    { 324, 100, 6 , 10 },
+    { 338, 100, 3 , 10 },
+    { 349, 100, 3 , 10 },
+    { 360, 100, 5 , 10 },
+    { 373, 100, 5 , 10 },
+    { 386, 100, 6 , 10 },
+    { 400, 100, 6 , 10 },
+    { 414, 100, 6 , 10 },
+    { 428, 100, 6 , 10 },
+    { 442, 100, 6 , 10 },
+    { 456, 100, 6 , 10 },
+    { 470, 100, 6 , 10 },
+    { 484, 100, 6 , 7 },
+    { 4, 124, 7 , 11 },
+    { 19, 124, 6 , 10 },
     { 33, 124, 6 , 10 },
     { 47, 124, 6 , 10 },
     { 61, 124, 6 , 10 },
-    { 75, 124, 6 , 10 },
+    { 75, 124, 6 , 12 },
     { 89, 124, 6 , 12 },
     { 103, 124, 6 , 12 },
-    { 117, 124, 6 , 12 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo enefeteFontGlyphs[189] = {
-    { 32, 0, 12, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 0, 2, 3, { 0 }},
     { 34, 0, 2, 6, { 0 }},
     { 35, 0, 2, 8, { 0 }},
@@ -469,7 +467,7 @@
     { 171, 0, 6, 8, { 0 }},
     { 172, 0, 7, 7, { 0 }},
     { 174, 0, 2, 8, { 0 }},
-    { 175, 1, 3, 14, { 0 }},
+    { 175, 0, 0, 0, { 0 }},
     { 176, 0, 2, 5, { 0 }},
     { 177, 0, 4, 7, { 0 }},
     { 178, 0, 2, 5, { 0 }},
diff --git a/raygui/styles/genesis/style_genesis.h b/raygui/styles/genesis/style_genesis.h
new file mode 100644
--- /dev/null
+++ b/raygui/styles/genesis/style_genesis.h
@@ -0,0 +1,589 @@
+//////////////////////////////////////////////////////////////////////////////////
+//                                                                              //
+// StyleAsCode exporter v2.0 - Style data exported as a values array            //
+//                                                                              //
+// USAGE: On init call: GuiLoadStyleGenesis();                                   //
+//                                                                              //
+// more info and bugs-report:  github.com/raysan5/raygui                        //
+// feedback and support:       ray[at]raylibtech.com                            //
+//                                                                              //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
+//                                                                              //
+//////////////////////////////////////////////////////////////////////////////////
+
+#define GENESIS_STYLE_PROPS_COUNT  23
+
+// Custom style name: Genesis
+static const GuiStyleProp genesisStyleProps[GENESIS_STYLE_PROPS_COUNT] = {
+    { 0, 0, (int)0x667384ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x181b1eff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xc2c8d0ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xd3dbdfff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xa7afb0ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0x020202ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x181b1eff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xac3c3cff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xdededeff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x3e4550ff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x2e353dff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x484f57ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x96a3b4ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x292c33ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 1, 5, (int)0x97a9aeff },    // LABEL_TEXT_COLOR_FOCUSED 
+    { 4, 5, (int)0xa69a9aff },    // SLIDER_TEXT_COLOR_FOCUSED 
+    { 4, 6, (int)0xc3ccd5ff },    // SLIDER_BORDER_COLOR_PRESSED 
+    { 6, 6, (int)0xa7aeb5ff },    // CHECKBOX_BORDER_COLOR_PRESSED 
+    { 9, 5, (int)0xa9a5a5ff },    // TEXTBOX_TEXT_COLOR_FOCUSED 
+    { 10, 5, (int)0xc9c7c7ff },    // VALUEBOX_TEXT_COLOR_FOCUSED 
+};
+
+// WARNING: This style uses a custom font: "PixelOperator.ttf" (size: 16, spacing: 0)
+
+#define GENESIS_STYLE_FONT_ATLAS_COMP_SIZE 2138
+
+// Font atlas image pixels data: DEFLATE compressed
+static unsigned char genesisFontData[GENESIS_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
+    0xdd, 0xdb, 0x92, 0xa5, 0xba, 0x0d, 0x00, 0x50, 0xff, 0xff, 0x4f, 0x2b, 0x0f, 0xa9, 0x54, 0x32, 0x95, 0xd3, 0x80, 0x64,
+    0xd9, 0x98, 0x9e, 0x35, 0xeb, 0xad, 0x77, 0x4f, 0xc3, 0x36, 0x96, 0x6f, 0x80, 0x1c, 0x03, 0x00, 0x00, 0x00, 0xf8, 0xeb,
+    0xc5, 0x3f, 0xfe, 0x24, 0x7e, 0xfc, 0xcd, 0x78, 0xfc, 0x77, 0xae, 0x7f, 0xfe, 0x9f, 0x4f, 0xe3, 0xe2, 0x58, 0xcf, 0xce,
+    0x35, 0x7b, 0xdc, 0x48, 0x94, 0xc4, 0x3f, 0x9f, 0x5f, 0x3c, 0xfe, 0xbb, 0x3f, 0x7d, 0xbf, 0xfc, 0xef, 0x5f, 0xfd, 0xa5,
+    0xeb, 0xb3, 0x8f, 0x54, 0xb9, 0xe7, 0xff, 0x4f, 0xee, 0x2a, 0xc6, 0x92, 0xb2, 0xbf, 0x3f, 0xbb, 0xfc, 0xb9, 0x77, 0xfe,
+    0x9f, 0xb8, 0xf8, 0x3e, 0x95, 0xab, 0xf4, 0x24, 0xb6, 0x4e, 0x88, 0xff, 0x78, 0x14, 0x89, 0xd1, 0x5a, 0x73, 0xfe, 0x7b,
+    0xd4, 0x68, 0x6c, 0xa9, 0xaa, 0x25, 0x79, 0x15, 0xe9, 0xf9, 0xf2, 0xb8, 0xaa, 0xfd, 0xf9, 0x16, 0x30, 0x5a, 0xbe, 0xcb,
+    0x7c, 0x6d, 0x8c, 0xe9, 0xdf, 0x7e, 0x56, 0x93, 0x7a, 0xbe, 0xd5, 0xcc, 0x27, 0xa3, 0xe5, 0x2a, 0xbd, 0x11, 0xff, 0x51,
+    0x6e, 0xa9, 0x22, 0x19, 0xd1, 0x1d, 0x25, 0x58, 0x69, 0x87, 0xc7, 0xc6, 0xf8, 0x8f, 0xd6, 0xb1, 0x4b, 0xb4, 0xd5, 0xb9,
+    0x37, 0xe2, 0xff, 0xba, 0xd7, 0x1e, 0x2d, 0xf5, 0x26, 0x6e, 0xca, 0xa8, 0x37, 0x96, 0xd7, 0x8e, 0x53, 0x57, 0xc7, 0xff,
+    0xf5, 0x6f, 0x66, 0xfb, 0xdb, 0x48, 0xf6, 0xdd, 0x1d, 0xe5, 0x54, 0xeb, 0xff, 0xfb, 0xcb, 0x31, 0x7e, 0xec, 0x9b, 0xab,
+    0xe3, 0x90, 0x4c, 0x09, 0x47, 0x72, 0x76, 0x35, 0xdb, 0xc3, 0xbe, 0xd9, 0xff, 0x3f, 0x1b, 0x0b, 0x8a, 0xff, 0xb5, 0xf1,
+    0x5f, 0xf9, 0x26, 0xf1, 0xf0, 0x0c, 0x2a, 0x7d, 0xe2, 0x38, 0x20, 0xfe, 0xb3, 0xed, 0xd0, 0x75, 0x59, 0x75, 0x8d, 0xe5,
+    0xb3, 0xeb, 0x12, 0xcf, 0x66, 0xd8, 0xfb, 0x6a, 0x61, 0x7e, 0x6c, 0x94, 0x8f, 0xff, 0x7c, 0xeb, 0x79, 0xf7, 0xd7, 0xb2,
+    0xa5, 0xfa, 0xf3, 0xcc, 0x6e, 0xbe, 0xc4, 0xf7, 0xc5, 0x7f, 0x14, 0xfa, 0x94, 0xf7, 0xe3, 0xb9, 0x27, 0xfe, 0xa3, 0xb0,
+    0x72, 0x31, 0xda, 0x56, 0xf9, 0x6a, 0xeb, 0x12, 0x7d, 0x6b, 0x06, 0xef, 0x5f, 0x81, 0xfb, 0xf8, 0xbf, 0x9b, 0x0f, 0xbe,
+    0xdf, 0xff, 0xc7, 0x11, 0xfd, 0x7f, 0x94, 0xd7, 0x86, 0x47, 0xa1, 0xf5, 0xfd, 0x5a, 0xfc, 0x47, 0x21, 0x66, 0xa2, 0x65,
+    0x5e, 0x3e, 0x96, 0xac, 0xe5, 0xf5, 0xae, 0x19, 0xee, 0xb9, 0x5a, 0x77, 0xbd, 0x68, 0xd7, 0xac, 0xe6, 0x77, 0xc5, 0xff,
+    0x68, 0xe8, 0xff, 0x9f, 0x8d, 0x01, 0x66, 0xbf, 0x47, 0x7c, 0xac, 0xf7, 0xef, 0x8c, 0xff, 0x5d, 0x2b, 0xf6, 0xeb, 0x6b,
+    0x63, 0x1c, 0x71, 0x65, 0xac, 0xff, 0x77, 0x8e, 0xff, 0xeb, 0x6b, 0x00, 0xb9, 0x79, 0x4c, 0x1c, 0x1a, 0xfd, 0x95, 0x39,
+    0x73, 0xd7, 0x1d, 0xfb, 0xde, 0xa7, 0x09, 0xa2, 0xf0, 0xac, 0x46, 0xcf, 0x2a, 0xce, 0xea, 0x6b, 0x90, 0xef, 0xff, 0x77,
+    0xdd, 0xff, 0xbf, 0x1f, 0x73, 0x9f, 0xd1, 0xff, 0x8f, 0xdb, 0xde, 0xfd, 0x94, 0xf3, 0x01, 0xf6, 0xb6, 0x00, 0x21, 0xfa,
+    0xc1, 0x73, 0xc4, 0x80, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x9f, 0xcf,
+    0xef, 0xcf, 0x63, 0x90, 0xcf, 0x84, 0xbd, 0x3e, 0x03, 0xfb, 0x4c, 0x4e, 0xe0, 0x6a, 0x09, 0x75, 0xfd, 0xc5, 0xeb, 0xdc,
+    0xf1, 0x99, 0x4c, 0x0b, 0xcf, 0x6b, 0x46, 0xa4, 0x77, 0x3f, 0xe8, 0xcc, 0x28, 0x50, 0xc9, 0x69, 0x30, 0x92, 0x75, 0xa2,
+    0x37, 0x97, 0x52, 0xee, 0xec, 0xf7, 0xd4, 0xf8, 0x2f, 0xc4, 0xff, 0xfb, 0x9f, 0x8c, 0x42, 0xfe, 0x9e, 0x9d, 0xf1, 0x9f,
+    0xad, 0x7b, 0xb5, 0xfc, 0xb8, 0x73, 0x7d, 0xc9, 0xee, 0x8c, 0x62, 0xd1, 0x78, 0x0d, 0xf7, 0xf5, 0xab, 0x7f, 0xe6, 0xd0,
+    0xd9, 0x51, 0xaf, 0x67, 0xe2, 0x3f, 0x9b, 0xfb, 0x34, 0x2e, 0x5a, 0xe1, 0x73, 0xe2, 0x7f, 0x6c, 0x89, 0xff, 0xf8, 0x48,
+    0xfc, 0x77, 0x67, 0x92, 0xe9, 0xca, 0x42, 0x5e, 0xc9, 0x7c, 0x93, 0xbf, 0x86, 0xf5, 0x0c, 0xbf, 0xd5, 0x1a, 0xf7, 0x9d,
+    0xf8, 0xaf, 0xd4, 0xad, 0xeb, 0x3c, 0xab, 0xe2, 0xff, 0xbe, 0x6c, 0x2b, 0xbd, 0xc9, 0xfc, 0xcf, 0x7b, 0x22, 0x7f, 0x4f,
+    0x1e, 0xdd, 0xbb, 0x7d, 0xb4, 0xd6, 0xd6, 0x95, 0xd9, 0xbe, 0xf2, 0x2b, 0xf1, 0x1f, 0xc5, 0xbf, 0x12, 0x37, 0x2d, 0x4a,
+    0x6f, 0x06, 0xf6, 0x9e, 0xac, 0x6d, 0x7b, 0xe3, 0xbf, 0x7b, 0xdf, 0x9f, 0x33, 0xe2, 0xff, 0xfd, 0x3c, 0xba, 0xab, 0xe6,
+    0x70, 0xbd, 0x7d, 0xe5, 0xef, 0xee, 0xff, 0xef, 0x5b, 0xc9, 0x1d, 0xf5, 0x63, 0xa6, 0xa7, 0x7f, 0xb3, 0xff, 0x7f, 0xef,
+    0xe7, 0xef, 0xc6, 0x7f, 0x57, 0x6d, 0x8f, 0x8d, 0x23, 0x93, 0x6a, 0x5f, 0xb9, 0x73, 0x95, 0x6c, 0xf7, 0xfc, 0x7f, 0xd5,
+    0x37, 0x1b, 0xe9, 0xd1, 0x44, 0x1c, 0x1b, 0xff, 0x33, 0xab, 0x90, 0xeb, 0xc6, 0x0b, 0xef, 0x8c, 0xff, 0x6b, 0xfb, 0xdb,
+    0x56, 0x76, 0x05, 0x3c, 0xad, 0xff, 0xdf, 0x75, 0x97, 0x6c, 0x6c, 0x8e, 0xc9, 0xbd, 0xc7, 0xea, 0xdc, 0xa5, 0x2e, 0x9f,
+    0xbf, 0x3f, 0x9a, 0x47, 0x78, 0x5f, 0x8f, 0xff, 0xfa, 0x2a, 0xff, 0x68, 0x5a, 0x95, 0x7c, 0x6f, 0xfc, 0xbf, 0x7b, 0xa7,
+    0x93, 0xca, 0x51, 0x7a, 0xda, 0xdf, 0x67, 0x77, 0x85, 0x7b, 0x7b, 0x81, 0x9f, 0xef, 0x36, 0x44, 0x53, 0xcf, 0xd1, 0x95,
+    0xbf, 0xff, 0xcf, 0x73, 0xcb, 0xef, 0x78, 0x96, 0xbf, 0x13, 0x1f, 0xc9, 0xfd, 0xf4, 0xdf, 0x88, 0xff, 0xca, 0x91, 0x2b,
+    0xff, 0x2b, 0x7b, 0x0d, 0x3b, 0xe3, 0xff, 0xbd, 0x16, 0x40, 0xde, 0x4d, 0x38, 0x71, 0x14, 0x03, 0xfc, 0x9e, 0x16, 0xc0,
+    0x0e, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xee, 0x37, 0xb1, 0x7f, 0xce,
+    0xcd, 0x55, 0xc9, 0x1a, 0x9f, 0x7f, 0x4f, 0x7a, 0x67, 0xc6, 0xfc, 0x71, 0x93, 0x85, 0xec, 0xfa, 0xb7, 0xfe, 0xfc, 0xa4,
+    0x23, 0x1f, 0x6f, 0x34, 0xbe, 0xb5, 0x9e, 0xc9, 0xe5, 0x1d, 0x85, 0x6c, 0x03, 0x57, 0x6f, 0xc6, 0xe6, 0xae, 0x41, 0xee,
+    0x18, 0xd1, 0x9e, 0xcf, 0x2c, 0xd2, 0xd9, 0x63, 0xfe, 0xf7, 0x9c, 0x9f, 0x67, 0xfa, 0x8d, 0xf2, 0x9b, 0xc6, 0x77, 0xb9,
+    0xcc, 0xf2, 0xf9, 0x11, 0x2a, 0x39, 0xf3, 0xf2, 0x79, 0x36, 0x4f, 0xcf, 0x98, 0x3f, 0x9f, 0x79, 0x27, 0x6e, 0xea, 0xe4,
+    0xf3, 0x5a, 0xfa, 0x24, 0x73, 0x44, 0x4f, 0x06, 0xa1, 0xda, 0x37, 0x8e, 0xe9, 0xda, 0x5a, 0xb9, 0xde, 0x99, 0x23, 0xc4,
+    0xf4, 0xfb, 0xfc, 0x95, 0xfa, 0x1e, 0x93, 0xd7, 0xbd, 0xfe, 0x8d, 0x67, 0xb2, 0x10, 0x44, 0xa9, 0xa7, 0xeb, 0x2c, 0xc1,
+    0xeb, 0x4f, 0xf2, 0x19, 0x33, 0xe3, 0x61, 0x7c, 0x75, 0xff, 0x74, 0x24, 0xdb, 0x9b, 0x4a, 0xbe, 0xba, 0x99, 0x5d, 0x33,
+    0xe6, 0xc6, 0x88, 0xe3, 0x22, 0xbb, 0x52, 0x3e, 0xfa, 0x7f, 0xee, 0x6f, 0x62, 0x32, 0xfe, 0xa3, 0x98, 0x59, 0xa8, 0x1e,
+    0x23, 0xd9, 0xf8, 0x9f, 0xbf, 0x0a, 0xbb, 0xe2, 0xbf, 0x92, 0xf5, 0xe0, 0xfd, 0x8c, 0xd9, 0xb5, 0x9e, 0x70, 0x6f, 0xfc,
+    0xc7, 0x83, 0xf6, 0x6b, 0xa4, 0xe7, 0x2f, 0xa3, 0x90, 0xe1, 0xfc, 0xf9, 0xec, 0x22, 0x5a, 0xfb, 0xff, 0x71, 0x59, 0x26,
+    0xf1, 0xc2, 0xf8, 0xbf, 0xb6, 0xdf, 0x5b, 0x2e, 0xfe, 0x67, 0x5a, 0xa7, 0xfd, 0xfd, 0x7f, 0xad, 0x5f, 0x3b, 0xb9, 0xff,
+    0x7f, 0x27, 0x63, 0x76, 0x14, 0xe6, 0xd9, 0x95, 0xe3, 0xc6, 0xe4, 0xc8, 0xe0, 0xd9, 0x5c, 0x30, 0x1a, 0xe6, 0xab, 0x1d,
+    0xf1, 0xdf, 0x3f, 0xc2, 0xae, 0xcc, 0xad, 0x6a, 0xfd, 0x7f, 0xb4, 0xce, 0xff, 0x23, 0xdd, 0xce, 0xcf, 0xc5, 0x7f, 0xe7,
+    0x6e, 0x4b, 0x2b, 0x32, 0xe6, 0xe6, 0x77, 0x76, 0x7c, 0xab, 0xff, 0x1f, 0xa5, 0xfe, 0xbf, 0xd6, 0x7b, 0xcd, 0xd4, 0xca,
+    0xd8, 0x90, 0xeb, 0x2e, 0x37, 0xfe, 0x5f, 0x3d, 0xca, 0xce, 0xcf, 0x28, 0x63, 0x53, 0xfb, 0xd4, 0x3d, 0xf7, 0xab, 0x45,
+    0x4b, 0xa4, 0xc7, 0xd7, 0x3b, 0x33, 0xe6, 0x56, 0xd6, 0x9e, 0xcf, 0x8f, 0xff, 0xce, 0x3d, 0x4c, 0x2a, 0x77, 0x17, 0xa2,
+    0xa1, 0x6f, 0x5e, 0xdd, 0xff, 0xcf, 0xce, 0xff, 0x2b, 0xb9, 0xf4, 0xf7, 0xac, 0xff, 0x8d, 0xa5, 0x6b, 0x3f, 0xd1, 0x7a,
+    0x3f, 0x71, 0x1c, 0xd0, 0x7e, 0x77, 0x66, 0x89, 0x7f, 0x63, 0xfe, 0xdf, 0x95, 0x81, 0x7d, 0x94, 0xef, 0x09, 0xd5, 0xd7,
+    0xff, 0x67, 0x6b, 0x60, 0x1c, 0xd3, 0xf7, 0x8f, 0xc7, 0x7b, 0xe8, 0xc4, 0x4b, 0xd1, 0x7f, 0x5a, 0xfc, 0x9f, 0xf2, 0x44,
+    0x43, 0x47, 0xbb, 0x15, 0x2d, 0xad, 0x5b, 0x65, 0xdf, 0x96, 0xce, 0x71, 0xd4, 0xfc, 0xfc, 0x7f, 0xcd, 0xba, 0xfa, 0x1b,
+    0xc7, 0x98, 0xb9, 0x23, 0x3b, 0x1f, 0xff, 0xb1, 0xbc, 0xe4, 0xe4, 0x23, 0xfe, 0x4e, 0x2b, 0x75, 0xde, 0xf1, 0xfe, 0xe6,
+    0xda, 0x13, 0x1b, 0x9f, 0xb5, 0x13, 0xa1, 0xc4, 0xd1, 0xf3, 0x21, 0xd7, 0x03, 0xfe, 0xe6, 0xf9, 0xd0, 0xdf, 0xfa, 0x1c,
+    0xbc, 0x92, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x7d, 0x3b, 0xa4, 0x92, 0x59,
+    0x72, 0x34, 0xbf, 0x8b, 0x9e, 0xcf, 0xad, 0x5f, 0x3d, 0xbb, 0xbb, 0xf7, 0xa5, 0x6b, 0xf9, 0x79, 0x6b, 0xe5, 0x34, 0x97,
+    0x8f, 0x28, 0x97, 0x53, 0xed, 0x7e, 0xff, 0x83, 0x38, 0xf2, 0x6a, 0xde, 0x65, 0x75, 0x8d, 0x42, 0x3e, 0xd5, 0xce, 0xeb,
+    0x35, 0x4a, 0x65, 0xb7, 0xab, 0x96, 0x3d, 0x7f, 0x3b, 0x34, 0x36, 0xbc, 0x4f, 0x59, 0x8b, 0x85, 0x5a, 0x6e, 0xc3, 0xb9,
+    0xdc, 0x61, 0xb1, 0xf8, 0x9b, 0xe5, 0xca, 0x6f, 0x7e, 0x3f, 0x89, 0xfb, 0x6c, 0xab, 0xf1, 0xb9, 0xab, 0x59, 0xcd, 0xf8,
+    0xde, 0x7d, 0xbd, 0xea, 0xb5, 0xe5, 0x84, 0x5a, 0xb6, 0x7e, 0x67, 0x89, 0x37, 0xde, 0x6e, 0xad, 0x66, 0xc4, 0x8d, 0xcf,
+    0xbf, 0x73, 0x1a, 0x1f, 0xcd, 0x05, 0x50, 0xcb, 0x13, 0xb9, 0xeb, 0x5b, 0x55, 0x8e, 0x14, 0xa5, 0x3d, 0x9b, 0xc6, 0xf6,
+    0xec, 0x10, 0xf9, 0xec, 0xe7, 0xa3, 0x94, 0xef, 0xb3, 0xfb, 0x93, 0xca, 0x15, 0xa9, 0xc7, 0xff, 0x55, 0x39, 0x55, 0x3e,
+    0x99, 0xed, 0xff, 0xc7, 0xf2, 0x7d, 0x18, 0x4e, 0xb9, 0x9a, 0x77, 0xfd, 0x7f, 0xb4, 0xed, 0x98, 0xb4, 0xf7, 0x93, 0x1d,
+    0xe5, 0xda, 0x11, 0x0b, 0xbb, 0xf6, 0xfb, 0x8b, 0x74, 0xad, 0x18, 0xe9, 0x1c, 0x7b, 0xf5, 0xd9, 0xd7, 0xae, 0x4f, 0x56,
+    0xc4, 0x7f, 0x5c, 0xcc, 0x93, 0xbb, 0x77, 0x1d, 0xeb, 0xbe, 0x9a, 0x4f, 0x5a, 0x86, 0x2f, 0xc6, 0xff, 0x8e, 0xf8, 0x99,
+    0x9d, 0xff, 0x47, 0x79, 0x37, 0x9d, 0x33, 0x6b, 0x4c, 0x3c, 0xcc, 0xe8, 0xfc, 0xdb, 0xe2, 0x7f, 0x6f, 0x19, 0x77, 0xb7,
+    0xe6, 0xd7, 0x23, 0xec, 0xf1, 0xd1, 0xf8, 0x9f, 0xdd, 0xef, 0x6d, 0x7e, 0xee, 0x7a, 0xbf, 0xfe, 0xdf, 0xb9, 0xb2, 0x71,
+    0x46, 0xfc, 0x9f, 0x12, 0xe5, 0xb9, 0x32, 0x5f, 0x1b, 0xff, 0xef, 0xc7, 0x50, 0x57, 0x1f, 0xf6, 0xde, 0xb9, 0x47, 0xdb,
+    0xfc, 0xff, 0x7e, 0x97, 0xe4, 0xdc, 0xdd, 0x9d, 0xbd, 0xab, 0x49, 0x3b, 0x4b, 0xbd, 0x73, 0xcf, 0xe1, 0xa7, 0x63, 0x87,
+    0xee, 0xf9, 0xee, 0x19, 0xfd, 0x7f, 0x88, 0xff, 0x05, 0x6b, 0xe1, 0x27, 0xf4, 0x92, 0xab, 0xee, 0xe5, 0xe5, 0xb3, 0xd8,
+    0xf7, 0x7e, 0x32, 0x5b, 0x52, 0xf9, 0xfb, 0xff, 0x5f, 0x1f, 0xff, 0x3f, 0xd9, 0xe1, 0xe7, 0x5b, 0xf1, 0x1f, 0xc5, 0x15,
+    0xc5, 0x33, 0xe2, 0x3f, 0x36, 0x8e, 0x6a, 0x57, 0xdc, 0xcb, 0x3f, 0x79, 0xfc, 0xdf, 0xbf, 0x13, 0xd4, 0xd7, 0xe3, 0xff,
+    0x0b, 0x63, 0xe8, 0x35, 0x4f, 0xa1, 0xbc, 0x17, 0xff, 0x73, 0x77, 0x86, 0x4f, 0x1e, 0xff, 0x9f, 0x1e, 0xff, 0xb3, 0x57,
+    0x25, 0x36, 0xcd, 0xff, 0x9f, 0x8e, 0x34, 0x7e, 0x7f, 0xfc, 0x8f, 0xad, 0x7b, 0xf7, 0xd7, 0xe7, 0x9b, 0xe3, 0xd0, 0x67,
+    0x61, 0xce, 0x79, 0x8a, 0xe3, 0xfd, 0x99, 0xcd, 0xfc, 0xae, 0xa8, 0xeb, 0xef, 0xff, 0x8f, 0x65, 0xfd, 0x7f, 0xef, 0x93,
+    0x2d, 0xbb, 0xee, 0xff, 0xef, 0x7d, 0xa2, 0xa8, 0xfb, 0x0c, 0xc5, 0xff, 0x37, 0xda, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe2, 0x73, 0xf2, 0x2b, 0x72, 0xeb, 0xf7, 0xe7, 0xc3, 0x38, 0x3d, 0xb7, 0xfe,
+    0xdd, 0xfb, 0x33, 0xe3, 0xd8, 0xdc, 0xfa, 0xe3, 0x51, 0x8e, 0xa2, 0xef, 0xe5, 0xd6, 0xf7, 0xee, 0xc7, 0xd3, 0xb7, 0x64,
+    0xd6, 0xe5, 0xd6, 0xef, 0x7b, 0x87, 0xfa, 0xf4, 0xdc, 0xfa, 0xe3, 0x26, 0x13, 0xd1, 0xc9, 0xb9, 0xf5, 0xeb, 0x6f, 0x51,
+    0x9d, 0x9a, 0x5b, 0x9f, 0xb5, 0xef, 0x4e, 0x3e, 0x6d, 0xfb, 0xe3, 0xc5, 0x6b, 0xf6, 0x46, 0x36, 0x84, 0x93, 0x6b, 0x64,
+    0x3e, 0x5b, 0xd3, 0x8a, 0x16, 0xa5, 0xf3, 0x7d, 0xcd, 0xb3, 0x73, 0xeb, 0x7f, 0x3d, 0xc6, 0xeb, 0xd9, 0x58, 0xa3, 0x9c,
+    0xf3, 0xbe, 0xef, 0x5d, 0xed, 0x48, 0x8d, 0x1b, 0xd7, 0xe6, 0xd6, 0xba, 0x6e, 0x47, 0xcf, 0xdd, 0x29, 0xe1, 0xdb, 0xb9,
+    0xb5, 0xc5, 0x7f, 0x4f, 0xab, 0x1b, 0x4d, 0x51, 0xf7, 0xf4, 0x93, 0x7d, 0x59, 0x83, 0xfa, 0xe2, 0x3f, 0xda, 0x3f, 0xdb,
+    0x9b, 0x5b, 0xab, 0xba, 0x9e, 0x20, 0xfe, 0x7f, 0x57, 0x3b, 0x90, 0x99, 0xd1, 0x3e, 0x29, 0xf7, 0x37, 0x6b, 0xc0, 0xce,
+    0xdc, 0x5a, 0x5f, 0x8f, 0xff, 0x4a, 0xff, 0x20, 0xfe, 0x7f, 0xcf, 0xf8, 0xbf, 0xaf, 0x2f, 0xaf, 0xaf, 0xff, 0x7f, 0x79,
+    0x6f, 0xad, 0xdf, 0x19, 0xff, 0x67, 0xe4, 0xd6, 0x15, 0xff, 0xef, 0x8c, 0xff, 0x6b, 0xfb, 0x13, 0xac, 0x58, 0xcd, 0xed,
+    0x9d, 0x4d, 0x88, 0xff, 0xd5, 0x75, 0xe7, 0xa4, 0x9d, 0x21, 0xc4, 0x78, 0x7d, 0xfe, 0x5f, 0xdb, 0x13, 0xb4, 0xda, 0x02,
+    0xec, 0xcb, 0xd5, 0xfa, 0x46, 0x6e, 0x7d, 0xb9, 0x75, 0x67, 0xae, 0xd7, 0xf5, 0xe8, 0xef, 0x9b, 0xf7, 0x63, 0x4e, 0x1f,
+    0xff, 0xf7, 0xde, 0xab, 0xf9, 0xff, 0x35, 0xa5, 0xd5, 0x2b, 0xde, 0xab, 0xc6, 0xff, 0x2b, 0xda, 0xd8, 0x37, 0x4b, 0x63,
+    0xcd, 0xbd, 0xa3, 0xfe, 0x33, 0x8c, 0xa6, 0xd1, 0x1f, 0xef, 0xb4, 0x1a, 0xb0, 0x66, 0x9c, 0xf1, 0x7e, 0x4b, 0x08, 0x7c,
+    0xed, 0x49, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x80, 0x93, 0xfc, 0xfb, 0x9f, 0x72, 0x00, 0xf1, 0x0f, 0xfc, 0x75, 0xf1, 0xff, 0x2f };
+
+// Font glyphs rectangles data (on atlas)
+static const Rectangle genesisFontRecs[189] = {
+    { 4, 4, 4 , 16 },
+    { 16, 4, 1 , 9 },
+    { 25, 4, 3 , 3 },
+    { 36, 4, 6 , 9 },
+    { 50, 4, 5 , 13 },
+    { 63, 4, 7 , 9 },
+    { 78, 4, 5 , 9 },
+    { 91, 4, 1 , 3 },
+    { 100, 4, 3 , 9 },
+    { 111, 4, 3 , 9 },
+    { 122, 4, 5 , 5 },
+    { 135, 4, 5 , 5 },
+    { 148, 4, 2 , 3 },
+    { 158, 4, 4 , 1 },
+    { 170, 4, 1 , 1 },
+    { 179, 4, 3 , 9 },
+    { 190, 4, 5 , 9 },
+    { 203, 4, 3 , 9 },
+    { 214, 4, 5 , 9 },
+    { 227, 4, 5 , 9 },
+    { 240, 4, 5 , 9 },
+    { 253, 4, 5 , 9 },
+    { 266, 4, 5 , 9 },
+    { 279, 4, 5 , 9 },
+    { 292, 4, 5 , 9 },
+    { 305, 4, 5 , 9 },
+    { 318, 4, 1 , 7 },
+    { 327, 4, 2 , 9 },
+    { 337, 4, 3 , 5 },
+    { 348, 4, 4 , 3 },
+    { 360, 4, 3 , 5 },
+    { 371, 4, 5 , 9 },
+    { 384, 4, 7 , 9 },
+    { 399, 4, 5 , 9 },
+    { 412, 4, 5 , 9 },
+    { 425, 4, 5 , 9 },
+    { 438, 4, 5 , 9 },
+    { 451, 4, 5 , 9 },
+    { 464, 4, 5 , 9 },
+    { 477, 4, 5 , 9 },
+    { 490, 4, 5 , 9 },
+    { 4, 28, 1 , 9 },
+    { 13, 28, 5 , 9 },
+    { 26, 28, 5 , 9 },
+    { 39, 28, 5 , 9 },
+    { 52, 28, 7 , 9 },
+    { 67, 28, 5 , 9 },
+    { 80, 28, 5 , 9 },
+    { 93, 28, 5 , 9 },
+    { 106, 28, 5 , 9 },
+    { 119, 28, 5 , 9 },
+    { 132, 28, 5 , 9 },
+    { 145, 28, 5 , 9 },
+    { 158, 28, 5 , 9 },
+    { 171, 28, 5 , 9 },
+    { 184, 28, 7 , 9 },
+    { 199, 28, 5 , 9 },
+    { 212, 28, 5 , 9 },
+    { 225, 28, 5 , 9 },
+    { 238, 28, 3 , 9 },
+    { 249, 28, 3 , 9 },
+    { 260, 28, 3 , 9 },
+    { 271, 28, 5 , 3 },
+    { 284, 28, 5 , 1 },
+    { 297, 28, 2 , 2 },
+    { 307, 28, 5 , 7 },
+    { 320, 28, 5 , 9 },
+    { 333, 28, 5 , 7 },
+    { 346, 28, 5 , 9 },
+    { 359, 28, 5 , 7 },
+    { 372, 28, 4 , 9 },
+    { 384, 28, 5 , 9 },
+    { 397, 28, 5 , 9 },
+    { 410, 28, 1 , 9 },
+    { 419, 28, 5 , 11 },
+    { 432, 28, 5 , 9 },
+    { 445, 28, 2 , 9 },
+    { 455, 28, 7 , 7 },
+    { 470, 28, 5 , 7 },
+    { 483, 28, 5 , 7 },
+    { 496, 28, 5 , 9 },
+    { 4, 52, 5 , 9 },
+    { 17, 52, 5 , 7 },
+    { 30, 52, 5 , 7 },
+    { 43, 52, 4 , 8 },
+    { 55, 52, 5 , 7 },
+    { 68, 52, 5 , 7 },
+    { 81, 52, 7 , 7 },
+    { 96, 52, 5 , 7 },
+    { 109, 52, 5 , 9 },
+    { 122, 52, 5 , 7 },
+    { 135, 52, 4 , 9 },
+    { 147, 52, 1 , 9 },
+    { 156, 52, 4 , 9 },
+    { 168, 52, 6 , 2 },
+    { 182, 52, 1 , 9 },
+    { 191, 52, 5 , 11 },
+    { 204, 52, 6 , 9 },
+    { 218, 52, 6 , 9 },
+    { 232, 52, 5 , 9 },
+    { 245, 52, 5 , 12 },
+    { 258, 52, 0 , 0 },
+    { 266, 52, 5 , 10 },
+    { 279, 52, 7 , 9 },
+    { 294, 52, 0 , 0 },
+    { 302, 52, 6 , 5 },
+    { 316, 52, 5 , 3 },
+    { 329, 52, 7 , 9 },
+    { 344, 52, 0 , 0 },
+    { 352, 52, 4 , 4 },
+    { 364, 52, 5 , 7 },
+    { 377, 52, 0 , 0 },
+    { 385, 52, 0 , 0 },
+    { 393, 52, 5 , 12 },
+    { 406, 52, 5 , 9 },
+    { 419, 52, 7 , 9 },
+    { 434, 52, 1 , 1 },
+    { 443, 52, 5 , 10 },
+    { 456, 52, 0 , 0 },
+    { 464, 52, 0 , 0 },
+    { 472, 52, 6 , 5 },
+    { 486, 52, 9 , 9 },
+    { 4, 76, 9 , 7 },
+    { 21, 76, 5 , 11 },
+    { 34, 76, 5 , 9 },
+    { 47, 76, 5 , 12 },
+    { 60, 76, 5 , 12 },
+    { 73, 76, 5 , 12 },
+    { 86, 76, 6 , 12 },
+    { 100, 76, 5 , 11 },
+    { 113, 76, 5 , 13 },
+    { 126, 76, 9 , 9 },
+    { 143, 76, 5 , 12 },
+    { 156, 76, 5 , 12 },
+    { 169, 76, 5 , 12 },
+    { 182, 76, 5 , 12 },
+    { 195, 76, 5 , 11 },
+    { 208, 76, 2 , 12 },
+    { 218, 76, 2 , 12 },
+    { 228, 76, 3 , 12 },
+    { 239, 76, 3 , 11 },
+    { 250, 76, 6 , 9 },
+    { 264, 76, 6 , 12 },
+    { 278, 76, 5 , 12 },
+    { 291, 76, 5 , 12 },
+    { 304, 76, 5 , 12 },
+    { 317, 76, 6 , 12 },
+    { 331, 76, 5 , 11 },
+    { 344, 76, 5 , 5 },
+    { 357, 76, 7 , 9 },
+    { 372, 76, 5 , 12 },
+    { 385, 76, 5 , 12 },
+    { 398, 76, 5 , 12 },
+    { 411, 76, 5 , 11 },
+    { 424, 76, 5 , 12 },
+    { 437, 76, 5 , 9 },
+    { 450, 76, 5 , 9 },
+    { 463, 76, 5 , 10 },
+    { 476, 76, 5 , 10 },
+    { 489, 76, 5 , 10 },
+    { 4, 100, 6 , 10 },
+    { 18, 100, 5 , 9 },
+    { 31, 100, 5 , 11 },
+    { 44, 100, 9 , 7 },
+    { 61, 100, 5 , 10 },
+    { 74, 100, 5 , 10 },
+    { 87, 100, 5 , 10 },
+    { 100, 100, 5 , 10 },
+    { 113, 100, 5 , 9 },
+    { 126, 100, 2 , 10 },
+    { 136, 100, 2 , 10 },
+    { 146, 100, 3 , 10 },
+    { 157, 100, 3 , 9 },
+    { 168, 100, 6 , 9 },
+    { 182, 100, 6 , 10 },
+    { 196, 100, 5 , 10 },
+    { 209, 100, 5 , 10 },
+    { 222, 100, 5 , 10 },
+    { 235, 100, 6 , 10 },
+    { 249, 100, 5 , 9 },
+    { 262, 100, 5 , 5 },
+    { 275, 100, 7 , 7 },
+    { 290, 100, 5 , 10 },
+    { 303, 100, 5 , 10 },
+    { 316, 100, 5 , 10 },
+    { 329, 100, 5 , 9 },
+    { 342, 100, 5 , 12 },
+    { 355, 100, 5 , 11 },
+    { 368, 100, 5 , 11 },
+};
+
+// Font glyphs info data
+// NOTE: No glyphs.image data provided
+static const GlyphInfo genesisFontGlyphs[189] = {
+    { 32, 0, 0, 4, { 0 }},
+    { 33, 2, 4, 5, { 0 }},
+    { 34, 2, 4, 7, { 0 }},
+    { 35, 1, 4, 8, { 0 }},
+    { 36, 1, 2, 7, { 0 }},
+    { 37, 1, 4, 9, { 0 }},
+    { 38, 1, 4, 7, { 0 }},
+    { 39, 2, 4, 5, { 0 }},
+    { 40, 3, 4, 7, { 0 }},
+    { 41, 1, 4, 7, { 0 }},
+    { 42, 1, 4, 7, { 0 }},
+    { 43, 1, 6, 7, { 0 }},
+    { 44, 1, 12, 5, { 0 }},
+    { 45, 1, 8, 6, { 0 }},
+    { 46, 2, 12, 5, { 0 }},
+    { 47, 1, 4, 5, { 0 }},
+    { 48, 1, 4, 7, { 0 }},
+    { 49, 2, 4, 7, { 0 }},
+    { 50, 1, 4, 7, { 0 }},
+    { 51, 1, 4, 7, { 0 }},
+    { 52, 1, 4, 7, { 0 }},
+    { 53, 1, 4, 7, { 0 }},
+    { 54, 1, 4, 7, { 0 }},
+    { 55, 1, 4, 7, { 0 }},
+    { 56, 1, 4, 7, { 0 }},
+    { 57, 1, 4, 7, { 0 }},
+    { 58, 2, 6, 5, { 0 }},
+    { 59, 1, 6, 5, { 0 }},
+    { 60, 1, 6, 5, { 0 }},
+    { 61, 1, 7, 6, { 0 }},
+    { 62, 1, 6, 5, { 0 }},
+    { 63, 1, 4, 7, { 0 }},
+    { 64, 1, 4, 9, { 0 }},
+    { 65, 1, 4, 7, { 0 }},
+    { 66, 1, 4, 7, { 0 }},
+    { 67, 1, 4, 7, { 0 }},
+    { 68, 1, 4, 7, { 0 }},
+    { 69, 1, 4, 7, { 0 }},
+    { 70, 1, 4, 7, { 0 }},
+    { 71, 1, 4, 7, { 0 }},
+    { 72, 1, 4, 7, { 0 }},
+    { 73, 2, 4, 5, { 0 }},
+    { 74, 1, 4, 7, { 0 }},
+    { 75, 1, 4, 7, { 0 }},
+    { 76, 1, 4, 7, { 0 }},
+    { 77, 1, 4, 9, { 0 }},
+    { 78, 1, 4, 7, { 0 }},
+    { 79, 1, 4, 7, { 0 }},
+    { 80, 1, 4, 7, { 0 }},
+    { 81, 1, 4, 7, { 0 }},
+    { 82, 1, 4, 7, { 0 }},
+    { 83, 1, 4, 7, { 0 }},
+    { 84, 1, 4, 7, { 0 }},
+    { 85, 1, 4, 7, { 0 }},
+    { 86, 1, 4, 7, { 0 }},
+    { 87, 1, 4, 9, { 0 }},
+    { 88, 1, 4, 7, { 0 }},
+    { 89, 1, 4, 7, { 0 }},
+    { 90, 1, 4, 7, { 0 }},
+    { 91, 3, 4, 7, { 0 }},
+    { 92, 1, 4, 5, { 0 }},
+    { 93, 1, 4, 7, { 0 }},
+    { 94, 1, 4, 7, { 0 }},
+    { 95, 0, 14, 5, { 0 }},
+    { 96, 1, 4, 5, { 0 }},
+    { 97, 1, 6, 7, { 0 }},
+    { 98, 1, 4, 7, { 0 }},
+    { 99, 1, 6, 7, { 0 }},
+    { 100, 1, 4, 7, { 0 }},
+    { 101, 1, 6, 7, { 0 }},
+    { 102, 1, 4, 6, { 0 }},
+    { 103, 1, 6, 7, { 0 }},
+    { 104, 1, 4, 7, { 0 }},
+    { 105, 2, 4, 5, { 0 }},
+    { 106, 1, 4, 7, { 0 }},
+    { 107, 1, 4, 7, { 0 }},
+    { 108, 2, 4, 5, { 0 }},
+    { 109, 1, 6, 9, { 0 }},
+    { 110, 1, 6, 7, { 0 }},
+    { 111, 1, 6, 7, { 0 }},
+    { 112, 1, 6, 7, { 0 }},
+    { 113, 1, 6, 7, { 0 }},
+    { 114, 1, 6, 7, { 0 }},
+    { 115, 1, 6, 7, { 0 }},
+    { 116, 1, 5, 6, { 0 }},
+    { 117, 1, 6, 7, { 0 }},
+    { 118, 1, 6, 7, { 0 }},
+    { 119, 1, 6, 9, { 0 }},
+    { 120, 1, 6, 7, { 0 }},
+    { 121, 1, 6, 7, { 0 }},
+    { 122, 1, 6, 7, { 0 }},
+    { 123, 2, 4, 7, { 0 }},
+    { 124, 2, 4, 5, { 0 }},
+    { 125, 1, 4, 7, { 0 }},
+    { 126, 1, 4, 8, { 0 }},
+    { 161, 2, 6, 5, { 0 }},
+    { 162, 1, 4, 7, { 0 }},
+    { 163, 1, 4, 8, { 0 }},
+    { 8364, 1, 4, 8, { 0 }},
+    { 165, 1, 4, 7, { 0 }},
+    { 352, 1, 1, 7, { 0 }},
+    { 167, 0, 0, 0, { 0 }},
+    { 353, 1, 3, 7, { 0 }},
+    { 169, 1, 4, 9, { 0 }},
+    { 170, 0, 0, 0, { 0 }},
+    { 171, 1, 6, 8, { 0 }},
+    { 172, 1, 8, 7, { 0 }},
+    { 174, 1, 4, 9, { 0 }},
+    { 175, 0, 0, 0, { 0 }},
+    { 176, 1, 4, 6, { 0 }},
+    { 177, 1, 6, 7, { 0 }},
+    { 178, 0, 0, 0, { 0 }},
+    { 179, 0, 0, 0, { 0 }},
+    { 381, 1, 1, 7, { 0 }},
+    { 181, 1, 6, 7, { 0 }},
+    { 182, 1, 4, 9, { 0 }},
+    { 183, 2, 8, 5, { 0 }},
+    { 382, 1, 3, 7, { 0 }},
+    { 185, 0, 0, 0, { 0 }},
+    { 186, 0, 0, 0, { 0 }},
+    { 187, 1, 6, 8, { 0 }},
+    { 338, 1, 4, 11, { 0 }},
+    { 339, 1, 6, 11, { 0 }},
+    { 376, 1, 2, 7, { 0 }},
+    { 191, 1, 6, 7, { 0 }},
+    { 192, 1, 1, 7, { 0 }},
+    { 193, 1, 1, 7, { 0 }},
+    { 194, 1, 1, 7, { 0 }},
+    { 195, 1, 1, 7, { 0 }},
+    { 196, 1, 2, 7, { 0 }},
+    { 197, 1, 0, 7, { 0 }},
+    { 198, 1, 4, 11, { 0 }},
+    { 199, 1, 4, 7, { 0 }},
+    { 200, 1, 1, 7, { 0 }},
+    { 201, 1, 1, 7, { 0 }},
+    { 202, 1, 1, 7, { 0 }},
+    { 203, 1, 2, 7, { 0 }},
+    { 204, 1, 1, 5, { 0 }},
+    { 205, 2, 1, 5, { 0 }},
+    { 206, 1, 1, 5, { 0 }},
+    { 207, 1, 2, 5, { 0 }},
+    { 208, 0, 4, 7, { 0 }},
+    { 209, 1, 1, 7, { 0 }},
+    { 210, 1, 1, 7, { 0 }},
+    { 211, 1, 1, 7, { 0 }},
+    { 212, 1, 1, 7, { 0 }},
+    { 213, 1, 1, 7, { 0 }},
+    { 214, 1, 2, 7, { 0 }},
+    { 215, 1, 6, 7, { 0 }},
+    { 216, 0, 4, 7, { 0 }},
+    { 217, 1, 1, 7, { 0 }},
+    { 218, 1, 1, 7, { 0 }},
+    { 219, 1, 1, 7, { 0 }},
+    { 220, 1, 2, 7, { 0 }},
+    { 221, 1, 1, 7, { 0 }},
+    { 222, 1, 4, 7, { 0 }},
+    { 223, 1, 4, 7, { 0 }},
+    { 224, 1, 3, 7, { 0 }},
+    { 225, 1, 3, 7, { 0 }},
+    { 226, 1, 3, 7, { 0 }},
+    { 227, 1, 3, 7, { 0 }},
+    { 228, 1, 4, 7, { 0 }},
+    { 229, 1, 2, 7, { 0 }},
+    { 230, 1, 6, 11, { 0 }},
+    { 231, 1, 6, 7, { 0 }},
+    { 232, 1, 3, 7, { 0 }},
+    { 233, 1, 3, 7, { 0 }},
+    { 234, 1, 3, 7, { 0 }},
+    { 235, 1, 4, 7, { 0 }},
+    { 236, 1, 3, 5, { 0 }},
+    { 237, 2, 3, 5, { 0 }},
+    { 238, 1, 3, 5, { 0 }},
+    { 239, 1, 4, 5, { 0 }},
+    { 240, 1, 4, 7, { 0 }},
+    { 241, 1, 3, 7, { 0 }},
+    { 242, 1, 3, 7, { 0 }},
+    { 243, 1, 3, 7, { 0 }},
+    { 244, 1, 3, 7, { 0 }},
+    { 245, 1, 3, 7, { 0 }},
+    { 246, 1, 4, 7, { 0 }},
+    { 247, 1, 6, 7, { 0 }},
+    { 248, 0, 6, 7, { 0 }},
+    { 249, 1, 3, 7, { 0 }},
+    { 250, 1, 3, 7, { 0 }},
+    { 251, 1, 3, 7, { 0 }},
+    { 252, 1, 4, 7, { 0 }},
+    { 253, 1, 3, 7, { 0 }},
+    { 254, 1, 4, 7, { 0 }},
+    { 255, 1, 4, 7, { 0 }},
+};
+
+// Style loading function: Genesis
+static void GuiLoadStyleGenesis(void)
+{
+    // Load style properties provided
+    // NOTE: Default properties are propagated
+    for (int i = 0; i < GENESIS_STYLE_PROPS_COUNT; i++)
+    {
+        GuiSetStyle(genesisStyleProps[i].controlId, genesisStyleProps[i].propertyId, genesisStyleProps[i].propertyValue);
+    }
+
+    // Custom font loading
+    // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
+    int genesisFontDataSize = 0;
+    unsigned char *data = DecompressData(genesisFontData, GENESIS_STYLE_FONT_ATLAS_COMP_SIZE, &genesisFontDataSize);
+    Image imFont = { data, 512, 256, 1, 2 };
+
+    Font font = { 0 };
+    font.baseSize = 16;
+    font.glyphCount = 189;
+
+    // Load texture from image
+    font.texture = LoadTextureFromImage(imFont);
+    UnloadImage(imFont);  // Uncompressed image data can be unloaded from memory
+
+    // Copy char recs data from global fontRecs
+    // NOTE: Required to avoid issues if trying to free font
+    font.recs = (Rectangle *)RAYGUI_MALLOC(font.glyphCount*sizeof(Rectangle));
+    memcpy(font.recs, genesisFontRecs, font.glyphCount*sizeof(Rectangle));
+
+    // Copy font char info data from global fontChars
+    // NOTE: Required to avoid issues if trying to free font
+    font.glyphs = (GlyphInfo *)RAYGUI_MALLOC(font.glyphCount*sizeof(GlyphInfo));
+    memcpy(font.glyphs, genesisFontGlyphs, font.glyphCount*sizeof(GlyphInfo));
+
+    GuiSetFont(font);
+
+    // Setup a white rectangle on the font to be used on shapes drawing,
+    // it makes possible to draw shapes and text (full UI) in a single draw call
+    Rectangle fontWhiteRec = { 510, 254, 1, 1 };
+    SetShapesTexture(font.texture, fontWhiteRec);
+
+    //-----------------------------------------------------------------
+
+    // TODO: Custom user style setup: Set specific properties here (if required)
+    // i.e. Controls specific BORDER_WIDTH, TEXT_PADDING, TEXT_ALIGNMENT
+}
diff --git a/raygui/styles/jungle/style_jungle.h b/raygui/styles/jungle/style_jungle.h
--- a/raygui/styles/jungle/style_jungle.h
+++ b/raygui/styles/jungle/style_jungle.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,133 +15,134 @@
 
 // Custom style name: Jungle
 static const GuiStyleProp jungleStyleProps[JUNGLE_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x60827dff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x2c3334ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0x82a29fff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0x5f9aa8ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x334e57ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0x6aa9b8ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xa9cb8dff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0x3b6357ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x97af81ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x5b6462ff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x2c3334ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x666b69ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x0000000c },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0x638465ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x2b3a3aff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000012 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0x60827dff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x2c3334ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x82a29fff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0x5f9aa8ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x334e57ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0x6aa9b8ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xa9cb8dff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0x3b6357ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x97af81ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x5b6462ff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x2c3334ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x666b69ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x0000000c },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x638465ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x2b3a3aff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000012 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
-// WARNING: This style uses a custom font: "Pixel Intv.otf" (size: 12, spacing: 0)
+// WARNING: This style uses a custom font: "PixelIntv.otf" (size: 12, spacing: 0)
 
-#define JUNGLE_STYLE_FONT_ATLAS_COMP_SIZE 2030
+#define JUNGLE_STYLE_FONT_ATLAS_COMP_SIZE 2059
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char jungleFontData[JUNGLE_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0x9d, 0x3d, 0x8e, 0x25, 0x35, 0x14, 0x85, 0x8d, 0xe9, 0x8c, 0x10, 0x11, 0x80, 0x84, 0x20, 0x41, 0x22, 0x21, 0x61, 0x34,
-    0x12, 0x64, 0xb3, 0x00, 0x62, 0x84, 0x08, 0x10, 0x11, 0x09, 0xb0, 0x07, 0x66, 0x65, 0x93, 0x90, 0xb3, 0x01, 0x76, 0xc1,
-    0x02, 0x2e, 0x9a, 0xbf, 0x9e, 0x9f, 0xae, 0xb2, 0x7d, 0x8f, 0xaf, 0xcb, 0xae, 0x7a, 0x5f, 0x7f, 0x9a, 0xd1, 0xcc, 0xf3,
-    0x73, 0x95, 0xcb, 0xc7, 0xd7, 0x55, 0xaf, 0x7d, 0xde, 0xb5, 0x25, 0x00, 0x00, 0x00, 0x80, 0x07, 0x3c, 0xff, 0xd9, 0x7e,
-    0x6d, 0xab, 0x24, 0xbd, 0x2a, 0x69, 0x3f, 0xd6, 0xeb, 0xd7, 0x5f, 0x96, 0xe4, 0xdd, 0x77, 0x6c, 0x1f, 0x31, 0xef, 0xb4,
-    0x61, 0xaf, 0xd5, 0xfb, 0x2d, 0x4b, 0xae, 0x92, 0xfd, 0xe3, 0xef, 0xf7, 0x57, 0x72, 0x5c, 0xc3, 0xbb, 0x3f, 0xc9, 0x71,
-    0x2d, 0xe5, 0x7a, 0xa3, 0xf5, 0x2f, 0xf7, 0xc1, 0x7e, 0xd9, 0xcb, 0x3f, 0xcf, 0x7b, 0x22, 0x3b, 0x8e, 0xb9, 0xff, 0xfe,
-    0xe4, 0x1a, 0x81, 0xfe, 0x91, 0xe1, 0xeb, 0xdd, 0xd2, 0xbb, 0x6b, 0xd7, 0x60, 0xce, 0x16, 0xbc, 0x7e, 0x7d, 0xa4, 0xfe,
-    0xe9, 0xfe, 0x1c, 0xed, 0xf1, 0x5f, 0x53, 0xc5, 0xec, 0x6e, 0xb3, 0x27, 0x52, 0x51, 0x9b, 0x2c, 0x9c, 0x29, 0x62, 0x34,
-    0xa7, 0xa0, 0xde, 0xcd, 0xc5, 0x6b, 0x30, 0x41, 0x7f, 0x6d, 0x84, 0x46, 0x1d, 0x47, 0x8b, 0xff, 0xe7, 0xdc, 0xbd, 0x18,
-    0x01, 0xe5, 0xa3, 0xf6, 0xb7, 0x38, 0x2a, 0xfe, 0xd3, 0x50, 0xf5, 0x7b, 0xf4, 0x3f, 0x62, 0xfe, 0x2f, 0xbf, 0xbf, 0xdc,
-    0x67, 0xdb, 0xc7, 0xcb, 0xaf, 0xe2, 0xdf, 0x7f, 0xf7, 0x8c, 0xd0, 0xdf, 0x7f, 0xff, 0x1f, 0xab, 0x7e, 0xc4, 0xfc, 0xbf,
-    0xe2, 0x73, 0x64, 0xed, 0x3e, 0xbe, 0x3d, 0x76, 0x6c, 0x78, 0xfc, 0xcf, 0xa0, 0xa4, 0x7e, 0xed, 0xf9, 0xcf, 0x3a, 0xe6,
-    0xd9, 0xd1, 0xf1, 0xaf, 0x1c, 0xeb, 0x75, 0x4f, 0x98, 0x10, 0x9f, 0x67, 0xd5, 0xbf, 0xa4, 0x3e, 0x44, 0xcc, 0x35, 0x9e,
-    0xbb, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x35, 0x57, 0x4c, 0xca, 0xeb, 0xef,
-    0x91, 0x2b, 0x91, 0xa3, 0xca, 0xda, 0xdd, 0x06, 0x1e, 0xe7, 0x4a, 0x16, 0x7a, 0x6c, 0x56, 0xbf, 0x8c, 0x72, 0xfb, 0x25,
-    0xb7, 0x37, 0xa3, 0xa5, 0x35, 0xea, 0xf9, 0x7a, 0xf4, 0xcf, 0xce, 0xb5, 0xd7, 0xb2, 0x43, 0x6f, 0xcf, 0xcf, 0xb8, 0xbf,
-    0x72, 0xdd, 0x7b, 0x7d, 0xde, 0x32, 0xdf, 0x0c, 0x90, 0x0e, 0x89, 0xff, 0x36, 0x0f, 0xd3, 0x18, 0x77, 0xe3, 0xb6, 0xcf,
-    0xac, 0xd4, 0xd6, 0x3b, 0xb7, 0xa3, 0xf0, 0xcd, 0x6a, 0xf3, 0xd8, 0xf1, 0x1d, 0xed, 0xfa, 0x54, 0x1d, 0x25, 0xca, 0x98,
-    0x54, 0x1c, 0xac, 0x11, 0xf3, 0x7f, 0x4c, 0xfc, 0x97, 0xc6, 0xa9, 0xbd, 0xf7, 0x27, 0x66, 0x1e, 0x9f, 0xa5, 0xff, 0x88,
-    0xb9, 0x3a, 0x35, 0xcc, 0x0a, 0x6a, 0x5b, 0xb4, 0xfb, 0xbf, 0xe2, 0xd4, 0x29, 0xb9, 0x95, 0xf4, 0xe7, 0xa9, 0x19, 0xf1,
-    0xdf, 0x13, 0xe3, 0xda, 0xfc, 0x5f, 0xd6, 0x3f, 0xce, 0xdd, 0x66, 0x53, 0x9e, 0xa6, 0x5b, 0x62, 0x6c, 0xad, 0xfb, 0x3f,
-    0x44, 0xfa, 0x19, 0xe7, 0x7d, 0x9e, 0x42, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x18, 0xeb, 0xc7, 0x3b, 0xca, 0x53, 0x62, 0x8d, 0x59, 0x7c, 0xf6, 0xfd, 0x5a, 0x47, 0xb9, 0xf5, 0x8e, 0xef, 0xef, 0x68,
-    0xdf, 0x47, 0xc4, 0x9a, 0xb4, 0x92, 0xc1, 0xac, 0xdf, 0x8b, 0x68, 0x4b, 0x38, 0xf2, 0x46, 0xf4, 0xdb, 0x4c, 0xfd, 0x95,
-    0xf6, 0xe8, 0xfa, 0x9b, 0x98, 0x61, 0xb0, 0x96, 0x19, 0x66, 0x2d, 0xfd, 0x15, 0xd7, 0x41, 0x8f, 0xfe, 0xf5, 0xb9, 0x2a,
-    0x15, 0xf4, 0xf0, 0xcf, 0xf1, 0xaa, 0xfe, 0x26, 0xe6, 0x7d, 0x1c, 0x73, 0x17, 0x1b, 0xa3, 0xbf, 0xd2, 0x6f, 0x57, 0x99,
-    0xff, 0xeb, 0xc7, 0xcc, 0xe2, 0xfc, 0x6f, 0xd5, 0x59, 0xc5, 0xc2, 0xdd, 0xa8, 0xfe, 0x98, 0x6a, 0xed, 0x9f, 0xdb, 0xd5,
-    0x3f, 0x0d, 0xd1, 0x7f, 0xcc, 0x19, 0xcf, 0x73, 0xff, 0x57, 0xb2, 0x57, 0xcf, 0xf0, 0xb9, 0xf5, 0x79, 0xc3, 0x6b, 0xa5,
-    0x47, 0xf4, 0xcc, 0xb8, 0xf9, 0x1f, 0x17, 0xe0, 0x79, 0x5c, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x47, 0xac, 0x51, 0xe8, 0x6b, 0x67, 0x23, 0x56, 0x00, 0x1f, 0xbe, 0x9e, 0x1b, 0xd6, 0x30, 0xdf, 0x7f,
-    0x3d, 0xef, 0x38, 0x46, 0xcc, 0x99, 0xf3, 0x4f, 0xdb, 0x8f, 0xb7, 0x35, 0x0f, 0x9a, 0xee, 0x20, 0x8c, 0x58, 0x07, 0x3c,
-    0x5e, 0xff, 0xd4, 0xb4, 0x1e, 0xfb, 0x50, 0xc9, 0x2c, 0x78, 0x86, 0xb2, 0x33, 0xe3, 0x53, 0x29, 0x43, 0x94, 0xdf, 0xcd,
-    0x54, 0xef, 0x05, 0x25, 0xd7, 0x5d, 0xed, 0x68, 0xda, 0x0e, 0xc5, 0x25, 0x77, 0x4c, 0xcd, 0xcb, 0xe2, 0xbf, 0xf2, 0xec,
-    0xbe, 0xb6, 0xdc, 0x30, 0x07, 0x8c, 0xf7, 0x48, 0x27, 0x77, 0x3c, 0xf6, 0xf9, 0xee, 0xc6, 0xeb, 0x9f, 0x1a, 0x3c, 0x7e,
-    0xd1, 0x8e, 0x14, 0xab, 0xec, 0x7e, 0xab, 0x8c, 0x80, 0xd1, 0xfa, 0xd7, 0xae, 0x55, 0x77, 0x09, 0xa5, 0xd0, 0x71, 0x13,
-    0xad, 0x7f, 0xcb, 0x38, 0x8d, 0x1e, 0xc1, 0xca, 0x08, 0x38, 0x26, 0xfe, 0xcd, 0xad, 0x7f, 0x79, 0x6e, 0x88, 0xef, 0x3d,
-    0xc5, 0x05, 0xa4, 0xcd, 0x28, 0xbd, 0x6e, 0x35, 0x45, 0xc5, 0x2c, 0xcd, 0x1c, 0x47, 0x3c, 0x41, 0x6b, 0x7b, 0x55, 0x47,
-    0x3b, 0x36, 0xb5, 0x9e, 0x38, 0x5e, 0x7f, 0x93, 0x77, 0xfc, 0xce, 0xcb, 0xed, 0x7e, 0xad, 0x45, 0x9d, 0xee, 0xd8, 0x8b,
-    0x8f, 0xff, 0xf5, 0xbe, 0x8d, 0x76, 0x44, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
-    0xdb, 0xcc, 0x51, 0xe0, 0xcf, 0x54, 0x53, 0xaf, 0xa1, 0xee, 0x9d, 0x75, 0xe4, 0xde, 0xb9, 0xa3, 0xf6, 0xb7, 0xd3, 0xf3,
-    0x82, 0xd9, 0x90, 0x1d, 0xe0, 0xe2, 0xd7, 0xcd, 0xfa, 0xb2, 0x98, 0xd5, 0x7a, 0x48, 0xcd, 0xb8, 0xe5, 0xf5, 0x60, 0xf4,
-    0xf8, 0x7a, 0x54, 0xcf, 0x47, 0xbc, 0x0f, 0x2f, 0xc6, 0xdb, 0xa6, 0xc5, 0xbf, 0xe6, 0x0b, 0x59, 0x4d, 0xff, 0x74, 0x90,
-    0x3f, 0xaf, 0x47, 0xff, 0xde, 0xdd, 0x9f, 0x75, 0x47, 0x96, 0xbf, 0x57, 0xaf, 0xa0, 0x7f, 0x9b, 0x7b, 0x57, 0xf1, 0x11,
-    0x46, 0xfb, 0xf0, 0x66, 0xe9, 0x6f, 0xd2, 0xde, 0xb9, 0x3d, 0xfa, 0xf7, 0xf9, 0xe5, 0x8f, 0x8b, 0x7f, 0xfd, 0x8c, 0x69,
-    0xc8, 0xce, 0xe8, 0xbd, 0xd1, 0xea, 0xcb, 0xd4, 0x38, 0xc3, 0xa9, 0x15, 0xef, 0x97, 0x1b, 0xe5, 0xec, 0x9a, 0xeb, 0x5e,
-    0x8c, 0xf2, 0xaf, 0xf6, 0x66, 0xff, 0x9e, 0xa1, 0x3f, 0xde, 0xb1, 0x6b, 0x8f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x5b, 0x5b, 0xc7, 0x89, 0xdb, 0x8f, 0xca, 0x9b, 0xd5, 0xcd, 0x36, 0x33, 0x7a, 0xa8,
-    0xb9, 0x63, 0xbc, 0xde, 0x8a, 0xd2, 0x5a, 0xe5, 0x77, 0xf6, 0xe5, 0x3d, 0x8f, 0xec, 0x13, 0x57, 0xef, 0xd4, 0xea, 0xe6,
-    0xb7, 0xea, 0x66, 0x77, 0xed, 0xd7, 0x59, 0x50, 0xb2, 0x70, 0x66, 0xbf, 0x03, 0xc0, 0x97, 0xab, 0xcd, 0xf7, 0x6a, 0x2e,
-    0x3a, 0x45, 0x62, 0x5c, 0x17, 0x6a, 0x86, 0xbe, 0xc7, 0xef, 0xf4, 0xc1, 0xa3, 0xe0, 0xba, 0xf9, 0x1d, 0x25, 0x7d, 0xb5,
-    0xf3, 0x7d, 0x66, 0xc3, 0xec, 0xae, 0xeb, 0xd3, 0x3f, 0xbb, 0x73, 0xb5, 0xf9, 0xd5, 0x89, 0xf1, 0x15, 0x6a, 0x59, 0x71,
-    0x4a, 0xc7, 0xfc, 0xb2, 0xf0, 0xbf, 0xda, 0x35, 0xd5, 0xeb, 0xbe, 0x3d, 0xf3, 0xfa, 0x6b, 0xdf, 0xdd, 0xff, 0xed, 0xad,
-    0xeb, 0x75, 0xf2, 0x68, 0x2e, 0xae, 0xde, 0x11, 0x10, 0x99, 0x4d, 0x51, 0x75, 0x64, 0xb5, 0xe8, 0xbf, 0x77, 0xce, 0x36,
-    0xfd, 0xdf, 0xfc, 0xdb, 0x57, 0xbb, 0x14, 0xff, 0x9a, 0xfe, 0x71, 0x3e, 0xf7, 0xb1, 0xf1, 0x3f, 0x22, 0x2b, 0xde, 0x5e,
-    0x49, 0x6b, 0xfc, 0x6b, 0x63, 0xa7, 0x27, 0xfe, 0x4b, 0xf7, 0x7f, 0xbf, 0xfe, 0xaa, 0x0b, 0xee, 0x68, 0xfd, 0x93, 0xec,
-    0x53, 0xd5, 0x9e, 0x1a, 0xeb, 0x3d, 0xd9, 0x33, 0x77, 0x94, 0xee, 0xff, 0xaa, 0x86, 0xd1, 0xf1, 0xbf, 0x9e, 0xfe, 0xa9,
-    0xc3, 0x59, 0xeb, 0x1d, 0x53, 0xad, 0x3d, 0x69, 0xe2, 0x0c, 0xbe, 0xff, 0xfc, 0x7f, 0xac, 0xfe, 0xb1, 0xce, 0x4a, 0x2d,
-    0xab, 0xe3, 0x8a, 0xfc, 0xf2, 0xd6, 0x27, 0xa9, 0xc7, 0xf6, 0xbd, 0x6b, 0xfe, 0xaf, 0xd5, 0xed, 0x3b, 0xf3, 0xa8, 0xba,
-    0x10, 0x39, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xa3, 0xd7, 0x89, 0xf6, 0xe9,
-    0xa6, 0x43, 0xa4, 0x56, 0x6f, 0x74, 0x79, 0xcd, 0xd3, 0xf6, 0x95, 0x7d, 0x63, 0x9f, 0x3f, 0x78, 0xf5, 0x99, 0x3d, 0xb3,
-    0x5f, 0x27, 0xb4, 0x35, 0xef, 0xee, 0x70, 0xbc, 0xf5, 0xa3, 0xbc, 0xa7, 0xb4, 0x8a, 0xd1, 0xe3, 0x44, 0xfb, 0xc1, 0x9e,
-    0xd8, 0x1f, 0x1b, 0x47, 0xad, 0xd5, 0x1b, 0x5d, 0x5e, 0xd2, 0xff, 0x33, 0xfb, 0xd7, 0xfe, 0xb1, 0xa7, 0xf6, 0xc5, 0x66,
-    0xad, 0xbf, 0x0e, 0x6f, 0x6b, 0xc9, 0xcd, 0xe3, 0xd9, 0x47, 0x59, 0xcb, 0x03, 0xd5, 0xe7, 0x44, 0xf9, 0xd9, 0x3e, 0xb0,
-    0x1f, 0x37, 0x7c, 0x68, 0xb5, 0x7a, 0xa3, 0xcb, 0x4b, 0xfa, 0xff, 0x69, 0x66, 0x7f, 0xdb, 0x6f, 0xf6, 0xe1, 0x83, 0x92,
-    0x27, 0xf6, 0x64, 0x42, 0x5b, 0x93, 0xdd, 0xbd, 0x68, 0xe7, 0x5d, 0xe7, 0x3e, 0xda, 0x6a, 0xfc, 0xf7, 0x38, 0xd1, 0x3e,
-    0xb6, 0x9f, 0xec, 0x5b, 0xc1, 0x85, 0x30, 0x53, 0xff, 0xaf, 0xed, 0xbf, 0x1d, 0xfd, 0x35, 0x47, 0x45, 0x6f, 0x79, 0x6b,
-    0xfc, 0xdb, 0xa0, 0xfc, 0x6f, 0x3d, 0xfa, 0xcf, 0xea, 0xb3, 0x76, 0x6f, 0xe6, 0x56, 0x9f, 0x7c, 0x64, 0x4f, 0x17, 0xd2,
-    0xbf, 0x76, 0xff, 0x4f, 0x8e, 0x8c, 0xb3, 0xd1, 0xf7, 0xff, 0x33, 0xeb, 0x5f, 0x6a, 0xdf, 0xef, 0x0b, 0xe9, 0xdf, 0xe2,
-    0xb0, 0x1a, 0xe7, 0x31, 0x19, 0xe3, 0x44, 0x43, 0xff, 0x38, 0xfd, 0xf5, 0xd8, 0x1e, 0xef, 0x81, 0x53, 0xeb, 0x8d, 0x2e,
-    0xaf, 0xe1, 0xd1, 0x7f, 0x76, 0x5b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xb4,
-    0xe4, 0x8b, 0x2b, 0xaf, 0x3e, 0xe7, 0xe2, 0xda, 0x62, 0x69, 0xd5, 0x31, 0x17, 0x4a, 0x14, 0xa7, 0x43, 0xdb, 0xce, 0xc2,
-    0x26, 0xe6, 0x3a, 0x8c, 0xbd, 0xce, 0xfa, 0xb5, 0xc4, 0x9e, 0x6f, 0x0f, 0x7f, 0xbe, 0xb8, 0xd6, 0xb6, 0xa4, 0x62, 0xf6,
-    0xb0, 0xbc, 0xab, 0x71, 0x4f, 0x9f, 0x99, 0xbc, 0x8a, 0x5e, 0x7b, 0x4f, 0xfc, 0x75, 0x1e, 0x7b, 0xbe, 0x9e, 0x7c, 0x31,
-    0x67, 0xcf, 0x70, 0xe1, 0xd1, 0xff, 0xd6, 0x68, 0x75, 0x52, 0x79, 0x33, 0xa0, 0xac, 0x5a, 0xa6, 0xe6, 0x0f, 0x3d, 0xdb,
-    0x75, 0x46, 0xea, 0xaf, 0xc4, 0xd5, 0x59, 0xca, 0x3c, 0xd7, 0x79, 0x54, 0xd9, 0xc8, 0xe3, 0xaa, 0xfa, 0x9f, 0x59, 0xe3,
-    0xde, 0xfb, 0xff, 0xad, 0xeb, 0x7f, 0xf6, 0xb9, 0xaf, 0xc5, 0x21, 0x6f, 0xe2, 0x3e, 0xe1, 0x23, 0xca, 0x8e, 0xd4, 0xbf,
-    0xc7, 0x9d, 0x76, 0xb6, 0x18, 0x6f, 0xbd, 0x9e, 0x5b, 0x8a, 0xff, 0x51, 0xcf, 0xd5, 0xe7, 0xd3, 0xdf, 0xd0, 0xff, 0xa6,
-    0xf5, 0x4f, 0x37, 0xf9, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd7, 0xa5, 0xf6, 0x3b, 0x55,
-    0xab, 0xec, 0x0c, 0x6a, 0x8b, 0xfb, 0x02, 0xdb, 0xae, 0x6f, 0x0d, 0xcf, 0x5f, 0x96, 0xf6, 0xce, 0xec, 0xfb, 0x9d, 0xaf,
-    0xde, 0x77, 0x65, 0xaf, 0xe1, 0x3a, 0xbe, 0x40, 0x3d, 0x73, 0xd6, 0xd1, 0x9e, 0xbf, 0xd2, 0x4e, 0xbb, 0xca, 0x0e, 0xd6,
-    0x31, 0xeb, 0x1e, 0xd7, 0xf1, 0xff, 0x6d, 0xbd, 0x9e, 0x4f, 0xb0, 0x5e, 0x32, 0x72, 0xdd, 0x4b, 0x1d, 0x5b, 0x56, 0xe8,
-    0xdb, 0xb5, 0xd6, 0xbd, 0x6a, 0x25, 0xf9, 0x02, 0xde, 0xa6, 0xbe, 0x11, 0xac, 0xf8, 0xa6, 0x29, 0xbb, 0x1d, 0xdf, 0xcb,
-    0x7e, 0xcc, 0x9d, 0xbb, 0x0f, 0xce, 0xa4, 0xf1, 0xc8, 0xfb, 0x7f, 0xdd, 0xdf, 0x1d, 0x79, 0xdf, 0x58, 0x4b, 0xff, 0x33,
-    0x5c, 0x43, 0xdd, 0xef, 0x63, 0x43, 0xf4, 0xaf, 0xd7, 0x39, 0xb7, 0xfe, 0xab, 0xcd, 0x0d, 0xb3, 0xbc, 0x3b, 0xba, 0xfe,
-    0xd7, 0xf5, 0x7e, 0xcd, 0x68, 0xe7, 0x2c, 0xed, 0x95, 0x71, 0x67, 0x85, 0xac, 0xd1, 0x57, 0xf0, 0xbd, 0xcd, 0x68, 0x27,
-    0xbe, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x46, 0xf3, 0x39, 0xcd, 0xce, 0xe1, 0xb7, 0xba,
-    0x9b, 0xef, 0xd8, 0xf3, 0xf5, 0x79, 0x00, 0x95, 0x96, 0x1e, 0x9b, 0xc3, 0xaf, 0x7f, 0x75, 0xe5, 0xec, 0x19, 0xfc, 0xea,
-    0xab, 0x5c, 0x57, 0x5e, 0x3f, 0x68, 0x8b, 0xff, 0x5b, 0x76, 0x71, 0x6b, 0x33, 0xc0, 0xd1, 0xb9, 0x8d, 0x7a, 0xca, 0xfa,
-    0xb2, 0xa2, 0xcc, 0x70, 0x1d, 0x7a, 0x33, 0xcd, 0xa8, 0x65, 0xbd, 0x2e, 0xc0, 0xf5, 0x33, 0xe3, 0xf5, 0xdd, 0xff, 0x57,
-    0x5b, 0xcd, 0x8f, 0x2e, 0xd3, 0xf5, 0x3f, 0x4b, 0xfc, 0x47, 0x39, 0xd9, 0xd6, 0x58, 0xcd, 0x5f, 0x4b, 0xff, 0x24, 0xcc,
-    0xb9, 0x47, 0x97, 0x79, 0xbe, 0xaf, 0x30, 0x7f, 0x0e, 0x9b, 0xa1, 0xbf, 0x75, 0xc6, 0xff, 0x95, 0xf3, 0x57, 0x5d, 0x25,
-    0x8b, 0x25, 0xd9, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xcd, 0xfb, 0x67, 0x15,
-    0x3f, 0xda, 0x3a, 0xde, 0x40, 0xc5, 0xfb, 0x12, 0x7d, 0x05, 0x3d, 0x59, 0xfc, 0x4c, 0xf0, 0xb5, 0x8d, 0x76, 0x0e, 0x95,
-    0xf3, 0xf8, 0xad, 0xe4, 0x0d, 0xdc, 0xef, 0xbb, 0x1c, 0xee, 0xfb, 0x8c, 0xcf, 0xe2, 0x97, 0x96, 0xd4, 0xff, 0x7c, 0x0e,
-    0xb7, 0xed, 0x92, 0x7c, 0x82, 0xbe, 0x5b, 0x71, 0xfd, 0xf6, 0xfc, 0x2b, 0xdc, 0x6a, 0x76, 0xbc, 0x51, 0x65, 0x4a, 0xcd,
-    0x99, 0x99, 0x36, 0xcf, 0x93, 0xc7, 0x4d, 0xf3, 0x76, 0x1d, 0xeb, 0x52, 0x9a, 0xe5, 0x7d, 0xba, 0x05, 0xfd, 0xd3, 0xf2,
-    0xee, 0x36, 0xf4, 0x47, 0x7f, 0x6b, 0xf0, 0x30, 0x9f, 0x47, 0xff, 0x73, 0x3b, 0xdc, 0xe6, 0x65, 0x1b, 0x36, 0xf9, 0x1b,
-    0x0a, 0x3c, 0xeb, 0x5f, 0xe3, 0xd3, 0x8a, 0xf2, 0xfd, 0x2c, 0xf4, 0xbf, 0xee, 0xe7, 0xd4, 0xf3, 0x7d, 0x36, 0x04, 0x00,
+    0x9d, 0xbd, 0x8e, 0x1d, 0x35, 0x18, 0x86, 0x8d, 0x39, 0x1d, 0x25, 0xa2, 0x00, 0x09, 0x91, 0x06, 0x89, 0x86, 0x86, 0x28,
+    0x12, 0x74, 0x7b, 0x01, 0xd4, 0x08, 0x51, 0x20, 0x2a, 0x1a, 0xe0, 0x1e, 0xc8, 0x95, 0xa5, 0xa1, 0xe7, 0x06, 0xb8, 0x0b,
+    0x2e, 0xe0, 0x8b, 0x36, 0x9b, 0xdd, 0xec, 0xee, 0x99, 0xb1, 0xfd, 0xfd, 0xd8, 0x9e, 0x9f, 0x67, 0x1f, 0x25, 0x52, 0xc6,
+    0x67, 0x66, 0x6c, 0xbf, 0xb6, 0x67, 0x36, 0xdf, 0x7b, 0x3e, 0x4b, 0x02, 0x00, 0x00, 0x00, 0xb8, 0xe2, 0xf6, 0x67, 0xf9,
+    0xd8, 0x52, 0x49, 0x7a, 0x5f, 0xd2, 0x7e, 0xad, 0xfb, 0xe3, 0x77, 0x25, 0x79, 0xf5, 0x13, 0xcb, 0x57, 0xcc, 0x2b, 0x75,
+    0x58, 0xab, 0xf5, 0x7a, 0xcd, 0x92, 0xaa, 0x64, 0xfd, 0xfa, 0xeb, 0xfd, 0x95, 0x14, 0x6d, 0x78, 0xfa, 0x93, 0x14, 0x6d,
+    0x29, 0x9f, 0xd7, 0x5b, 0xff, 0x72, 0x1f, 0xac, 0x97, 0xdd, 0xfd, 0xb9, 0xed, 0x89, 0xac, 0xb8, 0xe6, 0xfa, 0xe7, 0x93,
+    0x6a, 0x04, 0xea, 0x47, 0x86, 0xae, 0x77, 0x4b, 0x9f, 0xae, 0xb5, 0x41, 0x94, 0x35, 0xb8, 0x3f, 0xde, 0x53, 0xff, 0xf4,
+    0x70, 0x8f, 0xf6, 0xf9, 0x5f, 0x53, 0x45, 0xe4, 0xb2, 0xd8, 0x13, 0xa9, 0xa8, 0x4d, 0x36, 0xdc, 0x29, 0x62, 0x34, 0xa7,
+    0xa0, 0xde, 0xcd, 0xc5, 0x36, 0x88, 0x41, 0x7f, 0xdb, 0x08, 0x8d, 0xba, 0x8e, 0x6d, 0xfe, 0xdf, 0x72, 0x79, 0x37, 0x02,
+    0xca, 0x57, 0xf5, 0xd7, 0x38, 0x6a, 0xfe, 0xa7, 0xae, 0xea, 0x7b, 0xf4, 0x1f, 0xb1, 0xfe, 0x97, 0x3f, 0x5f, 0xee, 0xb3,
+    0xe5, 0xeb, 0xe5, 0xf7, 0xf3, 0x5f, 0xff, 0xf4, 0x8c, 0xd0, 0x5f, 0xff, 0xfc, 0xef, 0xab, 0x7e, 0xc4, 0xfa, 0xbf, 0xc5,
+    0xf7, 0xc8, 0xda, 0x73, 0x7c, 0x79, 0xec, 0x48, 0xf7, 0xf9, 0x3f, 0x83, 0x92, 0xfa, 0xb5, 0xf7, 0x3f, 0x71, 0xac, 0xb3,
+    0xbd, 0xe7, 0xbf, 0xe5, 0x5a, 0xf7, 0x3d, 0x21, 0x86, 0xf9, 0xb9, 0x57, 0xfd, 0x4b, 0xea, 0x43, 0xc4, 0x5a, 0xa3, 0x79,
+    0xca, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x33, 0x62, 0x52, 0x8e, 0xbf, 0x47,
+    0x46, 0x22, 0x7b, 0x95, 0xb5, 0xbb, 0x0d, 0x34, 0xce, 0x95, 0x6c, 0xe8, 0xb1, 0x59, 0xfd, 0xd2, 0xcb, 0xed, 0x97, 0xd4,
+    0xde, 0x8c, 0x96, 0xda, 0x58, 0xef, 0xe7, 0xd1, 0x3f, 0x2b, 0x63, 0xaf, 0x65, 0x87, 0xde, 0x9a, 0x9f, 0x71, 0x3d, 0x72,
+    0xed, 0x6d, 0x9f, 0xb6, 0x4c, 0xb7, 0x02, 0xa4, 0x21, 0xf3, 0xbf, 0xcd, 0xc3, 0xd4, 0xc7, 0xdd, 0xb8, 0xec, 0x33, 0x2b,
+    0xd5, 0xf5, 0xa2, 0x76, 0x14, 0x7e, 0x88, 0x36, 0xf7, 0x1d, 0xdf, 0xd1, 0xae, 0x4f, 0xab, 0xa3, 0xc4, 0x32, 0x26, 0x2d,
+    0x0e, 0xd6, 0x88, 0xf5, 0x3f, 0x66, 0xfe, 0x97, 0xc6, 0xa9, 0x3c, 0xfb, 0x13, 0xb3, 0x8e, 0xcf, 0xd2, 0xbf, 0xc7, 0x5a,
+    0x9d, 0x1a, 0x56, 0x05, 0x6b, 0x5d, 0x6c, 0xcf, 0x7f, 0x8b, 0x53, 0xa7, 0xe4, 0x56, 0xb2, 0xbf, 0x4f, 0xcd, 0x98, 0xff,
+    0x9e, 0x39, 0x6e, 0x5b, 0xff, 0xcb, 0xfa, 0xc7, 0xb9, 0xdb, 0x64, 0xca, 0xdb, 0x74, 0xcb, 0x1c, 0xdb, 0xd6, 0xf3, 0x1f,
+    0x22, 0xfd, 0x8c, 0xf3, 0x7e, 0x9f, 0x42, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xe8, 0xeb, 0xc7, 0x1b, 0xe5, 0x29, 0x91, 0xc6, 0x2c, 0x3e, 0xeb, 0x7e, 0xad, 0x51, 0x6e, 0xbd, 0xf1, 0xfd, 0x1d, 0xed,
+    0xfb, 0x88, 0x88, 0x49, 0x5b, 0x32, 0x98, 0xf9, 0xbd, 0x88, 0xb2, 0x09, 0x47, 0x5e, 0x8f, 0x7e, 0x9b, 0xa9, 0xbf, 0xa5,
+    0x3e, 0x76, 0xfd, 0xc5, 0x98, 0x61, 0xb0, 0x96, 0x19, 0x66, 0x5b, 0xfa, 0x5b, 0x5c, 0x07, 0x1e, 0xfd, 0xeb, 0x6b, 0x55,
+    0x2a, 0xe8, 0xa1, 0x5f, 0xe3, 0xad, 0xfa, 0x8b, 0x31, 0xef, 0x63, 0x9f, 0xa7, 0x58, 0x1f, 0xfd, 0x2d, 0xfd, 0x76, 0x94,
+    0xf5, 0xbf, 0x7e, 0xcd, 0x6c, 0x5c, 0xff, 0xa5, 0xba, 0xaa, 0x48, 0xb8, 0x1b, 0x55, 0x3f, 0xa7, 0x5a, 0xfb, 0xe7, 0xbc,
+    0xfa, 0xa7, 0x2e, 0xfa, 0xf7, 0xb9, 0xe3, 0x7e, 0x9e, 0xff, 0x96, 0xec, 0xd5, 0x33, 0x7c, 0x6e, 0x3e, 0x6f, 0x78, 0xad,
+    0x74, 0x44, 0xcf, 0xf4, 0x5b, 0xff, 0x71, 0x01, 0xee, 0xc7, 0x25, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x30, 0x22, 0x46, 0x61, 0x8f, 0x9d, 0xf5, 0x88, 0x00, 0x5e, 0x1f, 0xcf, 0x0d, 0x31, 0xcc, 0xe7, 0xc7,
+    0xf3, 0x8a, 0x63, 0x44, 0x94, 0x39, 0xff, 0x6c, 0xfb, 0xf1, 0xb6, 0xe6, 0x41, 0xb3, 0x3b, 0x08, 0x23, 0xe2, 0x80, 0xe3,
+    0xf5, 0x4f, 0x4d, 0xf1, 0xd8, 0x6b, 0x25, 0xb3, 0xc1, 0x33, 0x94, 0x95, 0x19, 0x9f, 0x4a, 0x19, 0xa2, 0xf4, 0x6e, 0xa6,
+    0x7a, 0x2f, 0x58, 0x72, 0xdd, 0xd5, 0xae, 0x66, 0xdb, 0xa1, 0xb8, 0xe4, 0x8e, 0xa9, 0x79, 0x59, 0xf4, 0x2d, 0xcf, 0xea,
+    0xb6, 0xe5, 0x86, 0x35, 0xa0, 0xbf, 0x47, 0x3a, 0xa9, 0xe7, 0xa3, 0xcf, 0x77, 0xd7, 0x5f, 0xff, 0xd4, 0xe0, 0xf1, 0x8b,
+    0x76, 0xa4, 0x48, 0x65, 0xf7, 0x5b, 0xcb, 0x08, 0xe8, 0xad, 0x7f, 0xad, 0xad, 0x76, 0x97, 0x50, 0x0a, 0x1d, 0x37, 0xd1,
+    0xfa, 0xb7, 0x8c, 0xd3, 0xe8, 0x11, 0x6c, 0x19, 0x01, 0x63, 0xe6, 0xbf, 0xa8, 0xf5, 0x2f, 0xaf, 0x0d, 0xf1, 0xbd, 0x67,
+    0x71, 0x01, 0xd9, 0x56, 0x14, 0xaf, 0x5b, 0xcd, 0xa2, 0x62, 0x36, 0xad, 0x1c, 0x23, 0xde, 0xa0, 0x6d, 0x7b, 0x55, 0x47,
+    0x3b, 0x36, 0x6d, 0x3d, 0x31, 0x5e, 0x7f, 0x31, 0xef, 0xf8, 0x9d, 0x37, 0xb7, 0xfb, 0xb5, 0x6d, 0xd6, 0xd9, 0x1d, 0x7b,
+    0xf1, 0xf3, 0x7f, 0x7b, 0xdf, 0x46, 0x1b, 0x71, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xc0, 0x39, 0x73, 0x14, 0xe8, 0x33, 0xd5, 0xd4, 0xcf, 0xb0, 0xee, 0x9d, 0x35, 0x72, 0xef, 0xdc, 0x5e, 0xfb, 0xdb, 0xd9,
+    0xf3, 0x82, 0x49, 0x97, 0x1d, 0xe0, 0xe2, 0xe3, 0x66, 0xbe, 0x2c, 0x66, 0xb5, 0x1e, 0xb2, 0x66, 0xdc, 0xd2, 0x7a, 0x30,
+    0x3c, 0xbe, 0x1e, 0xab, 0xe7, 0x23, 0xde, 0x87, 0x17, 0xe3, 0x6d, 0xb3, 0xcd, 0x7f, 0x9b, 0x2f, 0x64, 0x6b, 0xfa, 0xa7,
+    0x41, 0xfe, 0x3c, 0x8f, 0xfe, 0xde, 0xdd, 0x9f, 0xed, 0x8e, 0x2c, 0x7d, 0xaf, 0x1e, 0x41, 0xff, 0x36, 0xf7, 0xae, 0xc5,
+    0x47, 0x18, 0xed, 0xc3, 0x9b, 0xa5, 0xbf, 0x98, 0xf6, 0xce, 0xf5, 0xe8, 0xef, 0xf3, 0xcb, 0x8f, 0x9b, 0xff, 0xf6, 0x3b,
+    0xa6, 0x2e, 0x3b, 0xa3, 0x7b, 0x67, 0xab, 0x2e, 0x53, 0xe3, 0x0c, 0xa7, 0x56, 0xbc, 0x5f, 0xae, 0x97, 0xb3, 0x6b, 0xae,
+    0x7b, 0x31, 0xca, 0xbf, 0xea, 0xcd, 0xfe, 0x3d, 0x43, 0x7f, 0xbc, 0x63, 0xc7, 0x1e, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x8b, 0xe3, 0xc4, 0xed, 0x47, 0xa5, 0xcd, 0xea, 0x26, 0x8b, 0x19, 0x3d,
+    0xac, 0xb9, 0x63, 0xb4, 0xde, 0x0a, 0x4d, 0xec, 0x5c, 0x13, 0xbb, 0xcc, 0x8f, 0x3e, 0x7b, 0xed, 0x83, 0xf8, 0x5e, 0x5e,
+    0x3c, 0xf0, 0x52, 0x3e, 0x5b, 0xcd, 0x72, 0xb2, 0xd4, 0x33, 0xb5, 0x73, 0xcb, 0x77, 0xb6, 0x38, 0x00, 0x74, 0xb9, 0xda,
+    0x74, 0x47, 0xb3, 0xb2, 0x9e, 0x16, 0xd7, 0x85, 0x2f, 0x43, 0x9f, 0xf5, 0xb3, 0xf9, 0x89, 0x92, 0x4f, 0x79, 0xf5, 0xa4,
+    0x77, 0x5f, 0x2e, 0xf6, 0x78, 0x5e, 0xc9, 0x75, 0x54, 0x3b, 0xb7, 0x7c, 0x67, 0xad, 0xfe, 0x59, 0x9d, 0xab, 0x4d, 0xaf,
+    0x4e, 0x8c, 0xaf, 0xd0, 0x96, 0x15, 0xc7, 0xa2, 0x7f, 0x5b, 0xfd, 0x1f, 0xaf, 0xac, 0xcf, 0x4b, 0x5f, 0x14, 0xfe, 0x75,
+    0x77, 0xf6, 0xe5, 0xe1, 0x6f, 0xfd, 0xb9, 0xa5, 0x3b, 0xeb, 0x9d, 0x3c, 0x36, 0x17, 0x97, 0x77, 0x04, 0x44, 0x66, 0x53,
+    0x4c, 0xae, 0x0c, 0xad, 0x36, 0xc7, 0x83, 0x3c, 0xd9, 0x5b, 0x5e, 0xab, 0x61, 0x69, 0xfe, 0xb7, 0xe9, 0xbf, 0x76, 0xe7,
+    0x64, 0xf4, 0xc7, 0xc9, 0x46, 0xe6, 0x7f, 0x8f, 0xac, 0x78, 0xa2, 0x9a, 0x27, 0xad, 0x6b, 0x85, 0x67, 0xfe, 0x97, 0x9e,
+    0xff, 0xf1, 0xfa, 0x5b, 0x5d, 0x70, 0xa3, 0xf5, 0x4f, 0x66, 0x9f, 0xaa, 0xf5, 0xad, 0xd1, 0xbe, 0x02, 0x94, 0x9f, 0xc2,
+    0x75, 0xfd, 0xd7, 0xa9, 0xeb, 0x6f, 0x79, 0xfe, 0xcb, 0x4e, 0xf4, 0x4f, 0x0e, 0x67, 0x6d, 0x94, 0x1f, 0xd5, 0xff, 0xfe,
+    0xdf, 0x53, 0x7f, 0xfd, 0xfb, 0x7f, 0xac, 0xb3, 0xd2, 0x96, 0xd5, 0x71, 0x4f, 0xdf, 0x94, 0xf3, 0xb7, 0xe6, 0xd7, 0x47,
+    0xbf, 0xc3, 0xbd, 0x92, 0x1f, 0x86, 0x9d, 0x0b, 0x31, 0x8e, 0x57, 0x3c, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0xb0, 0x75, 0xea, 0x5e, 0xb4, 0xcf, 0x17, 0xe3, 0x48, 0xb5, 0xf3, 0xbc, 0xe5, 0x5a, 0x97, 0xda, 0xd7,
+    0xf2, 0xad, 0x7c, 0x79, 0x75, 0xf4, 0x8d, 0xbc, 0x91, 0xdf, 0xdc, 0x7d, 0x50, 0x2b, 0xbf, 0x8b, 0xb4, 0x2d, 0xc7, 0xdb,
+    0x96, 0x7e, 0x2c, 0x9f, 0xd1, 0xc7, 0xb5, 0xdb, 0xa2, 0x7f, 0x35, 0x3f, 0xd9, 0x8f, 0x72, 0x23, 0x7f, 0x1a, 0xce, 0xf3,
+    0x96, 0x6b, 0xf4, 0xff, 0x42, 0xfe, 0x93, 0x7f, 0xe5, 0xb5, 0x7c, 0xb5, 0x78, 0xd6, 0xdf, 0xee, 0x3e, 0xf0, 0xf8, 0xf5,
+    0x34, 0x3b, 0x21, 0xc7, 0x45, 0x18, 0xdb, 0xa3, 0xff, 0xb5, 0x78, 0xf2, 0x2f, 0xf2, 0x91, 0xfc, 0xb4, 0xe0, 0x44, 0xab,
+    0x9d, 0xe7, 0x2d, 0xd7, 0xe8, 0xff, 0x97, 0x88, 0xfc, 0x23, 0xbf, 0xcb, 0xc7, 0x57, 0x25, 0x37, 0x72, 0xd3, 0x70, 0x6d,
+    0x7f, 0x5d, 0x2f, 0xef, 0xea, 0x75, 0x71, 0xee, 0x84, 0x1d, 0x39, 0xff, 0xa3, 0xfc, 0x04, 0x9f, 0xca, 0xcf, 0xf2, 0x5d,
+    0x87, 0x3e, 0x8b, 0xd4, 0xff, 0x1b, 0xf9, 0x7f, 0x45, 0xff, 0x98, 0x3e, 0xf0, 0xf8, 0xf5, 0xae, 0xb5, 0x8d, 0x71, 0x16,
+    0x48, 0x63, 0x26, 0x5d, 0x6f, 0xdb, 0x7b, 0xf5, 0x99, 0x45, 0xff, 0xf5, 0x36, 0x7d, 0x22, 0xaf, 0x27, 0xea, 0x5f, 0x7b,
+    0xfe, 0x27, 0x45, 0xce, 0x58, 0x8d, 0x1b, 0x35, 0xc2, 0xa9, 0xb2, 0x27, 0xfd, 0x4b, 0xe5, 0x7f, 0x4c, 0xd4, 0x7f, 0x8c,
+    0x6b, 0xa8, 0xfd, 0xba, 0xe8, 0xbf, 0x2d, 0xfd, 0x2d, 0xef, 0xf6, 0x9e, 0xf5, 0x50, 0x73, 0x2f, 0xab, 0x9f, 0xac, 0x76,
+    0x9e, 0xb7, 0x5c, 0x8b, 0x47, 0xff, 0xd1, 0x75, 0xdd, 0x8f, 0x53, 0x11, 0xce, 0xed, 0x54, 0x05, 0xf4, 0x07, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3d, 0x93, 0x2b, 0xb9, 0xdf, 0x72, 0xa1, 0xc4, 0xe2, 0x67, 0x68, 0xdb, 0x01,
+    0x58, 0x8c, 0x39, 0x09, 0x73, 0x68, 0x3b, 0xeb, 0x6d, 0x19, 0x77, 0xbf, 0x52, 0xcb, 0x4b, 0x7a, 0xd4, 0xff, 0xbf, 0x38,
+    0x17, 0x4a, 0xb2, 0x3a, 0x77, 0x9f, 0x6f, 0x07, 0x58, 0x9f, 0xb7, 0x21, 0x9b, 0x3c, 0x91, 0xa5, 0x76, 0x6e, 0xe7, 0x7e,
+    0xe5, 0xb2, 0x3c, 0x24, 0xe7, 0xcb, 0xec, 0xb8, 0xc5, 0x99, 0x63, 0x1a, 0x52, 0x75, 0x41, 0x8f, 0xf3, 0x11, 0xf4, 0x2e,
+    0xb3, 0xe6, 0xf9, 0xdc, 0x62, 0x5b, 0x22, 0xcb, 0xd6, 0xe7, 0xbf, 0x6d, 0x7e, 0xd8, 0xd7, 0x9b, 0xb1, 0x65, 0x1a, 0xdf,
+    0xdb, 0xa8, 0xb2, 0xd1, 0xf7, 0x8c, 0xf2, 0x7c, 0x1d, 0x41, 0x7f, 0x4b, 0x86, 0xd0, 0xa3, 0xeb, 0x2f, 0xc1, 0x99, 0xf3,
+    0x66, 0xae, 0x6f, 0xbe, 0x6b, 0xa7, 0x21, 0x65, 0x33, 0xf4, 0x6f, 0xd9, 0x23, 0x7e, 0xaf, 0xf3, 0x5f, 0x5b, 0xe7, 0xb3,
+    0xcd, 0xff, 0xb6, 0xef, 0x47, 0x1c, 0x5d, 0x7f, 0x41, 0xff, 0x40, 0xcf, 0xd7, 0x1e, 0xe7, 0xff, 0x59, 0xdf, 0xff, 0xdb,
+    0xc6, 0x21, 0x9e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe7, 0x61, 0xb3, 0x7a, 0x03, 0xdb,
+    0xbc, 0x6f, 0xdb, 0xf0, 0xfd, 0x65, 0xd3, 0x6e, 0x62, 0xe5, 0x36, 0xd8, 0xbc, 0x64, 0x7e, 0xdf, 0x57, 0xbc, 0x87, 0xcd,
+    0xda, 0xaf, 0xf6, 0x1c, 0x59, 0xa3, 0x7d, 0x7f, 0xa5, 0x5d, 0x71, 0xad, 0xbb, 0x4d, 0xdb, 0xc6, 0xf0, 0x7a, 0x1b, 0xf6,
+    0xec, 0x01, 0x5c, 0x3a, 0x9e, 0x77, 0xb0, 0xfb, 0xd6, 0xac, 0xd8, 0xd7, 0x11, 0x62, 0x66, 0xa9, 0x5a, 0x92, 0x37, 0xef,
+    0x53, 0xaa, 0x97, 0x8d, 0xf5, 0xa7, 0x1c, 0xc5, 0xff, 0x75, 0xf4, 0xb2, 0xf1, 0x8e, 0x81, 0x7d, 0xc5, 0xff, 0xf7, 0xa4,
+    0xb1, 0xcd, 0xbf, 0xcb, 0xfc, 0xf7, 0x3c, 0xa7, 0xb6, 0x36, 0x36, 0xb4, 0x1e, 0xad, 0xda, 0xb3, 0xc1, 0x52, 0x76, 0x16,
+    0xff, 0xcf, 0x96, 0xe6, 0xf8, 0x96, 0xde, 0xc3, 0xd0, 0xbf, 0x5f, 0x3d, 0x8f, 0xf2, 0x3d, 0xa4, 0xbd, 0xf8, 0xdb, 0xb6,
+    0xd4, 0x86, 0xb3, 0x7f, 0x0f, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xc1, 0x98, 0x3c, 0x7e,
+    0x5b, 0x77, 0xf3, 0x8d, 0xbd, 0x9f, 0x3d, 0x8b, 0x5f, 0x0e, 0xd8, 0xf1, 0x37, 0xca, 0xab, 0xd7, 0xe2, 0x75, 0xb4, 0x46,
+    0x57, 0x8e, 0x9c, 0xc5, 0x2f, 0x15, 0x47, 0x46, 0xd9, 0xcd, 0x69, 0x1b, 0xa7, 0x33, 0x63, 0x4a, 0xa2, 0x8e, 0x92, 0x1f,
+    0xdf, 0x6d, 0x1d, 0x1f, 0x01, 0x8b, 0xa8, 0x4d, 0x6c, 0x99, 0xcf, 0xcb, 0x34, 0xc3, 0x75, 0xa8, 0xcd, 0x36, 0x63, 0x2d,
+    0x6b, 0x73, 0x01, 0xce, 0x71, 0x01, 0xc4, 0x94, 0xf9, 0x9e, 0xff, 0x5b, 0x8b, 0xe6, 0xf7, 0xc9, 0xe2, 0xa6, 0x2f, 0xed,
+    0xe3, 0xaa, 0x98, 0xe5, 0x3b, 0x4b, 0xa6, 0xdd, 0xab, 0x67, 0x44, 0xf3, 0x8f, 0xaf, 0xff, 0x2c, 0xdf, 0xd9, 0x39, 0xb3,
+    0x78, 0xb5, 0x8c, 0xc4, 0x19, 0xf3, 0x7f, 0xbe, 0x8f, 0x67, 0x7b, 0x8e, 0xa4, 0xf1, 0xbd, 0x53, 0xd6, 0x7f, 0xbc, 0x1a,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x67, 0x07, 0x60, 0xb4, 0x37, 0xd0, 0xe2, 0x7d,
+    0x89, 0xce, 0x80, 0xe7, 0xc9, 0xe2, 0x27, 0x06, 0x5f, 0xdb, 0x3e, 0xfd, 0x48, 0x3d, 0xf6, 0xf8, 0x5d, 0xef, 0xbb, 0x1c,
+    0xee, 0xfb, 0x8c, 0xcf, 0xe2, 0x97, 0x4e, 0xa4, 0xff, 0x48, 0x4f, 0x59, 0x29, 0x8b, 0xdb, 0x5e, 0xda, 0x70, 0x1e, 0x37,
+    0x62, 0x7c, 0xc4, 0xd9, 0x93, 0x1d, 0xaf, 0x47, 0x99, 0xe5, 0xcc, 0x1e, 0xbb, 0x30, 0xcf, 0xd5, 0xbf, 0x8f, 0xa3, 0xce,
+    0x32, 0xda, 0x46, 0xea, 0x3f, 0xcf, 0xfb, 0x74, 0x06, 0xfd, 0xad, 0x35, 0x41, 0x7f, 0xf4, 0xdf, 0x8a, 0xfe, 0x72, 0x12,
+    0xfd, 0xb7, 0xe1, 0xff, 0x9b, 0x97, 0x6d, 0x58, 0xcc, 0xdf, 0x50, 0xe0, 0x5d, 0xff, 0x38, 0x2b, 0xa0, 0xf6, 0x1b, 0x4a,
+    0xe8, 0x7f, 0xdc, 0xdf, 0x53, 0xf9, 0xdd, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xb2,
-    0x0c, 0xa2, 0x3f, 0xfa, 0xdf, 0xb8, 0xfe, 0xff, 0x03 };
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x90, 0xb9, 0x10, 0xfd, 0xd1, 0xff, 0xe4, 0xfa, 0xbf, 0x05 };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle jungleFontRecs[189] = {
@@ -245,101 +246,101 @@
     { 103, 104, 6 , 7 },
     { 117, 104, 6 , 7 },
     { 131, 104, 6 , 7 },
-    { 145, 104, 8 , 8 },
-    { 161, 104, 6 , 9 },
-    { 175, 104, 8 , 8 },
-    { 191, 104, 7 , 7 },
-    { 206, 104, 8 , 8 },
-    { 222, 104, 6 , 5 },
-    { 236, 104, 8 , 8 },
-    { 4, 124, 7 , 7 },
-    { 19, 124, 8 , 8 },
-    { 35, 124, 4 , 4 },
-    { 47, 124, 8 , 8 },
-    { 63, 124, 8 , 8 },
-    { 79, 124, 8 , 8 },
-    { 95, 124, 8 , 8 },
-    { 111, 124, 6 , 7 },
-    { 125, 124, 5 , 8 },
-    { 138, 124, 3 , 3 },
-    { 149, 124, 8 , 8 },
-    { 165, 124, 8 , 8 },
-    { 181, 124, 8 , 8 },
-    { 197, 124, 6 , 5 },
-    { 211, 124, 10 , 7 },
-    { 229, 124, 10 , 5 },
-    { 4, 144, 8 , 8 },
-    { 20, 144, 6 , 7 },
-    { 34, 144, 6 , 10 },
-    { 48, 144, 6 , 10 },
-    { 62, 144, 6 , 10 },
-    { 76, 144, 6 , 10 },
-    { 90, 144, 6 , 10 },
-    { 104, 144, 6 , 10 },
-    { 118, 144, 10 , 7 },
-    { 136, 144, 6 , 9 },
-    { 150, 144, 6 , 10 },
-    { 164, 144, 6 , 10 },
-    { 178, 144, 6 , 10 },
-    { 192, 144, 6 , 10 },
-    { 206, 144, 6 , 10 },
-    { 220, 144, 6 , 10 },
-    { 234, 144, 6 , 10 },
+    { 145, 104, 0 , 0 },
+    { 153, 104, 6 , 9 },
+    { 167, 104, 0 , 0 },
+    { 175, 104, 7 , 7 },
+    { 190, 104, 8 , 8 },
+    { 206, 104, 6 , 5 },
+    { 220, 104, 8 , 8 },
+    { 236, 104, 7 , 7 },
+    { 4, 124, 8 , 8 },
+    { 20, 124, 4 , 4 },
+    { 32, 124, 8 , 8 },
+    { 48, 124, 8 , 8 },
+    { 64, 124, 8 , 8 },
+    { 80, 124, 0 , 0 },
+    { 88, 124, 6 , 7 },
+    { 102, 124, 5 , 8 },
+    { 115, 124, 3 , 3 },
+    { 126, 124, 0 , 0 },
+    { 134, 124, 8 , 8 },
+    { 150, 124, 8 , 8 },
+    { 166, 124, 6 , 5 },
+    { 180, 124, 10 , 7 },
+    { 198, 124, 10 , 5 },
+    { 216, 124, 0 , 0 },
+    { 224, 124, 6 , 7 },
+    { 238, 124, 6 , 10 },
+    { 4, 144, 6 , 10 },
+    { 18, 144, 6 , 10 },
+    { 32, 144, 6 , 10 },
+    { 46, 144, 6 , 10 },
+    { 60, 144, 6 , 10 },
+    { 74, 144, 10 , 7 },
+    { 92, 144, 6 , 9 },
+    { 106, 144, 6 , 10 },
+    { 120, 144, 6 , 10 },
+    { 134, 144, 6 , 10 },
+    { 148, 144, 6 , 10 },
+    { 162, 144, 6 , 10 },
+    { 176, 144, 6 , 10 },
+    { 190, 144, 6 , 10 },
+    { 204, 144, 6 , 10 },
+    { 218, 144, 6 , 7 },
+    { 232, 144, 6 , 10 },
     { 4, 164, 6 , 10 },
-    { 18, 164, 6 , 7 },
+    { 18, 164, 6 , 10 },
     { 32, 164, 6 , 10 },
     { 46, 164, 6 , 10 },
     { 60, 164, 6 , 10 },
-    { 74, 164, 6 , 10 },
-    { 88, 164, 6 , 10 },
+    { 74, 164, 6 , 5 },
+    { 88, 164, 6 , 7 },
     { 102, 164, 6 , 10 },
-    { 116, 164, 6 , 5 },
-    { 130, 164, 6 , 7 },
+    { 116, 164, 6 , 10 },
+    { 130, 164, 6 , 10 },
     { 144, 164, 6 , 10 },
     { 158, 164, 6 , 10 },
-    { 172, 164, 6 , 10 },
-    { 186, 164, 6 , 10 },
-    { 200, 164, 6 , 10 },
-    { 214, 164, 6 , 7 },
-    { 228, 164, 6 , 7 },
+    { 172, 164, 6 , 7 },
+    { 186, 164, 6 , 7 },
+    { 200, 164, 6 , 8 },
+    { 214, 164, 6 , 8 },
+    { 228, 164, 6 , 8 },
     { 4, 184, 6 , 8 },
     { 18, 184, 6 , 8 },
     { 32, 184, 6 , 8 },
-    { 46, 184, 6 , 8 },
-    { 60, 184, 6 , 8 },
-    { 74, 184, 6 , 8 },
-    { 88, 184, 9 , 5 },
-    { 105, 184, 6 , 7 },
+    { 46, 184, 9 , 5 },
+    { 63, 184, 6 , 7 },
+    { 77, 184, 6 , 8 },
+    { 91, 184, 6 , 8 },
+    { 105, 184, 6 , 8 },
     { 119, 184, 6 , 8 },
     { 133, 184, 6 , 8 },
     { 147, 184, 6 , 8 },
     { 161, 184, 6 , 8 },
     { 175, 184, 6 , 8 },
-    { 189, 184, 6 , 8 },
+    { 189, 184, 6 , 7 },
     { 203, 184, 6 , 8 },
     { 217, 184, 6 , 8 },
-    { 231, 184, 6 , 7 },
+    { 231, 184, 6 , 8 },
     { 4, 204, 6 , 8 },
     { 18, 204, 6 , 8 },
     { 32, 204, 6 , 8 },
-    { 46, 204, 6 , 8 },
-    { 60, 204, 6 , 8 },
-    { 74, 204, 6 , 8 },
-    { 88, 204, 5 , 5 },
-    { 101, 204, 6 , 5 },
+    { 46, 204, 5 , 5 },
+    { 59, 204, 6 , 5 },
+    { 73, 204, 6 , 8 },
+    { 87, 204, 6 , 8 },
+    { 101, 204, 6 , 8 },
     { 115, 204, 6 , 8 },
-    { 129, 204, 6 , 8 },
-    { 143, 204, 6 , 8 },
-    { 157, 204, 6 , 8 },
-    { 171, 204, 6 , 10 },
-    { 185, 204, 6 , 9 },
-    { 199, 204, 6 , 10 },
+    { 129, 204, 6 , 10 },
+    { 143, 204, 6 , 9 },
+    { 157, 204, 6 , 10 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo jungleFontGlyphs[189] = {
-    { 32, 0, 9, 5, { 0 }},
+    { 32, 0, 0, 5, { 0 }},
     { 33, 0, 2, 3, { 0 }},
     { 34, 0, 2, 6, { 0 }},
     { 35, 0, 3, 6, { 0 }},
@@ -439,9 +440,9 @@
     { 163, 0, 2, 7, { 0 }},
     { 8364, 0, 2, 7, { 0 }},
     { 165, 0, 2, 7, { 0 }},
-    { 352, 0, 1, 8, { 0 }},
+    { 352, 0, 0, 0, { 0 }},
     { 167, 0, 1, 7, { 0 }},
-    { 353, 0, 1, 8, { 0 }},
+    { 353, 0, 0, 0, { 0 }},
     { 169, 0, 2, 8, { 0 }},
     { 170, 0, 1, 8, { 0 }},
     { 171, 0, 3, 7, { 0 }},
@@ -452,17 +453,17 @@
     { 177, 0, 1, 8, { 0 }},
     { 178, 0, 1, 8, { 0 }},
     { 179, 0, 1, 8, { 0 }},
-    { 381, 0, 1, 8, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
     { 181, 0, 4, 7, { 0 }},
     { 182, 0, 1, 4, { 0 }},
     { 183, 0, 4, 4, { 0 }},
-    { 382, 0, 1, 8, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
     { 185, 0, 1, 8, { 0 }},
     { 186, 0, 1, 8, { 0 }},
     { 187, 0, 3, 7, { 0 }},
     { 338, 0, 2, 11, { 0 }},
     { 339, 0, 4, 11, { 0 }},
-    { 376, 0, 1, 8, { 0 }},
+    { 376, 0, 0, 0, { 0 }},
     { 191, 0, 2, 7, { 0 }},
     { 192, 0, -1, 7, { 0 }},
     { 193, 0, -1, 7, { 0 }},
diff --git a/raygui/styles/lavanda/style_lavanda.h b/raygui/styles/lavanda/style_lavanda.h
--- a/raygui/styles/lavanda/style_lavanda.h
+++ b/raygui/styles/lavanda/style_lavanda.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,22 +15,22 @@
 
 // Custom style name: Lavanda
 static const GuiStyleProp lavandaStyleProps[LAVANDA_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0xab9bd3ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x3e4350ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xdadaf4ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xee84a0ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xf4b7c7ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xb7657bff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xd5c8dbff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0x966ec0ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0xd7ccf7ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x8fa2bdff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x6b798dff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x8292a9ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 18, 0x84adb7ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x5b5b81ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0xab9bd3ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x3e4350ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xdadaf4ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xee84a0ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xf4b7c7ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xb7657bff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xd5c8dbff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0x966ec0ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xd7ccf7ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x8fa2bdff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x6b798dff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x8292a9ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 18, (int)0x84adb7ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x5b5b81ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
 // WARNING: This style uses a custom font: "Cartridge.ttf" (size: 16, spacing: 1)
@@ -368,7 +368,7 @@
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo lavandaFontGlyphs[189] = {
-    { 32, 0, 12, 5, { 0 }},
+    { 32, 0, 0, 5, { 0 }},
     { 33, 0, 3, 2, { 0 }},
     { 34, 0, 3, 4, { 0 }},
     { 35, 0, 3, 8, { 0 }},
diff --git a/raygui/styles/rltech/style_rltech.h b/raygui/styles/rltech/style_rltech.h
new file mode 100644
--- /dev/null
+++ b/raygui/styles/rltech/style_rltech.h
@@ -0,0 +1,572 @@
+//////////////////////////////////////////////////////////////////////////////////
+//                                                                              //
+// StyleAsCode exporter v2.0 - Style data exported as a values array            //
+//                                                                              //
+// USAGE: On init call: GuiLoadStyleRLTech();                                   //
+//                                                                              //
+// more info and bugs-report:  github.com/raysan5/raygui                        //
+// feedback and support:       ray[at]raylibtech.com                            //
+//                                                                              //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
+//                                                                              //
+//////////////////////////////////////////////////////////////////////////////////
+
+#define RLTECH_STYLE_PROPS_COUNT  15
+
+// Custom style name: RLTech
+static const GuiStyleProp rltechStyleProps[RLTECH_STYLE_PROPS_COUNT] = {
+    { 0, 0, (int)0x000000ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0xf5f5f5ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x000000ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xe10000ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xffffffff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xed0000ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xed0000ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0x0f0f0fff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0xff2323ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0xcacacaff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0xe3e3e3ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0xb3b3b3ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 18, (int)0x352c2cff },    // DEFAULT_LINE_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+};
+
+// WARNING: This style uses a custom font: "2a03.ttf" (size: 16, spacing: 1)
+
+#define RLTECH_STYLE_FONT_ATLAS_COMP_SIZE 1950
+
+// Font atlas image pixels data: DEFLATE compressed
+static unsigned char rltechFontData[RLTECH_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
+    0xdd, 0xcb, 0x76, 0xc3, 0x2a, 0x12, 0x05, 0x50, 0xfe, 0xff, 0xa7, 0xab, 0x07, 0x77, 0xd0, 0x8f, 0xd5, 0xb1, 0xa0, 0x28,
+    0x10, 0xd8, 0x3b, 0x7b, 0x16, 0xc7, 0xb6, 0x1e, 0x1c, 0x03, 0x8a, 0x29, 0x45, 0x03, 0x00, 0x00, 0x00, 0x88, 0xf8, 0xbf,
+    0xbf, 0x89, 0x3f, 0xfe, 0x32, 0xba, 0x5f, 0xe5, 0x9f, 0xdf, 0xc6, 0x87, 0x67, 0xfd, 0xfb, 0xa7, 0xef, 0xf5, 0xa2, 0xf3,
+    0x5d, 0xff, 0xfe, 0xfd, 0xdf, 0x5b, 0xd2, 0xbf, 0x6d, 0x15, 0xaf, 0x30, 0xfa, 0xd7, 0x9f, 0xb7, 0xa4, 0xea, 0xd5, 0x6a,
+    0x1f, 0xd9, 0x77, 0xe4, 0x9f, 0xb7, 0x6e, 0xe7, 0xb1, 0x68, 0x9b, 0x8e, 0x6c, 0x0c, 0xb4, 0xfa, 0xb5, 0xf9, 0xff, 0xbc,
+    0xad, 0xf1, 0x78, 0xee, 0x67, 0xb2, 0x9e, 0x7d, 0xcf, 0x8a, 0x23, 0xd7, 0xb7, 0xbd, 0x31, 0xf9, 0xd7, 0x77, 0x3e, 0x92,
+    0x3d, 0x6a, 0xe3, 0x7f, 0xdf, 0x9b, 0x87, 0xda, 0x3d, 0x6e, 0xa5, 0xcf, 0x69, 0x8b, 0xdf, 0xa7, 0x22, 0xff, 0xcf, 0x29,
+    0x1f, 0xef, 0xff, 0x33, 0x79, 0xea, 0x69, 0x11, 0xa3, 0x9f, 0xc3, 0xed, 0xf5, 0xfc, 0x67, 0x1e, 0xa9, 0x6d, 0x71, 0x6d,
+    0x7b, 0xfe, 0xc7, 0x47, 0x85, 0xe3, 0xc7, 0x27, 0x3e, 0xb4, 0x41, 0xf9, 0x1f, 0x7b, 0x56, 0x66, 0x5e, 0x90, 0x1b, 0xa5,
+    0xcf, 0x8d, 0xdc, 0x5b, 0xf2, 0x33, 0xa7, 0x32, 0xfd, 0xf2, 0xbf, 0xab, 0xff, 0xef, 0xe9, 0x31, 0xe4, 0x7f, 0xfe, 0x1c,
+    0x64, 0xe6, 0x2a, 0x4f, 0xf9, 0x1f, 0xe9, 0x91, 0x6b, 0xc6, 0xf3, 0xa7, 0xe6, 0x3f, 0x3a, 0xe6, 0x8f, 0x6b, 0xe7, 0x95,
+    0xad, 0xf4, 0x6a, 0xc2, 0x8a, 0x4f, 0x80, 0x78, 0x68, 0x9b, 0xe3, 0xfd, 0x7f, 0x66, 0x2e, 0x9f, 0xc9, 0xe5, 0xe8, 0x99,
+    0xfd, 0xcf, 0x6d, 0x9f, 0xbd, 0x3e, 0xb2, 0xbe, 0xff, 0xcf, 0x5d, 0x1b, 0x1c, 0xed, 0x3d, 0x76, 0xf6, 0xe7, 0x6f, 0xe4,
+    0x7f, 0xa6, 0xf5, 0xd7, 0xcc, 0x4a, 0x56, 0x3c, 0x67, 0xcf, 0x99, 0x8a, 0x87, 0x36, 0x79, 0x6f, 0xff, 0x1f, 0x17, 0x8c,
+    0xff, 0xdb, 0xe3, 0x88, 0x7b, 0xf4, 0x4a, 0xe6, 0x5d, 0xf9, 0x5f, 0x77, 0x75, 0xf3, 0xde, 0xfc, 0xb7, 0x63, 0xf2, 0x3f,
+    0x73, 0x25, 0xfd, 0x17, 0xf2, 0x5f, 0x71, 0xf4, 0x9f, 0xae, 0xd8, 0x44, 0xc1, 0xec, 0xf1, 0x37, 0xf2, 0xbf, 0x3a, 0xb1,
+    0xbb, 0x5f, 0xed, 0xbc, 0xf3, 0x22, 0xff, 0x2b, 0x12, 0x14, 0x0f, 0xa3, 0x86, 0xf9, 0xd9, 0xe3, 0x7d, 0xf9, 0x1f, 0x99,
+    0x45, 0x9e, 0x33, 0x63, 0xdf, 0x3b, 0xff, 0xdf, 0x73, 0x06, 0xce, 0xfd, 0xfe, 0x44, 0xe6, 0x6a, 0xdb, 0x69, 0xfd, 0xff,
+    0x09, 0xbd, 0x2c, 0x20, 0xff, 0x80, 0xfc, 0x03, 0xbf, 0xb0, 0x52, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x7e, 0xe9, 0x3b, 0xf4, 0xb9, 0xd5, 0xc9, 0x77, 0xd6, 0xa6, 0x5f, 0x51, 0x11, 0xa6, 0xba, 0xb2, 0xf6,
+    0xe7, 0xad, 0x1f, 0x59, 0x01, 0xdf, 0xb7, 0x6a, 0x2a, 0x06, 0xd7, 0xa9, 0xd7, 0xae, 0x6a, 0xcf, 0x54, 0x1f, 0x68, 0xa5,
+    0xeb, 0xed, 0x67, 0x56, 0x9b, 0x44, 0x2a, 0x21, 0x2b, 0xaa, 0x37, 0xee, 0xca, 0x7f, 0x4f, 0xfb, 0x6d, 0x47, 0xd4, 0xa6,
+    0xcf, 0xac, 0x26, 0x7c, 0x3f, 0xff, 0xa3, 0xad, 0x70, 0xbc, 0x2e, 0x76, 0xc5, 0xea, 0xcb, 0xf6, 0x6a, 0xf5, 0xe1, 0xfc,
+    0x59, 0xac, 0x5c, 0x81, 0xba, 0xb7, 0x57, 0x98, 0xdd, 0x8f, 0xbf, 0x5e, 0x73, 0xae, 0xd6, 0xe9, 0x48, 0x15, 0x6e, 0xf9,
+    0x7f, 0x3b, 0xff, 0xeb, 0x47, 0x94, 0xbb, 0xea, 0xdd, 0xc4, 0xf0, 0x1d, 0x41, 0x66, 0xee, 0x57, 0x14, 0x13, 0x7f, 0x7b,
+    0x76, 0xfe, 0xb3, 0x6d, 0x2a, 0x1e, 0x3f, 0x55, 0xe4, 0xbf, 0x3d, 0xdc, 0x39, 0xa1, 0x0d, 0x56, 0x4e, 0xce, 0x56, 0x59,
+    0xae, 0xcf, 0x7d, 0x65, 0x85, 0xaa, 0x48, 0x8e, 0xff, 0xab, 0xcf, 0x62, 0x55, 0x4f, 0x79, 0x53, 0xfe, 0xf3, 0x77, 0xd1,
+    0xdb, 0x37, 0xe7, 0x7d, 0x7a, 0xce, 0xe8, 0x5c, 0x73, 0x6f, 0xfe, 0x2b, 0xef, 0x75, 0x73, 0x4e, 0xfe, 0x57, 0xce, 0x28,
+    0xfb, 0xb6, 0x7f, 0x7f, 0xfe, 0xe3, 0x2b, 0xc7, 0xff, 0x71, 0x48, 0xfe, 0x2b, 0xdb, 0x5a, 0x4b, 0xe5, 0x71, 0x6f, 0xff,
+    0xdf, 0x5e, 0xfc, 0xbd, 0xfc, 0x8f, 0xe7, 0x3f, 0xcc, 0xff, 0x5f, 0xcb, 0x7f, 0x5c, 0x9b, 0xff, 0x98, 0xd8, 0x92, 0xf9,
+    0x47, 0xce, 0x1a, 0xff, 0x57, 0x3e, 0x27, 0x73, 0x47, 0xe0, 0x3b, 0xfa, 0xff, 0x6f, 0xb8, 0xfe, 0x5f, 0xff, 0x7e, 0x99,
+    0x19, 0x74, 0x55, 0xfe, 0xfb, 0xe7, 0xf0, 0xbf, 0x92, 0xff, 0xcc, 0xc8, 0xa0, 0xf6, 0x39, 0xe3, 0xd7, 0x78, 0xf6, 0xcd,
+    0xff, 0x77, 0x5d, 0x6b, 0x3d, 0xe3, 0xd3, 0x3a, 0x16, 0x5d, 0x83, 0xfd, 0xdf, 0x2b, 0xbd, 0x55, 0xfd, 0x46, 0xed, 0x7c,
+    0x3c, 0xff, 0x5d, 0x88, 0x99, 0x3e, 0xec, 0xe4, 0xfc, 0x8f, 0xf7, 0x52, 0xb9, 0x9e, 0xed, 0x8d, 0xfc, 0xbf, 0x59, 0x87,
+    0x52, 0xbd, 0x4b, 0x42, 0x9b, 0x59, 0xf2, 0x59, 0x72, 0xc3, 0x71, 0x93, 0x7f, 0xe4, 0x5f, 0xd6, 0xd0, 0x2e, 0xb5, 0x63,
+    0xf9, 0xe7, 0x17, 0xdb, 0x65, 0xff, 0xc8, 0xf6, 0x84, 0x7b, 0xf3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x50, 0xb5, 0xfe, 0x3f, 0x53, 0x6d, 0xe4, 0xf4, 0xfa, 0xb9, 0xa3, 0x2b, 0xf9, 0xfb, 0xd7, 0x74, 0x47, 0x41,
+    0x55, 0xde, 0xe7, 0xaa, 0x85, 0x23, 0x35, 0x04, 0x62, 0xb8, 0x42, 0x7f, 0xcf, 0x73, 0x3e, 0xb7, 0x89, 0xbf, 0xf7, 0x36,
+    0xd7, 0xc2, 0x9e, 0x1f, 0xef, 0x5f, 0xdb, 0x10, 0x43, 0xc7, 0x3c, 0x73, 0x8c, 0x3e, 0x9d, 0x8b, 0xb1, 0x7b, 0x28, 0xfc,
+    0xfd, 0x2a, 0x63, 0x2d, 0x23, 0x3a, 0x57, 0x33, 0x55, 0x55, 0x6b, 0xbf, 0xb7, 0x7e, 0xf6, 0xfc, 0xef, 0x62, 0x7a, 0x15,
+    0xfa, 0xf8, 0x31, 0xaf, 0xad, 0x21, 0xd4, 0xfb, 0x9c, 0xf1, 0xba, 0xc4, 0x63, 0x6d, 0x26, 0x26, 0x56, 0xdc, 0x45, 0x6a,
+    0xbf, 0x63, 0xe2, 0x68, 0xf4, 0xf6, 0x3a, 0x23, 0x7b, 0x32, 0x5a, 0x51, 0x30, 0x77, 0xe4, 0xab, 0xf3, 0x9f, 0xa9, 0xbc,
+    0x9e, 0xcf, 0x7f, 0x6d, 0xfd, 0xec, 0xb9, 0xfc, 0x8f, 0xb5, 0xed, 0x7c, 0x65, 0xe5, 0xf1, 0x4f, 0xba, 0x36, 0x38, 0x06,
+    0xfa, 0x94, 0xaa, 0x4f, 0xf5, 0x96, 0xf2, 0xbd, 0x50, 0x0c, 0xbf, 0xdb, 0xf8, 0xda, 0xc6, 0x48, 0xf5, 0xf3, 0x51, 0x54,
+    0xab, 0x2c, 0xb7, 0x27, 0xb3, 0xf9, 0x1f, 0xf9, 0xec, 0xad, 0xba, 0x5b, 0xd3, 0x29, 0xf5, 0xb3, 0x73, 0xa3, 0xef, 0x53,
+    0xf3, 0xbf, 0xef, 0x6e, 0x66, 0x9f, 0x9e, 0x13, 0xcb, 0x7a, 0xa1, 0xbf, 0xdf, 0x2d, 0x0a, 0x57, 0xdc, 0xd7, 0x8c, 0x86,
+    0x66, 0xee, 0x48, 0xb2, 0x62, 0x84, 0xb8, 0xbf, 0xff, 0x1f, 0xcf, 0xc6, 0x49, 0xfd, 0xff, 0x9a, 0x39, 0x41, 0x45, 0xfe,
+    0x73, 0x33, 0x83, 0xf9, 0x2a, 0xed, 0xbd, 0xcf, 0x89, 0x8e, 0x54, 0xb4, 0x92, 0x4f, 0x80, 0xf8, 0xaf, 0xde, 0x77, 0x75,
+    0xfe, 0xc7, 0xaf, 0x68, 0xe4, 0xae, 0x3a, 0x55, 0x54, 0x31, 0x3d, 0x61, 0xfe, 0x5f, 0x99, 0xff, 0x35, 0xf5, 0x73, 0xeb,
+    0xaa, 0xc6, 0xaf, 0xc8, 0xff, 0x78, 0xca, 0x67, 0x7a, 0xf9, 0x3d, 0xf9, 0xaf, 0xbe, 0x2a, 0xbd, 0xa2, 0xff, 0xaf, 0xdf,
+    0xc6, 0xb1, 0xeb, 0x7f, 0xb3, 0x2d, 0x67, 0xf7, 0xf5, 0xff, 0xca, 0xf1, 0xff, 0xde, 0xfa, 0xb9, 0x75, 0x79, 0x99, 0xad,
+    0x9d, 0x5b, 0x73, 0xaf, 0x9e, 0xea, 0xfe, 0x3f, 0x77, 0x74, 0x32, 0xf3, 0xff, 0x53, 0xf2, 0x1f, 0x0b, 0xb6, 0x70, 0xbc,
+    0x95, 0xc7, 0x82, 0xf4, 0xd7, 0xec, 0xd7, 0xda, 0x31, 0xd6, 0x39, 0x9f, 0xd1, 0xed, 0xf5, 0xfe, 0x3f, 0xf7, 0xd7, 0x95,
+    0x23, 0x83, 0xcc, 0xbb, 0xc5, 0xd6, 0xb3, 0x1c, 0x07, 0xb7, 0xa9, 0x99, 0x2b, 0x09, 0x71, 0x6c, 0x8a, 0xa2, 0x74, 0x3c,
+    0x21, 0xff, 0xad, 0xf8, 0xba, 0xfc, 0xf8, 0xf8, 0xa9, 0x25, 0xbf, 0x03, 0xd1, 0xba, 0xee, 0x5b, 0xbf, 0x6b, 0x54, 0x7a,
+    0x62, 0xa5, 0xc2, 0xdc, 0x1d, 0xd5, 0x46, 0xae, 0xff, 0x9d, 0x9e, 0x94, 0x7b, 0xbf, 0xd5, 0xb4, 0x3b, 0xff, 0x77, 0x7d,
+    0x06, 0x9e, 0x9a, 0xb8, 0x3b, 0x8e, 0x67, 0xbc, 0xda, 0x7a, 0x59, 0x33, 0xa7, 0xfb, 0xa5, 0xfc, 0xe3, 0x78, 0x3a, 0xb3,
+    0xdf, 0x91, 0x7f, 0xbd, 0x88, 0xe3, 0x4a, 0xd5, 0xff, 0x74, 0x7c, 0xfe, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x70, 0xc6, 0x6a, 0x86, 0xde, 0x5a, 0xec, 0xa3, 0xd5, 0xaf, 0xf3, 0xab, 0xa5, 0xc7, 0x7e, 0x1f, 0xc3,
+    0x95, 0xb9, 0x9f, 0xd6, 0xda, 0x57, 0xde, 0x01, 0x21, 0xbf, 0x5f, 0x99, 0x3a, 0x6b, 0x3d, 0xbf, 0x1b, 0xaf, 0x32, 0x10,
+    0xa9, 0xf5, 0x50, 0xeb, 0xcf, 0x64, 0xee, 0x6e, 0x0f, 0x7b, 0xce, 0x53, 0x66, 0x15, 0x59, 0xfd, 0x56, 0x3f, 0x3f, 0xa3,
+    0xbf, 0x5a, 0x5c, 0xbc, 0xb8, 0x92, 0xfa, 0x73, 0xcb, 0x88, 0xc4, 0xfb, 0xc7, 0xd4, 0x31, 0x59, 0xbd, 0x5f, 0xb3, 0x6b,
+    0x95, 0xb2, 0x55, 0xa7, 0x62, 0xf0, 0x08, 0xdf, 0x79, 0x26, 0xf7, 0x9c, 0xa7, 0x7c, 0x0d, 0xb9, 0xd8, 0x50, 0x7f, 0xa0,
+    0x22, 0xff, 0x31, 0xb9, 0x9f, 0xef, 0xac, 0xd6, 0x8c, 0xd4, 0x3d, 0x75, 0xf6, 0xee, 0xd3, 0xba, 0xb5, 0xa6, 0x91, 0x6c,
+    0x0d, 0xa7, 0xae, 0xbb, 0x7d, 0x77, 0x7f, 0x6a, 0xef, 0xc2, 0xb0, 0xf3, 0x2c, 0x8c, 0xe4, 0x3f, 0xb7, 0xbd, 0x51, 0x98,
+    0xcb, 0xda, 0x5a, 0xf7, 0xb9, 0xcf, 0xb3, 0xb6, 0xa0, 0xb6, 0xe6, 0x78, 0x0d, 0xd6, 0xb6, 0xa4, 0x82, 0x69, 0xed, 0x1d,
+    0xd5, 0x76, 0x9d, 0xc9, 0xa7, 0xfe, 0x7f, 0x7d, 0xbb, 0xd8, 0xf7, 0x48, 0xbe, 0xda, 0x63, 0x5f, 0xba, 0x2b, 0x46, 0x08,
+    0xcf, 0x33, 0xec, 0x5b, 0x8e, 0x72, 0xdd, 0x7d, 0xba, 0xaa, 0x1f, 0x59, 0x91, 0xff, 0xa7, 0xfa, 0xbe, 0x37, 0xe6, 0xe5,
+    0xe9, 0xbe, 0x8c, 0xe7, 0x6e, 0x75, 0x94, 0xb7, 0x98, 0x56, 0x9c, 0xee, 0xfe, 0x7d, 0xbc, 0x2d, 0xff, 0xa7, 0xa4, 0xfc,
+    0xac, 0xfe, 0xff, 0xd4, 0x24, 0x7d, 0x1e, 0x63, 0xdf, 0xf9, 0xa9, 0xb5, 0xa2, 0xc5, 0x64, 0xd3, 0x9d, 0xbf, 0x1a, 0x7e,
+    0x42, 0xfe, 0xf7, 0xce, 0xff, 0xab, 0x1f, 0x99, 0xbb, 0x57, 0x71, 0xf5, 0xf8, 0xff, 0xcc, 0x91, 0x74, 0xbb, 0xb4, 0x97,
+    0xd9, 0x73, 0x5f, 0x8b, 0xfa, 0x2b, 0x17, 0x35, 0xed, 0xe9, 0xbb, 0xe7, 0xff, 0x55, 0x8f, 0xdc, 0x30, 0xff, 0x97, 0xff,
+    0x99, 0x94, 0xb7, 0xd7, 0x8e, 0x4e, 0xef, 0x7f, 0xf7, 0x67, 0xf2, 0xbf, 0x27, 0x97, 0xab, 0x1e, 0x59, 0x31, 0xe2, 0x39,
+    0x7b, 0xfc, 0x7f, 0xe3, 0xf9, 0xaa, 0xbd, 0xc6, 0x5e, 0xbf, 0x6d, 0x99, 0xff, 0xe3, 0xd5, 0x6d, 0xf5, 0xae, 0xff, 0xce,
+    0x54, 0x8d, 0x3b, 0x4e, 0x6e, 0x4f, 0x6b, 0xfe, 0x3b, 0x7b, 0x4e, 0xfe, 0xbf, 0xf9, 0x7c, 0x9d, 0x96, 0xff, 0xb6, 0xe9,
+    0xbf, 0x4b, 0xf2, 0xbf, 0xa2, 0x3d, 0xc5, 0xd6, 0xf9, 0x7f, 0xc8, 0x7f, 0x71, 0x0b, 0xdf, 0xb9, 0x6d, 0xb1, 0x38, 0x4b,
+    0x77, 0x7e, 0xef, 0xf8, 0xdc, 0xbe, 0x61, 0xff, 0xf7, 0x8d, 0x6a, 0xee, 0x56, 0xb4, 0xf2, 0xfb, 0x57, 0xef, 0x5f, 0xc9,
+    0xb9, 0xf5, 0x3c, 0xa9, 0x18, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x5b, 0xbb,
+    0xa7, 0xa6, 0x7e, 0x2b, 0xaa, 0xa9, 0x3f, 0xfe, 0x48, 0xed, 0xfe, 0x46, 0xa2, 0x3a, 0x78, 0x14, 0xac, 0x48, 0x5c, 0x71,
+    0x96, 0xc6, 0xce, 0x4a, 0xdf, 0xab, 0xfd, 0x7a, 0xfa, 0xd7, 0x57, 0x62, 0xef, 0x5f, 0xc3, 0x57, 0xb5, 0x7a, 0xf6, 0x94,
+    0x9a, 0xfa, 0x2d, 0x75, 0x7c, 0xdf, 0xae, 0xa8, 0x3f, 0x7e, 0xfe, 0xda, 0x05, 0x15, 0xf5, 0x79, 0x6b, 0xed, 0xe4, 0x6c,
+    0xfe, 0x77, 0x6c, 0xfd, 0xba, 0x71, 0xd4, 0x6d, 0x15, 0xf5, 0x9f, 0xea, 0xe6, 0xc4, 0xa1, 0x15, 0xf5, 0x2b, 0x5f, 0x4d,
+    0x96, 0xa3, 0xb0, 0x0a, 0x71, 0xcf, 0xf8, 0x7f, 0xcf, 0x5a, 0xed, 0x18, 0x1a, 0x41, 0x56, 0xd5, 0xd4, 0x78, 0xb3, 0x02,
+    0x62, 0xe5, 0x7d, 0x26, 0xce, 0xae, 0xa8, 0x7d, 0xc2, 0x1d, 0x35, 0xbe, 0xeb, 0x1a, 0x40, 0x2b, 0xcb, 0xff, 0xfc, 0xab,
+    0xad, 0xaf, 0x15, 0xb2, 0xae, 0xa6, 0xfe, 0x7d, 0x15, 0xf5, 0x3e, 0xdd, 0x05, 0xf0, 0xfb, 0x2a, 0x6a, 0xcb, 0xff, 0xea,
+    0x9e, 0xeb, 0x9c, 0xfc, 0xef, 0xac, 0xa9, 0xa5, 0xa2, 0xae, 0xfc, 0x7f, 0xdb, 0xf8, 0xbf, 0xe2, 0x9e, 0x3a, 0xef, 0x8f,
+    0x7a, 0x9b, 0xfc, 0xa7, 0xf2, 0x7f, 0x77, 0x45, 0x67, 0x89, 0x3f, 0xf9, 0x38, 0x7f, 0x5f, 0x4d, 0xdd, 0xdf, 0x49, 0x85,
+    0xfc, 0xff, 0x4e, 0xfe, 0xd7, 0x54, 0x6f, 0xfc, 0xc6, 0x9a, 0xba, 0x67, 0xf7, 0x97, 0x37, 0x56, 0xd4, 0x7d, 0x7f, 0x4f,
+    0x5d, 0xff, 0xbf, 0x61, 0x8e, 0x72, 0x7a, 0x4d, 0x5d, 0x15, 0x75, 0x73, 0xdb, 0xb6, 0xe6, 0x3f, 0x56, 0xc0, 0xdd, 0x57,
+    0xac, 0x63, 0xe2, 0x2a, 0x37, 0xf0, 0xad, 0xf9, 0x37, 0xfe, 0x87, 0xef, 0x9f, 0xb1, 0x3a, 0x3a, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x37, 0xf8, 0xe7, 0xc7,
+    0x71, 0x00, 0xf9, 0x07, 0x7e, 0x2e, 0xff, 0xff, 0x02 };
+
+// Font glyphs rectangles data (on atlas)
+static const Rectangle rltechFontRecs[189] = {
+    { 4, 4, 5 , 16 },
+    { 17, 4, 1 , 9 },
+    { 26, 4, 3 , 3 },
+    { 37, 4, 5 , 7 },
+    { 50, 4, 5 , 9 },
+    { 63, 4, 7 , 9 },
+    { 78, 4, 6 , 9 },
+    { 92, 4, 1 , 3 },
+    { 101, 4, 2 , 13 },
+    { 111, 4, 2 , 13 },
+    { 121, 4, 5 , 5 },
+    { 134, 4, 5 , 5 },
+    { 147, 4, 1 , 2 },
+    { 156, 4, 5 , 1 },
+    { 169, 4, 1 , 1 },
+    { 178, 4, 3 , 9 },
+    { 189, 4, 5 , 9 },
+    { 202, 4, 2 , 9 },
+    { 212, 4, 5 , 9 },
+    { 225, 4, 5 , 9 },
+    { 238, 4, 5 , 9 },
+    { 251, 4, 5 , 9 },
+    { 264, 4, 5 , 9 },
+    { 277, 4, 5 , 9 },
+    { 290, 4, 5 , 9 },
+    { 303, 4, 5 , 9 },
+    { 316, 4, 1 , 6 },
+    { 325, 4, 1 , 7 },
+    { 334, 4, 3 , 5 },
+    { 345, 4, 5 , 3 },
+    { 358, 4, 3 , 5 },
+    { 369, 4, 5 , 9 },
+    { 382, 4, 8 , 9 },
+    { 398, 4, 5 , 9 },
+    { 411, 4, 5 , 9 },
+    { 424, 4, 5 , 9 },
+    { 437, 4, 5 , 9 },
+    { 450, 4, 5 , 9 },
+    { 463, 4, 5 , 9 },
+    { 476, 4, 5 , 9 },
+    { 489, 4, 5 , 9 },
+    { 502, 4, 1 , 9 },
+    { 4, 28, 5 , 9 },
+    { 17, 28, 5 , 9 },
+    { 30, 28, 5 , 9 },
+    { 43, 28, 9 , 9 },
+    { 60, 28, 5 , 9 },
+    { 73, 28, 5 , 9 },
+    { 86, 28, 5 , 9 },
+    { 99, 28, 5 , 9 },
+    { 112, 28, 5 , 9 },
+    { 125, 28, 5 , 9 },
+    { 138, 28, 5 , 9 },
+    { 151, 28, 5 , 9 },
+    { 164, 28, 5 , 9 },
+    { 177, 28, 9 , 9 },
+    { 194, 28, 5 , 9 },
+    { 207, 28, 5 , 9 },
+    { 220, 28, 5 , 9 },
+    { 233, 28, 3 , 13 },
+    { 244, 28, 3 , 9 },
+    { 255, 28, 3 , 13 },
+    { 266, 28, 3 , 2 },
+    { 277, 28, 6 , 1 },
+    { 291, 28, 2 , 2 },
+    { 301, 28, 5 , 7 },
+    { 314, 28, 5 , 9 },
+    { 327, 28, 5 , 7 },
+    { 340, 28, 5 , 9 },
+    { 353, 28, 5 , 7 },
+    { 366, 28, 4 , 9 },
+    { 378, 28, 5 , 10 },
+    { 391, 28, 5 , 9 },
+    { 404, 28, 1 , 9 },
+    { 413, 28, 1 , 12 },
+    { 422, 28, 5 , 9 },
+    { 435, 28, 1 , 9 },
+    { 444, 28, 9 , 7 },
+    { 461, 28, 5 , 7 },
+    { 474, 28, 5 , 7 },
+    { 487, 28, 5 , 10 },
+    { 4, 52, 5 , 10 },
+    { 17, 52, 5 , 7 },
+    { 30, 52, 5 , 7 },
+    { 43, 52, 4 , 9 },
+    { 55, 52, 5 , 7 },
+    { 68, 52, 5 , 7 },
+    { 81, 52, 9 , 7 },
+    { 98, 52, 5 , 7 },
+    { 111, 52, 5 , 10 },
+    { 124, 52, 5 , 7 },
+    { 137, 52, 3 , 13 },
+    { 148, 52, 1 , 13 },
+    { 157, 52, 3 , 13 },
+    { 168, 52, 4 , 2 },
+    { 180, 52, 1 , 9 },
+    { 189, 52, 5 , 7 },
+    { 202, 52, 5 , 9 },
+    { 215, 52, 5 , 9 },
+    { 228, 52, 5 , 9 },
+    { 241, 52, 5 , 12 },
+    { 254, 52, 5 , 9 },
+    { 267, 52, 5 , 10 },
+    { 280, 52, 8 , 9 },
+    { 296, 52, 4 , 7 },
+    { 308, 52, 6 , 5 },
+    { 322, 52, 5 , 3 },
+    { 335, 52, 8 , 9 },
+    { 351, 52, 6 , 1 },
+    { 365, 52, 3 , 3 },
+    { 376, 52, 5 , 7 },
+    { 389, 52, 4 , 5 },
+    { 401, 52, 4 , 5 },
+    { 413, 52, 5 , 12 },
+    { 426, 52, 5 , 10 },
+    { 439, 52, 6 , 9 },
+    { 453, 52, 1 , 1 },
+    { 462, 52, 5 , 10 },
+    { 475, 52, 2 , 5 },
+    { 485, 52, 4 , 7 },
+    { 497, 52, 6 , 5 },
+    { 4, 76, 9 , 9 },
+    { 21, 76, 9 , 7 },
+    { 38, 76, 5 , 11 },
+    { 51, 76, 5 , 9 },
+    { 64, 76, 5 , 12 },
+    { 77, 76, 5 , 12 },
+    { 90, 76, 5 , 12 },
+    { 103, 76, 5 , 12 },
+    { 116, 76, 5 , 11 },
+    { 129, 76, 5 , 11 },
+    { 142, 76, 9 , 9 },
+    { 159, 76, 5 , 11 },
+    { 172, 76, 5 , 12 },
+    { 185, 76, 5 , 12 },
+    { 198, 76, 5 , 12 },
+    { 211, 76, 5 , 11 },
+    { 224, 76, 2 , 12 },
+    { 234, 76, 2 , 12 },
+    { 244, 76, 3 , 12 },
+    { 255, 76, 3 , 11 },
+    { 266, 76, 6 , 9 },
+    { 280, 76, 5 , 12 },
+    { 293, 76, 5 , 12 },
+    { 306, 76, 5 , 12 },
+    { 319, 76, 5 , 12 },
+    { 332, 76, 5 , 12 },
+    { 345, 76, 5 , 11 },
+    { 358, 76, 5 , 5 },
+    { 371, 76, 5 , 9 },
+    { 384, 76, 5 , 12 },
+    { 397, 76, 5 , 12 },
+    { 410, 76, 5 , 12 },
+    { 423, 76, 5 , 11 },
+    { 436, 76, 5 , 12 },
+    { 449, 76, 5 , 9 },
+    { 462, 76, 5 , 11 },
+    { 475, 76, 5 , 10 },
+    { 488, 76, 5 , 10 },
+    { 4, 100, 5 , 10 },
+    { 17, 100, 5 , 10 },
+    { 30, 100, 5 , 9 },
+    { 43, 100, 5 , 9 },
+    { 56, 100, 9 , 7 },
+    { 73, 100, 5 , 9 },
+    { 86, 100, 5 , 10 },
+    { 99, 100, 5 , 10 },
+    { 112, 100, 5 , 10 },
+    { 125, 100, 5 , 9 },
+    { 138, 100, 2 , 10 },
+    { 148, 100, 2 , 10 },
+    { 158, 100, 3 , 10 },
+    { 169, 100, 3 , 9 },
+    { 180, 100, 5 , 9 },
+    { 193, 100, 5 , 10 },
+    { 206, 100, 5 , 10 },
+    { 219, 100, 5 , 10 },
+    { 232, 100, 5 , 10 },
+    { 245, 100, 5 , 10 },
+    { 258, 100, 5 , 9 },
+    { 271, 100, 5 , 5 },
+    { 284, 100, 5 , 7 },
+    { 297, 100, 5 , 10 },
+    { 310, 100, 5 , 10 },
+    { 323, 100, 5 , 10 },
+    { 336, 100, 5 , 9 },
+    { 349, 100, 5 , 13 },
+    { 362, 100, 5 , 13 },
+    { 375, 100, 5 , 12 },
+};
+
+// Font glyphs info data
+// NOTE: No glyphs.image data provided
+static const GlyphInfo rltechFontGlyphs[189] = {
+    { 32, 0, 0, 5, { 0 }},
+    { 33, 0, 3, 2, { 0 }},
+    { 34, 0, 3, 4, { 0 }},
+    { 35, 0, 3, 6, { 0 }},
+    { 36, 0, 3, 6, { 0 }},
+    { 37, 0, 3, 8, { 0 }},
+    { 38, 0, 3, 7, { 0 }},
+    { 39, 0, 3, 2, { 0 }},
+    { 40, 0, 1, 2, { 0 }},
+    { 41, -1, 1, 2, { 0 }},
+    { 42, 0, 3, 6, { 0 }},
+    { 43, 0, 5, 6, { 0 }},
+    { 44, 0, 10, 2, { 0 }},
+    { 45, 0, 8, 6, { 0 }},
+    { 46, 0, 11, 2, { 0 }},
+    { 47, 0, 3, 4, { 0 }},
+    { 48, 0, 3, 6, { 0 }},
+    { 49, 0, 3, 3, { 0 }},
+    { 50, 0, 3, 6, { 0 }},
+    { 51, 0, 3, 6, { 0 }},
+    { 52, 0, 3, 6, { 0 }},
+    { 53, 0, 3, 6, { 0 }},
+    { 54, 0, 3, 6, { 0 }},
+    { 55, 0, 3, 6, { 0 }},
+    { 56, 0, 3, 6, { 0 }},
+    { 57, 0, 3, 6, { 0 }},
+    { 58, 0, 4, 2, { 0 }},
+    { 59, 0, 4, 2, { 0 }},
+    { 60, 0, 5, 4, { 0 }},
+    { 61, 0, 6, 6, { 0 }},
+    { 62, 0, 5, 4, { 0 }},
+    { 63, 0, 3, 6, { 0 }},
+    { 64, 0, 3, 9, { 0 }},
+    { 65, 0, 3, 6, { 0 }},
+    { 66, 0, 3, 6, { 0 }},
+    { 67, 0, 3, 6, { 0 }},
+    { 68, 0, 3, 6, { 0 }},
+    { 69, 0, 3, 6, { 0 }},
+    { 70, 0, 3, 6, { 0 }},
+    { 71, 0, 3, 6, { 0 }},
+    { 72, 0, 3, 6, { 0 }},
+    { 73, 0, 3, 2, { 0 }},
+    { 74, 0, 3, 6, { 0 }},
+    { 75, 0, 3, 6, { 0 }},
+    { 76, 0, 3, 6, { 0 }},
+    { 77, 0, 3, 10, { 0 }},
+    { 78, 0, 3, 6, { 0 }},
+    { 79, 0, 3, 6, { 0 }},
+    { 80, 0, 3, 6, { 0 }},
+    { 81, 0, 3, 6, { 0 }},
+    { 82, 0, 3, 6, { 0 }},
+    { 83, 0, 3, 6, { 0 }},
+    { 84, 0, 3, 6, { 0 }},
+    { 85, 0, 3, 6, { 0 }},
+    { 86, 0, 3, 6, { 0 }},
+    { 87, 0, 3, 10, { 0 }},
+    { 88, 0, 3, 6, { 0 }},
+    { 89, 0, 3, 6, { 0 }},
+    { 90, 0, 3, 6, { 0 }},
+    { 91, 0, 1, 2, { 0 }},
+    { 92, 0, 3, 4, { 0 }},
+    { 93, -2, 1, 2, { 0 }},
+    { 94, 0, 3, 4, { 0 }},
+    { 95, 0, 12, 7, { 0 }},
+    { 96, 0, 3, 3, { 0 }},
+    { 97, 0, 5, 6, { 0 }},
+    { 98, 0, 3, 6, { 0 }},
+    { 99, 0, 5, 6, { 0 }},
+    { 100, 0, 3, 6, { 0 }},
+    { 101, 0, 5, 6, { 0 }},
+    { 102, 0, 3, 5, { 0 }},
+    { 103, 0, 5, 6, { 0 }},
+    { 104, 0, 3, 6, { 0 }},
+    { 105, 0, 3, 2, { 0 }},
+    { 106, 0, 3, 2, { 0 }},
+    { 107, 0, 3, 6, { 0 }},
+    { 108, 0, 3, 2, { 0 }},
+    { 109, 0, 5, 10, { 0 }},
+    { 110, 0, 5, 6, { 0 }},
+    { 111, 0, 5, 6, { 0 }},
+    { 112, 0, 5, 6, { 0 }},
+    { 113, 0, 5, 6, { 0 }},
+    { 114, 0, 5, 6, { 0 }},
+    { 115, 0, 5, 6, { 0 }},
+    { 116, 0, 3, 5, { 0 }},
+    { 117, 0, 5, 6, { 0 }},
+    { 118, 0, 5, 6, { 0 }},
+    { 119, 0, 5, 10, { 0 }},
+    { 120, 0, 5, 6, { 0 }},
+    { 121, 0, 5, 6, { 0 }},
+    { 122, 0, 5, 6, { 0 }},
+    { 123, 0, 1, 3, { 0 }},
+    { 124, 0, 1, 2, { 0 }},
+    { 125, -1, 1, 3, { 0 }},
+    { 126, 0, 3, 5, { 0 }},
+    { 161, 0, 6, 2, { 0 }},
+    { 162, 0, 4, 6, { 0 }},
+    { 163, 0, 3, 6, { 0 }},
+    { 8364, 0, 3, 6, { 0 }},
+    { 165, 0, 3, 6, { 0 }},
+    { 352, 0, 0, 6, { 0 }},
+    { 167, 0, 3, 6, { 0 }},
+    { 353, 0, 2, 6, { 0 }},
+    { 169, 0, 3, 9, { 0 }},
+    { 170, 0, 3, 5, { 0 }},
+    { 171, 0, 5, 7, { 0 }},
+    { 172, 0, 6, 6, { 0 }},
+    { 174, 0, 3, 9, { 0 }},
+    { 175, 0, 2, 7, { 0 }},
+    { 176, 0, 3, 4, { 0 }},
+    { 177, 0, 4, 6, { 0 }},
+    { 178, 0, 3, 5, { 0 }},
+    { 179, 0, 3, 5, { 0 }},
+    { 381, 0, 0, 6, { 0 }},
+    { 181, 0, 5, 6, { 0 }},
+    { 182, 0, 3, 7, { 0 }},
+    { 183, 0, 7, 2, { 0 }},
+    { 382, 0, 2, 6, { 0 }},
+    { 185, 0, 3, 3, { 0 }},
+    { 186, 0, 3, 5, { 0 }},
+    { 187, 0, 5, 7, { 0 }},
+    { 338, 0, 3, 10, { 0 }},
+    { 339, 0, 5, 10, { 0 }},
+    { 376, 0, 1, 6, { 0 }},
+    { 191, 0, 6, 6, { 0 }},
+    { 192, 0, 0, 6, { 0 }},
+    { 193, 0, 0, 6, { 0 }},
+    { 194, 0, 0, 6, { 0 }},
+    { 195, 0, 0, 6, { 0 }},
+    { 196, 0, 1, 6, { 0 }},
+    { 197, 0, 1, 6, { 0 }},
+    { 198, 0, 3, 10, { 0 }},
+    { 199, 0, 3, 6, { 0 }},
+    { 200, 0, 0, 6, { 0 }},
+    { 201, 0, 0, 6, { 0 }},
+    { 202, 0, 0, 6, { 0 }},
+    { 203, 0, 1, 6, { 0 }},
+    { 204, -1, 0, 2, { 0 }},
+    { 205, 0, 0, 2, { 0 }},
+    { 206, -1, 0, 2, { 0 }},
+    { 207, -1, 1, 2, { 0 }},
+    { 208, -1, 3, 6, { 0 }},
+    { 209, 0, 0, 6, { 0 }},
+    { 210, 0, 0, 6, { 0 }},
+    { 211, 0, 0, 6, { 0 }},
+    { 212, 0, 0, 6, { 0 }},
+    { 213, 0, 0, 6, { 0 }},
+    { 214, 0, 1, 6, { 0 }},
+    { 215, 0, 5, 6, { 0 }},
+    { 216, 0, 3, 6, { 0 }},
+    { 217, 0, 0, 6, { 0 }},
+    { 218, 0, 0, 6, { 0 }},
+    { 219, 0, 0, 6, { 0 }},
+    { 220, 0, 1, 6, { 0 }},
+    { 221, 0, 0, 6, { 0 }},
+    { 222, 0, 3, 6, { 0 }},
+    { 223, 0, 3, 6, { 0 }},
+    { 224, 0, 2, 6, { 0 }},
+    { 225, 0, 2, 6, { 0 }},
+    { 226, 0, 2, 6, { 0 }},
+    { 227, 0, 2, 6, { 0 }},
+    { 228, 0, 3, 6, { 0 }},
+    { 229, 0, 3, 6, { 0 }},
+    { 230, 0, 5, 10, { 0 }},
+    { 231, 0, 5, 6, { 0 }},
+    { 232, 0, 2, 6, { 0 }},
+    { 233, 0, 2, 6, { 0 }},
+    { 234, 0, 2, 6, { 0 }},
+    { 235, 0, 3, 6, { 0 }},
+    { 236, -1, 2, 2, { 0 }},
+    { 237, 0, 2, 2, { 0 }},
+    { 238, -1, 2, 2, { 0 }},
+    { 239, -1, 3, 2, { 0 }},
+    { 240, 0, 3, 6, { 0 }},
+    { 241, 0, 2, 6, { 0 }},
+    { 242, 0, 2, 6, { 0 }},
+    { 243, 0, 2, 6, { 0 }},
+    { 244, 0, 2, 6, { 0 }},
+    { 245, 0, 2, 6, { 0 }},
+    { 246, 0, 3, 6, { 0 }},
+    { 247, 0, 5, 6, { 0 }},
+    { 248, 0, 5, 6, { 0 }},
+    { 249, 0, 2, 6, { 0 }},
+    { 250, 0, 2, 6, { 0 }},
+    { 251, 0, 2, 6, { 0 }},
+    { 252, 0, 3, 6, { 0 }},
+    { 253, 0, 2, 6, { 0 }},
+    { 254, 0, 2, 6, { 0 }},
+    { 255, 0, 3, 6, { 0 }},
+};
+
+// Style loading function: RLTech
+static void GuiLoadStyleRLTech(void)
+{
+    // Load style properties provided
+    // NOTE: Default properties are propagated
+    for (int i = 0; i < RLTECH_STYLE_PROPS_COUNT; i++)
+    {
+        GuiSetStyle(rltechStyleProps[i].controlId, rltechStyleProps[i].propertyId, rltechStyleProps[i].propertyValue);
+    }
+
+    // Custom font loading
+    // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function
+    int rltechFontDataSize = 0;
+    unsigned char *data = DecompressData(rltechFontData, RLTECH_STYLE_FONT_ATLAS_COMP_SIZE, &rltechFontDataSize);
+    Image imFont = { data, 512, 256, 1, 2 };
+
+    Font font = { 0 };
+    font.baseSize = 16;
+    font.glyphCount = 189;
+
+    // Load texture from image
+    font.texture = LoadTextureFromImage(imFont);
+    UnloadImage(imFont);  // Uncompressed image data can be unloaded from memory
+
+    // Copy char recs data from global fontRecs
+    // NOTE: Required to avoid issues if trying to free font
+    font.recs = (Rectangle *)RAYGUI_MALLOC(font.glyphCount*sizeof(Rectangle));
+    memcpy(font.recs, rltechFontRecs, font.glyphCount*sizeof(Rectangle));
+
+    // Copy font char info data from global fontChars
+    // NOTE: Required to avoid issues if trying to free font
+    font.glyphs = (GlyphInfo *)RAYGUI_MALLOC(font.glyphCount*sizeof(GlyphInfo));
+    memcpy(font.glyphs, rltechFontGlyphs, font.glyphCount*sizeof(GlyphInfo));
+
+    GuiSetFont(font);
+
+    // Setup a white rectangle on the font to be used on shapes drawing,
+    // it makes possible to draw shapes and text (full UI) in a single draw call
+    Rectangle fontWhiteRec = { 510, 254, 1, 1 };
+    SetShapesTexture(font.texture, fontWhiteRec);
+
+    //-----------------------------------------------------------------
+
+    // TODO: Custom user style setup: Set specific properties here (if required)
+    // i.e. Controls specific BORDER_WIDTH, TEXT_PADDING, TEXT_ALIGNMENT
+}
diff --git a/raygui/styles/sunny/style_sunny.h b/raygui/styles/sunny/style_sunny.h
--- a/raygui/styles/sunny/style_sunny.h
+++ b/raygui/styles/sunny/style_sunny.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,171 +15,169 @@
 
 // Custom style name: Sunny
 static const GuiStyleProp sunnyStyleProps[SUNNY_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x9c760aff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x594006ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0xf6d519ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xf6ee89ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0xf5f3d1ff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xf4cd19ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0xf7e580ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0xf7f2c1ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x52470aff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0xc0be92ff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0xd3d3a1ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0xbcbc89ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0x725706ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0xf0be4bff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
-    { 1, 2, 0x504506ff },    // LABEL_TEXT_COLOR_NORMAL 
-    { 1, 5, 0xfdeb9bff },    // LABEL_TEXT_COLOR_FOCUSED 
-    { 1, 8, 0xf5e8a4ff },    // LABEL_TEXT_COLOR_PRESSED 
-    { 2, 2, 0xebc21fff },    // BUTTON_TEXT_COLOR_NORMAL 
-    { 3, 2, 0xebc21fff },    // TOGGLE_TEXT_COLOR_NORMAL 
-    { 4, 2, 0x81700fff },    // SLIDER_TEXT_COLOR_NORMAL 
-    { 4, 5, 0xf4e49aff },    // SLIDER_TEXT_COLOR_FOCUSED 
-    { 7, 2, 0xebc21fff },    // COMBOBOX_TEXT_COLOR_NORMAL 
-    { 8, 2, 0xefd87bff },    // DROPDOWNBOX_TEXT_COLOR_NORMAL 
-    { 8, 5, 0xd4b219ff },    // DROPDOWNBOX_TEXT_COLOR_FOCUSED 
-    { 9, 2, 0x7a680bff },    // TEXTBOX_TEXT_COLOR_NORMAL 
-    { 9, 5, 0xad931fff },    // TEXTBOX_TEXT_COLOR_FOCUSED 
-    { 10, 2, 0x62570eff },    // VALUEBOX_TEXT_COLOR_NORMAL 
-    { 10, 5, 0xf2df88ff },    // VALUEBOX_TEXT_COLOR_FOCUSED 
-    { 12, 2, 0xf4e798ff },    // LISTVIEW_TEXT_COLOR_NORMAL 
-    { 15, 2, 0xebc21fff },    // STATUSBAR_TEXT_COLOR_NORMAL 
+    { 0, 0, (int)0x9c760aff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x594006ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0xf6d519ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xf6ee89ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0xf5f3d1ff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xf4cd19ff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0xf7e580ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0xf7f2c1ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x52470aff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0xc0be92ff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0xd3d3a1ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0xbcbc89ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0x725706ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0xf0be4bff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 1, 2, (int)0x504506ff },    // LABEL_TEXT_COLOR_NORMAL 
+    { 1, 5, (int)0xfdeb9bff },    // LABEL_TEXT_COLOR_FOCUSED 
+    { 1, 8, (int)0xf5e8a4ff },    // LABEL_TEXT_COLOR_PRESSED 
+    { 2, 2, (int)0xebc21fff },    // BUTTON_TEXT_COLOR_NORMAL 
+    { 3, 2, (int)0xebc21fff },    // TOGGLE_TEXT_COLOR_NORMAL 
+    { 4, 2, (int)0x81700fff },    // SLIDER_TEXT_COLOR_NORMAL 
+    { 4, 5, (int)0xf4e49aff },    // SLIDER_TEXT_COLOR_FOCUSED 
+    { 7, 2, (int)0xebc21fff },    // COMBOBOX_TEXT_COLOR_NORMAL 
+    { 8, 2, (int)0xefd87bff },    // DROPDOWNBOX_TEXT_COLOR_NORMAL 
+    { 8, 5, (int)0xd4b219ff },    // DROPDOWNBOX_TEXT_COLOR_FOCUSED 
+    { 9, 2, (int)0x7a680bff },    // TEXTBOX_TEXT_COLOR_NORMAL 
+    { 9, 5, (int)0xad931fff },    // TEXTBOX_TEXT_COLOR_FOCUSED 
+    { 10, 2, (int)0x62570eff },    // VALUEBOX_TEXT_COLOR_NORMAL 
+    { 10, 5, (int)0xf2df88ff },    // VALUEBOX_TEXT_COLOR_FOCUSED 
+    { 12, 2, (int)0xf4e798ff },    // LISTVIEW_TEXT_COLOR_NORMAL 
+    { 15, 2, (int)0xebc21fff },    // STATUSBAR_TEXT_COLOR_NORMAL 
 };
 
-// WARNING: This style uses a custom font: "GenericMobileSystemNuevo.ttf" (size: 16, spacing: 0)
+// WARNING: This style uses a custom font: "GMSN.ttf" (size: 16, spacing: 0)
 
-#define SUNNY_STYLE_FONT_ATLAS_COMP_SIZE 2462
+#define SUNNY_STYLE_FONT_ATLAS_COMP_SIZE 2434
 
 // Font atlas image pixels data: DEFLATE compressed
 static unsigned char sunnyFontData[SUNNY_STYLE_FONT_ATLAS_COMP_SIZE] = { 0xed,
-    0xdd, 0x59, 0xb2, 0xeb, 0x34, 0x10, 0x00, 0x50, 0x6d, 0x83, 0xfd, 0xef, 0x8d, 0x6d, 0x88, 0xa2, 0x28, 0x0a, 0xb8, 0x8f,
-    0x58, 0x52, 0x77, 0xcb, 0x53, 0x0e, 0xa7, 0xf8, 0xb9, 0x7e, 0x49, 0x6c, 0xd9, 0x6d, 0x0d, 0xb6, 0x5a, 0xbd, 0x01, 0x00,
-    0x00, 0x00, 0x5f, 0xef, 0xcf, 0xff, 0xfe, 0xff, 0x6f, 0xff, 0xb7, 0xe5, 0x78, 0xdb, 0xdf, 0xff, 0x62, 0xb4, 0xa5, 0x7f,
-    0xdc, 0x93, 0x7e, 0xb0, 0x6d, 0xee, 0xb7, 0x62, 0xfb, 0xd5, 0x97, 0xca, 0xa6, 0x1f, 0x94, 0x5a, 0x4b, 0xff, 0xbd, 0x1d,
-    0x96, 0x7d, 0x0f, 0x94, 0x5e, 0x3b, 0xdc, 0xe7, 0xcf, 0x9f, 0x1c, 0x6f, 0xa9, 0xdc, 0xcf, 0x95, 0x32, 0xad, 0x3c, 0x3f,
-    0x2b, 0x25, 0xd8, 0x96, 0xb7, 0x1e, 0x97, 0x54, 0x9b, 0x2a, 0xc7, 0xf1, 0x37, 0xf7, 0x9b, 0xc4, 0xff, 0xf1, 0xd1, 0xfc,
-    0x1d, 0x83, 0x47, 0x67, 0x7f, 0xfe, 0xda, 0x68, 0xd3, 0xf7, 0x84, 0x7f, 0x7e, 0xbb, 0x2e, 0x42, 0xb3, 0xdf, 0xd2, 0x87,
-    0x77, 0xc2, 0x95, 0x12, 0x3a, 0x2e, 0xbd, 0x16, 0x8a, 0x89, 0xd1, 0x77, 0x46, 0xf6, 0x65, 0x66, 0xdb, 0xdc, 0xfe, 0xc5,
-    0xae, 0xf9, 0xf1, 0xf7, 0x1d, 0x5f, 0x69, 0x47, 0x67, 0x78, 0x74, 0xe5, 0x1e, 0x1f, 0xdf, 0xfa, 0x59, 0xcd, 0x44, 0xfd,
-    0x9e, 0xf8, 0xef, 0xff, 0x8a, 0xb3, 0x1e, 0x8c, 0xc1, 0x7c, 0xdd, 0xb0, 0x5e, 0x2b, 0x1c, 0xdf, 0xb3, 0xf6, 0x95, 0xf0,
-    0x51, 0x39, 0xf5, 0xc0, 0x35, 0xbc, 0x7e, 0xce, 0xa2, 0xbf, 0xb3, 0x23, 0xfe, 0x2b, 0xa2, 0xb8, 0x4d, 0xd4, 0x88, 0x91,
-    0x36, 0xc5, 0x71, 0x8b, 0x33, 0x1f, 0xe1, 0xa3, 0x7d, 0x89, 0xb4, 0x67, 0xf2, 0x65, 0xb9, 0xda, 0xe6, 0xd8, 0x17, 0xff,
-    0x15, 0xf5, 0xed, 0x7a, 0x5d, 0xb2, 0xaf, 0x7c, 0x67, 0xea, 0xeb, 0xfb, 0xc7, 0xff, 0xfa, 0xb1, 0x55, 0xc4, 0x7f, 0xcd,
-    0x59, 0x59, 0x8f, 0xff, 0x2e, 0xfe, 0xa7, 0xeb, 0xff, 0xcf, 0xa5, 0xde, 0x83, 0xfd, 0xaf, 0x7c, 0xaf, 0xbd, 0xaa, 0x9d,
-    0x7f, 0x6d, 0xfc, 0x8f, 0xfa, 0x83, 0x6d, 0x39, 0xfe, 0x63, 0xbd, 0xcf, 0x48, 0x6f, 0xa3, 0xaa, 0xfd, 0xbf, 0xb7, 0xf5,
-    0x70, 0xdc, 0x7b, 0x8c, 0x44, 0xf8, 0xda, 0x98, 0x47, 0xcd, 0x37, 0x5f, 0x11, 0xff, 0x6d, 0x62, 0x6f, 0xfa, 0xb0, 0xff,
-    0x3f, 0x7b, 0x87, 0x5f, 0x8b, 0xff, 0xba, 0xba, 0xe3, 0xca, 0xf8, 0xef, 0xc1, 0x11, 0xb1, 0xea, 0x7e, 0x7c, 0xfc, 0xd8,
-    0xd6, 0xc6, 0xe7, 0xce, 0xeb, 0xff, 0x8f, 0xda, 0x81, 0x99, 0xf8, 0xff, 0x96, 0xf6, 0xff, 0x4c, 0x4b, 0x7b, 0x54, 0x8a,
-    0xf3, 0xf7, 0xb3, 0x2b, 0x6a, 0xf3, 0x6b, 0xe3, 0xbf, 0x4f, 0x8d, 0x61, 0xdf, 0x3b, 0xfe, 0xdb, 0x72, 0x2f, 0xaf, 0xf2,
-    0xda, 0x8d, 0x5e, 0x51, 0xa3, 0x2b, 0x77, 0xe6, 0xae, 0x76, 0xcf, 0xf8, 0x8f, 0x5d, 0xcf, 0x7d, 0xd8, 0xca, 0xef, 0x89,
-    0xf8, 0x6f, 0xaf, 0x89, 0xff, 0x1e, 0x1c, 0xed, 0xf8, 0xdc, 0xde, 0x8b, 0x8d, 0xc7, 0x45, 0x5a, 0xf2, 0x99, 0x36, 0x4a,
-    0x55, 0xfc, 0xdf, 0xe3, 0x4c, 0xaa, 0xff, 0x2b, 0xc6, 0xff, 0xdb, 0x61, 0x0b, 0xff, 0xbb, 0xe2, 0xff, 0xf8, 0xf9, 0x5f,
-    0xbb, 0xfd, 0xb3, 0xba, 0x68, 0xfc, 0xd7, 0x8c, 0xff, 0x9f, 0x7f, 0xb7, 0x9e, 0xe9, 0xb9, 0xe6, 0xe2, 0xff, 0x4e, 0xcf,
-    0xff, 0xf6, 0xc6, 0xff, 0xe8, 0xa9, 0xf7, 0x9b, 0xda, 0xff, 0x95, 0xe3, 0xf8, 0xf1, 0x77, 0x6e, 0xf6, 0xbd, 0x53, 0xd4,
-    0x42, 0x6f, 0x72, 0xdd, 0x33, 0xfa, 0xa3, 0xcf, 0x81, 0x72, 0xa3, 0x74, 0x57, 0xbf, 0xff, 0x53, 0x5f, 0x82, 0xe7, 0xed,
-    0x4f, 0xbf, 0x71, 0x9b, 0x91, 0x77, 0xbd, 0x25, 0xeb, 0xea, 0x3a, 0xf3, 0xf9, 0x6c, 0xfc, 0xb7, 0x9c, 0x1f, 0x76, 0xd6,
-    0x6c, 0x4a, 0xe2, 0x19, 0x63, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xe4, 0xf9,
-    0x0b, 0x91, 0xb9, 0x86, 0xd1, 0x3c, 0x04, 0xb5, 0xf9, 0x10, 0xfa, 0x8f, 0x3c, 0xac, 0xab, 0xdf, 0x3d, 0x3b, 0xd7, 0x32,
-    0x33, 0x4f, 0xb3, 0xf6, 0xb3, 0x55, 0x99, 0x25, 0x6a, 0x67, 0x9e, 0x8f, 0xf3, 0xdd, 0xac, 0x66, 0x6b, 0x8e, 0x65, 0x41,
-    0x5b, 0xcf, 0x69, 0x9e, 0xc9, 0xc3, 0x57, 0x3d, 0xb7, 0xba, 0x26, 0x3f, 0xda, 0xfc, 0x9c, 0xf7, 0x9a, 0x4c, 0x23, 0x75,
-    0xf1, 0xbf, 0x7e, 0x1e, 0xfa, 0x8f, 0xfd, 0xe8, 0x17, 0x64, 0x62, 0xa8, 0xdf, 0x3a, 0xf7, 0xf7, 0x3b, 0xc5, 0xff, 0xec,
-    0x3d, 0x38, 0x7b, 0xf5, 0xc6, 0x67, 0xdc, 0x8d, 0xb2, 0x4f, 0x5c, 0x9f, 0x5b, 0xa5, 0x15, 0xe5, 0x47, 0xad, 0x8f, 0xff,
-    0x76, 0x52, 0xfd, 0x1f, 0x8d, 0xff, 0x3e, 0x5c, 0xcd, 0xe6, 0xbe, 0xf1, 0xbf, 0x9e, 0x77, 0x22, 0xfb, 0xab, 0xb9, 0x95,
-    0x1c, 0xe2, 0xb9, 0xc6, 0xf3, 0xf1, 0x9f, 0xcb, 0x14, 0x74, 0x6e, 0xdd, 0x7a, 0x7d, 0xfc, 0x47, 0x4b, 0xe4, 0xaa, 0xf8,
-    0x8f, 0x5f, 0x25, 0x7d, 0x98, 0xcd, 0xfa, 0x8e, 0xf1, 0xdf, 0xbf, 0x3e, 0xfe, 0xeb, 0x73, 0xf9, 0x46, 0x23, 0x20, 0x92,
-    0x07, 0x7d, 0xa6, 0x5f, 0xbd, 0xda, 0xb3, 0x8b, 0xf7, 0xbb, 0xa2, 0xf5, 0xff, 0xe8, 0xf3, 0x33, 0x47, 0x72, 0x75, 0xfc,
-    0xb7, 0x61, 0x84, 0xf7, 0x44, 0x94, 0xc6, 0x47, 0x40, 0x32, 0x39, 0xaf, 0xae, 0x89, 0xff, 0x16, 0x5a, 0x31, 0x30, 0x5e,
-    0xcf, 0xdf, 0x3d, 0xfe, 0xab, 0xfb, 0x06, 0xd1, 0x11, 0x83, 0xea, 0x7d, 0xbc, 0x5f, 0xff, 0xbf, 0x17, 0xb4, 0x12, 0x77,
-    0xc4, 0x7f, 0xb6, 0x7c, 0xa2, 0xd1, 0x14, 0x8b, 0xff, 0xf9, 0xda, 0x3d, 0xb6, 0x9a, 0x40, 0x6c, 0xdb, 0x7b, 0xe3, 0xbf,
-    0x36, 0x7f, 0xea, 0xfa, 0x58, 0xf1, 0x9b, 0xe2, 0x3f, 0xda, 0xff, 0x9f, 0xef, 0x71, 0xdd, 0x2b, 0xfe, 0xdb, 0xc4, 0xfa,
-    0x3c, 0x67, 0xc7, 0xff, 0x28, 0xdb, 0xea, 0xdb, 0xe2, 0x3f, 0x1a, 0x5b, 0xd1, 0xf5, 0x8c, 0x5a, 0xb8, 0xa6, 0x7b, 0x7b,
-    0xfd, 0xdf, 0x5e, 0x1a, 0xff, 0xf1, 0xf5, 0x65, 0x9e, 0x15, 0xff, 0xd5, 0xad, 0x86, 0xeb, 0xf3, 0xea, 0x45, 0x47, 0x1b,
-    0xe3, 0xa3, 0x94, 0xef, 0x6e, 0xff, 0xf7, 0x0d, 0xad, 0xf0, 0xec, 0xf3, 0xff, 0xab, 0xe2, 0x7f, 0xd7, 0xf3, 0xbf, 0x36,
-    0xbd, 0xe6, 0xdf, 0xae, 0xbd, 0x12, 0xff, 0xd5, 0xf1, 0x5f, 0xb9, 0x76, 0xe2, 0xda, 0xbf, 0xa8, 0x7e, 0xff, 0xa7, 0x7e,
-    0x24, 0x6d, 0xff, 0xfb, 0x3f, 0x7b, 0xca, 0xa7, 0x4d, 0xad, 0x98, 0xb6, 0xfe, 0xab, 0x6d, 0x72, 0x55, 0xe3, 0xd8, 0x3e,
-    0xd7, 0xbf, 0x7d, 0x73, 0xf7, 0xf8, 0x8f, 0xbd, 0x6d, 0x10, 0xfd, 0x64, 0xfc, 0x13, 0xf7, 0x59, 0x0b, 0xec, 0x3b, 0xdf,
-    0xa2, 0x54, 0x0a, 0xd5, 0x25, 0xa4, 0x54, 0x71, 0x07, 0x78, 0xd7, 0x7b, 0xe6, 0xbb, 0xfe, 0x3d, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xf7, 0xcd, 0x5e, 0x99, 0xcd, 0xb9, 0xfd, 0xeb, 0x5f, 0x7b, 0x68, 0xbe,
-    0x6b, 0x4f, 0x7e, 0xae, 0x2f, 0xe7, 0xc1, 0x58, 0x9b, 0x77, 0x5a, 0x91, 0x83, 0xa1, 0x3a, 0x8f, 0xd4, 0xb8, 0x5c, 0x66,
-    0xcf, 0xf4, 0x78, 0x26, 0xf1, 0xea, 0x15, 0x12, 0x99, 0xd7, 0x93, 0xc9, 0xa8, 0x93, 0x59, 0x4b, 0x20, 0x9e, 0x09, 0xb1,
-    0x2d, 0x7e, 0x6b, 0xec, 0xac, 0xfd, 0x3a, 0xbf, 0x3a, 0x9a, 0xef, 0xb3, 0x26, 0xfe, 0x63, 0xb9, 0x25, 0xaf, 0xc8, 0xb4,
-    0x16, 0xbf, 0x9a, 0x6a, 0xf7, 0xb0, 0xbe, 0x54, 0xe6, 0xf6, 0x36, 0xf7, 0xf7, 0xcc, 0x9c, 0xfe, 0x5c, 0xee, 0x81, 0xd5,
-    0x92, 0xe8, 0x53, 0xd9, 0x52, 0x56, 0x3f, 0xf9, 0xf9, 0x73, 0xc7, 0xdb, 0xfa, 0x54, 0xe6, 0x88, 0xd5, 0x6f, 0xfd, 0x6d,
-    0xf0, 0xf9, 0x7e, 0x78, 0xe7, 0xad, 0x9d, 0xd1, 0xfa, 0x94, 0xf8, 0x5f, 0xbf, 0x77, 0x9f, 0x1d, 0xff, 0xf1, 0xfb, 0x54,
-    0x2b, 0xcc, 0xd2, 0xb3, 0xbe, 0x36, 0x46, 0x64, 0xe6, 0x6e, 0x3e, 0x47, 0xd0, 0x19, 0x19, 0x35, 0x7e, 0x8d, 0xa6, 0x95,
-    0xb3, 0x36, 0x17, 0xfd, 0x9f, 0xe2, 0xf7, 0xa8, 0xb5, 0xb5, 0x2f, 0xfe, 0x6b, 0x32, 0xf2, 0x3d, 0x27, 0xfe, 0x67, 0x7a,
-    0x1b, 0xe7, 0xc5, 0xff, 0xf8, 0xef, 0x75, 0x59, 0xb5, 0x2b, 0x73, 0xed, 0x8c, 0x72, 0x2d, 0xb7, 0xd0, 0x19, 0xe9, 0xc1,
-    0xfb, 0x4a, 0x2f, 0xce, 0x67, 0xde, 0x7f, 0xfc, 0x3f, 0x5b, 0x53, 0xcf, 0xc6, 0xff, 0xfa, 0x2f, 0xfe, 0x36, 0xd1, 0x7e,
-    0xa8, 0x6e, 0xff, 0xaf, 0x5c, 0x2f, 0x4f, 0x8a, 0xff, 0x36, 0x95, 0x8b, 0xec, 0xde, 0xf5, 0x7f, 0x65, 0x4e, 0xad, 0xfa,
-    0xf8, 0x8f, 0x45, 0x78, 0xa4, 0x26, 0x9f, 0x5f, 0xf1, 0x60, 0xed, 0xbc, 0x8d, 0x5b, 0xf9, 0x3d, 0xd1, 0xfe, 0xdf, 0x11,
-    0xff, 0xd5, 0xe3, 0x7f, 0xf1, 0x35, 0x3c, 0xf6, 0x8c, 0x74, 0x45, 0xc6, 0xff, 0xda, 0x09, 0xfd, 0xff, 0x51, 0x3b, 0x31,
-    0x5a, 0x2a, 0xf5, 0x6b, 0x53, 0x55, 0x66, 0xdb, 0x5e, 0x8d, 0xff, 0x4c, 0xfd, 0x1f, 0x1d, 0x87, 0xc8, 0xb4, 0x10, 0xdb,
-    0x60, 0xe4, 0x60, 0xf4, 0x9b, 0xeb, 0x59, 0x43, 0x73, 0xed, 0xff, 0x1d, 0xf1, 0x7f, 0x56, 0xff, 0xbf, 0x25, 0x47, 0x35,
-    0xeb, 0xda, 0xff, 0x2d, 0xd1, 0xca, 0xcc, 0xdc, 0x6d, 0x22, 0xa5, 0xbd, 0x5e, 0x66, 0x57, 0xd6, 0xff, 0xb9, 0xd5, 0xe6,
-    0xda, 0xb6, 0x7c, 0xa7, 0x91, 0xf6, 0xff, 0x9e, 0xa7, 0x70, 0xcf, 0xae, 0xff, 0xcf, 0x7c, 0x3a, 0xb9, 0x73, 0x7f, 0xee,
-    0x93, 0x77, 0x6e, 0x4f, 0x1e, 0xe9, 0x3b, 0xf4, 0xff, 0x23, 0xe3, 0xff, 0xd1, 0x9c, 0xc7, 0xe3, 0xda, 0xb8, 0x0f, 0x46,
-    0xd3, 0xbe, 0x35, 0xfe, 0xef, 0x91, 0x9b, 0xf9, 0x7b, 0xe3, 0xbf, 0xa5, 0xf2, 0x4f, 0x5f, 0x11, 0xff, 0x73, 0x2d, 0xab,
-    0xd8, 0x8a, 0x88, 0xb9, 0x15, 0x93, 0xa3, 0x7d, 0xba, 0x76, 0xea, 0x95, 0x9e, 0x79, 0xfe, 0xb7, 0xe7, 0xfd, 0x9f, 0xda,
-    0x37, 0x0d, 0x9e, 0xfb, 0x3e, 0xd4, 0x3b, 0x8e, 0xe0, 0x8c, 0xfb, 0xf9, 0x78, 0xd5, 0x82, 0xbe, 0xe9, 0x98, 0x62, 0x4f,
-    0x75, 0xa3, 0xcf, 0x89, 0xf7, 0xdd, 0xef, 0xa3, 0xef, 0xff, 0x44, 0xcb, 0x54, 0xa6, 0xe5, 0x6f, 0xb9, 0x83, 0x5d, 0x19,
-    0xff, 0xfb, 0x5b, 0x49, 0x55, 0xef, 0x30, 0x7c, 0xdb, 0x95, 0xa2, 0x64, 0xbe, 0xe5, 0xbc, 0xae, 0x8f, 0xf4, 0xbe, 0xa5,
-    0x24, 0x5c, 0xe3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x3b, 0xe7, 0x8a, 0xf5, 0xb2,
-    0x4c, 0x0b, 0xb3, 0x79, 0xae, 0xfa, 0xc2, 0xcc, 0xcd, 0x36, 0xcc, 0xdd, 0x9c, 0xfd, 0x9e, 0x9f, 0xab, 0x1a, 0xf4, 0xc1,
-    0xbf, 0x68, 0x81, 0x3d, 0xed, 0xe1, 0x79, 0xc8, 0xeb, 0x6b, 0x34, 0xfc, 0xdc, 0xe3, 0x96, 0x28, 0x91, 0x36, 0x31, 0x4f,
-    0x75, 0x6d, 0xae, 0xde, 0xee, 0x63, 0xae, 0xc9, 0x96, 0x1e, 0xc9, 0xfb, 0x37, 0x9b, 0xbd, 0x64, 0x47, 0xa9, 0xe4, 0x66,
-    0xbb, 0xf7, 0xa5, 0xbf, 0xc7, 0x72, 0x3f, 0xf4, 0xc3, 0x18, 0xaa, 0xb9, 0x3f, 0xcd, 0x65, 0xe8, 0x5b, 0x9f, 0xb3, 0x3e,
-    0x97, 0xdf, 0xb3, 0x6d, 0xcc, 0x89, 0xbd, 0x7e, 0xb5, 0xc7, 0x23, 0x25, 0xb7, 0x75, 0x6e, 0xae, 0xfe, 0x8e, 0x63, 0x8e,
-    0xe7, 0xb3, 0x9d, 0xff, 0x9e, 0x4c, 0xed, 0x37, 0x9e, 0x85, 0x99, 0xa9, 0x27, 0xda, 0xd4, 0xdd, 0x7a, 0x2d, 0xca, 0xa3,
-    0x77, 0x99, 0xf5, 0xf8, 0xaf, 0x99, 0x83, 0xde, 0x87, 0xc7, 0x13, 0x5b, 0x81, 0x63, 0x26, 0xbf, 0xf7, 0xfb, 0x5a, 0x8b,
-    0xf9, 0xf8, 0xff, 0xd6, 0x36, 0xf6, 0xfd, 0x4a, 0xa0, 0x2f, 0xd6, 0xf1, 0x9f, 0x23, 0x6c, 0xa6, 0x15, 0x5f, 0x13, 0xff,
-    0xeb, 0xf7, 0xad, 0xfd, 0xf1, 0xdf, 0x43, 0xdf, 0x10, 0x6d, 0xcf, 0xde, 0x73, 0x6b, 0x2e, 0xfe, 0xc7, 0x57, 0xd0, 0x9e,
-    0xad, 0x55, 0xab, 0x28, 0xe5, 0x72, 0x94, 0xb7, 0x60, 0xef, 0x21, 0x9b, 0x11, 0x68, 0x35, 0x1b, 0x5b, 0x0f, 0xf5, 0x46,
-    0xf6, 0xd4, 0xff, 0xf9, 0xfe, 0xff, 0x7c, 0xbb, 0x2d, 0x3e, 0x7e, 0x90, 0xb9, 0x96, 0x76, 0xb4, 0xc3, 0xcf, 0xd8, 0x1a,
-    0xc9, 0x1b, 0x77, 0xd5, 0xd6, 0xdd, 0xf1, 0xdf, 0xff, 0xb3, 0x8e, 0xe2, 0x51, 0x44, 0xad, 0xdf, 0x1d, 0xee, 0xd7, 0xb2,
-    0x88, 0x8f, 0xff, 0xad, 0x1e, 0x43, 0xc5, 0x11, 0xcf, 0xf5, 0x0b, 0x67, 0xa3, 0xb4, 0xae, 0xfe, 0xcf, 0x9c, 0xf5, 0x2b,
-    0xb7, 0x66, 0x46, 0xf7, 0xde, 0x1a, 0xff, 0x73, 0xe5, 0x76, 0x45, 0xfc, 0x57, 0xe7, 0x13, 0x9c, 0x69, 0x4d, 0x44, 0xb3,
-    0x34, 0x5f, 0xd1, 0x97, 0x9c, 0xcd, 0xce, 0x9f, 0x5b, 0x7f, 0xf6, 0x4d, 0xf1, 0xdf, 0x8a, 0xda, 0xff, 0x67, 0x6e, 0xbd,
-    0x47, 0xfb, 0xbf, 0xbf, 0xa0, 0xfe, 0xcf, 0x3e, 0xd7, 0x99, 0x3f, 0x86, 0x73, 0x8e, 0x36, 0x9e, 0x81, 0x3f, 0xf3, 0x04,
-    0xe3, 0xca, 0xbe, 0x70, 0x76, 0x6b, 0x36, 0xfe, 0xbf, 0xb5, 0xfe, 0x7f, 0x43, 0xfc, 0xe7, 0x9f, 0x65, 0xaf, 0x8c, 0xff,
-    0x9d, 0x75, 0x44, 0xf9, 0xf6, 0x51, 0xee, 0xd9, 0xff, 0x3b, 0xeb, 0xff, 0x9a, 0x35, 0xe7, 0x9f, 0x16, 0xff, 0x33, 0xa3,
-    0x4c, 0x4f, 0x8d, 0xff, 0x8a, 0x18, 0xa9, 0x1a, 0xff, 0xaf, 0xbe, 0x03, 0xb4, 0xad, 0x99, 0xeb, 0x9f, 0x17, 0xe1, 0xe3,
-    0xf8, 0x8f, 0xbc, 0xff, 0xf3, 0xee, 0xf8, 0x9f, 0x7b, 0x37, 0xe6, 0xd9, 0xf1, 0xdf, 0x93, 0x2b, 0xd6, 0xbc, 0xf3, 0xf9,
-    0x70, 0x6c, 0xfd, 0xe9, 0xe7, 0xc5, 0x7f, 0xfc, 0xed, 0x8c, 0xb7, 0x3f, 0xff, 0xcb, 0xbd, 0x1d, 0xb0, 0xf7, 0xf9, 0xdf,
-    0x13, 0xde, 0x2b, 0xe1, 0x4d, 0xf7, 0x42, 0xe7, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xa0, 0x6a, 0x46, 0x46, 0x3c, 0x67, 0xcf, 0xdc, 0x4c, 0xb1, 0x6b, 0x72, 0xdf, 0x47, 0x32, 0x15, 0xcc, 0xec, 0x4f, 0x3c,
-    0xff, 0x41, 0x24, 0xdb, 0xfe, 0xd1, 0xb9, 0x89, 0xe6, 0x1c, 0xcf, 0xe4, 0x32, 0xda, 0x75, 0x8c, 0xeb, 0x59, 0xe1, 0xe7,
-    0x7f, 0x61, 0x2e, 0xf7, 0x63, 0x2c, 0x2e, 0xee, 0x71, 0xae, 0x33, 0xf3, 0xee, 0x76, 0x67, 0x8a, 0xef, 0xc9, 0x5c, 0xef,
-    0xf5, 0x7b, 0x95, 0xcd, 0xd3, 0x1f, 0xdb, 0xe7, 0x48, 0x09, 0xf7, 0xc4, 0x4a, 0x2c, 0x6d, 0xe2, 0x4a, 0xaa, 0xce, 0xef,
-    0x93, 0x59, 0x99, 0x63, 0xfe, 0x8e, 0x5c, 0x99, 0xb3, 0xbf, 0x25, 0xf2, 0xf5, 0xdf, 0xe5, 0x5c, 0xdf, 0xb5, 0x65, 0x71,
-    0x9c, 0xf1, 0xe8, 0xbe, 0xad, 0xa4, 0x1e, 0xca, 0x6e, 0xfa, 0x9c, 0x7c, 0x04, 0xb1, 0x3a, 0xf7, 0xbd, 0xed, 0xe2, 0xf7,
-    0x1f, 0x73, 0x26, 0xb7, 0xc0, 0x4c, 0x1d, 0xde, 0x02, 0xf1, 0x7f, 0xc7, 0xfc, 0x55, 0xb9, 0xcc, 0x57, 0x33, 0xa5, 0x78,
-    0xd6, 0xb6, 0x23, 0xbf, 0x07, 0x56, 0x62, 0xbb, 0xf2, 0x18, 0xf7, 0xe7, 0xec, 0x38, 0xce, 0x26, 0xf2, 0xe4, 0x73, 0xdd,
-    0xb6, 0xe7, 0x82, 0x89, 0xd6, 0xff, 0x4f, 0x8d, 0xff, 0xd1, 0x6a, 0x04, 0x9f, 0xd7, 0x25, 0x3b, 0x6f, 0x5b, 0xc5, 0x5a,
-    0x06, 0xab, 0x3d, 0xa0, 0x1d, 0xdb, 0xae, 0xcc, 0xd9, 0x75, 0xf6, 0xb1, 0xc6, 0xcb, 0xe8, 0xca, 0xf8, 0x9f, 0xe9, 0xd7,
-    0x7d, 0x5b, 0xfd, 0xdf, 0x2f, 0xdf, 0x36, 0x7f, 0x17, 0xbb, 0xfb, 0xb5, 0xbd, 0x7f, 0xcd, 0x8e, 0x27, 0xc4, 0xf8, 0x5d,
-    0xe3, 0xff, 0xaa, 0x8c, 0xc7, 0x35, 0x2b, 0x2d, 0x45, 0xfb, 0xff, 0x4f, 0xb8, 0x26, 0x7a, 0x72, 0x2d, 0xe3, 0xd8, 0x0a,
-    0xce, 0xd5, 0xdb, 0xc4, 0xff, 0xdd, 0xeb, 0xff, 0x99, 0xb1, 0x95, 0xfb, 0xe5, 0xaf, 0xcc, 0xae, 0xd3, 0xfb, 0xee, 0xeb,
-    0xa5, 0x85, 0x9e, 0x79, 0xed, 0xd8, 0x26, 0xfe, 0xc5, 0xbf, 0xf8, 0xbf, 0xe2, 0x7a, 0xe9, 0x5f, 0xd1, 0xff, 0x17, 0xff,
-    0xe2, 0x3f, 0xf2, 0xcc, 0xec, 0xfe, 0xfd, 0xff, 0x5d, 0x4f, 0x2a, 0xdf, 0x36, 0xfe, 0x1f, 0x7d, 0xda, 0xfb, 0xa4, 0xf1,
-    0xff, 0xdd, 0xcf, 0xff, 0xae, 0xf8, 0xdd, 0xdc, 0x3e, 0xbf, 0xff, 0x19, 0xf7, 0x3b, 0xf2, 0xc5, 0xf3, 0xfc, 0xeb, 0x50,
-    0x19, 0x80, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xe7, 0x00, 0xaf, 0xbe,
-    0xa7, 0x7f, 0x66, 0xbe, 0xf7, 0xc8, 0x9c, 0xcf, 0xb9, 0xef, 0x04, 0xf1, 0x1f, 0x9b, 0xa7, 0x73, 0x66, 0xbe, 0xf7, 0xfc,
-    0xdc, 0x7c, 0xe7, 0x1a, 0xde, 0x37, 0x53, 0x5c, 0x6c, 0x43, 0x2e, 0xc2, 0xa3, 0x39, 0x3a, 0xef, 0xb2, 0xed, 0x1d, 0x2b,
-    0x73, 0xc0, 0xbd, 0x6a, 0xd5, 0xa7, 0xc4, 0x7f, 0xdb, 0x96, 0xef, 0x12, 0xbe, 0xb1, 0xff, 0x2f, 0xfe, 0x41, 0xfc, 0x8b,
-    0x7f, 0xd0, 0xfe, 0x17, 0xff, 0x60, 0xfc, 0xff, 0xaa, 0x58, 0x9d, 0x59, 0x27, 0xe6, 0xc9, 0x4f, 0x31, 0xe0, 0x7e, 0x77,
-    0x80, 0xf8, 0xdd, 0xa1, 0x7a, 0x5b, 0x1b, 0xde, 0x37, 0x3c, 0x23, 0x84, 0xb3, 0xe2, 0xff, 0x9a, 0x51, 0x8a, 0xd5, 0xf8,
-    0xd7, 0xfe, 0x87, 0x37, 0xf5, 0x46, 0x22, 0xef, 0xff, 0x89, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xdd, 0x5b, 0x6e, 0xe4, 0x36, 0x10, 0x05, 0x50, 0xee, 0x7f, 0xa1, 0xd9, 0x06, 0x83, 0x20, 0x18, 0x24, 0xe3, 0xb1, 0x45,
+    0xb2, 0xaa, 0xa8, 0x57, 0x9f, 0x1c, 0xe4, 0xc7, 0x1a, 0xb7, 0xd5, 0x94, 0xae, 0x44, 0x3d, 0x58, 0xec, 0x0d, 0x00, 0x00,
+    0x00, 0xf8, 0x78, 0xff, 0xfc, 0xf7, 0xfd, 0xcf, 0xbe, 0x5b, 0x72, 0xbc, 0xec, 0xd7, 0xbf, 0x18, 0x2d, 0xe9, 0x3f, 0xae,
+    0x49, 0x3f, 0x58, 0x36, 0xf7, 0xb7, 0x62, 0xeb, 0xd5, 0x97, 0xda, 0xa6, 0x1f, 0xb4, 0x5a, 0x4b, 0xff, 0xbc, 0x1d, 0xb6,
+    0x7d, 0x0f, 0xb4, 0x5e, 0x3b, 0x5c, 0xe7, 0x9f, 0x7f, 0x73, 0xbc, 0xa4, 0x72, 0x3d, 0x57, 0xda, 0xb4, 0x72, 0xfb, 0xac,
+    0xb4, 0x60, 0x5b, 0x5e, 0x7a, 0xdc, 0x52, 0x6d, 0xaa, 0x1d, 0xc7, 0x9f, 0xdc, 0x6f, 0x92, 0xff, 0xe3, 0x6f, 0xf3, 0x2b,
+    0x83, 0x47, 0x5b, 0x7f, 0x7e, 0xdf, 0x68, 0xd3, 0xc7, 0x84, 0xff, 0xfe, 0x76, 0x5d, 0x42, 0xb3, 0x9f, 0xd2, 0x87, 0x47,
+    0xc2, 0x95, 0x16, 0x3a, 0x6e, 0xbd, 0x16, 0xca, 0xc4, 0xe8, 0x33, 0x23, 0xeb, 0x32, 0xb3, 0x6c, 0x6e, 0xfd, 0x62, 0xfb,
+    0xfc, 0xf8, 0xf3, 0x8e, 0xf7, 0xb4, 0xa3, 0x2d, 0x3c, 0xda, 0x73, 0x8f, 0xbf, 0xdf, 0xfa, 0x56, 0xcd, 0xa4, 0x7e, 0x4f,
+    0xfe, 0xfb, 0xff, 0x72, 0xd6, 0x83, 0x19, 0xcc, 0x9f, 0x1b, 0xd6, 0xcf, 0x0a, 0xc7, 0xc7, 0xac, 0x7d, 0x2d, 0x7c, 0xd4,
+    0x4e, 0x3d, 0xb0, 0x0f, 0xaf, 0x6f, 0xb3, 0xe8, 0xdf, 0xd9, 0x91, 0xff, 0x8a, 0x14, 0xb7, 0x89, 0x33, 0x62, 0xa4, 0x4f,
+    0x71, 0xdc, 0xe3, 0xcc, 0x27, 0x7c, 0xb4, 0x2e, 0x91, 0xfe, 0x4c, 0xbe, 0x2d, 0x57, 0xfb, 0x1c, 0xfb, 0xf2, 0x5f, 0x71,
+    0xbe, 0x5d, 0x3f, 0x97, 0xec, 0x6b, 0xdf, 0x99, 0xf3, 0xf5, 0xfd, 0xf3, 0xbf, 0xfe, 0xdd, 0x2a, 0xf2, 0x5f, 0xb3, 0x55,
+    0xd6, 0xf3, 0xdf, 0xe5, 0x7f, 0xfa, 0xfc, 0xff, 0x73, 0xab, 0xf7, 0xe0, 0xf5, 0x57, 0xfe, 0xaa, 0xbd, 0xaa, 0x9f, 0x7f,
+    0x6d, 0xfe, 0x47, 0xd7, 0x83, 0x6d, 0x39, 0xff, 0xb1, 0xab, 0xcf, 0xc8, 0xd5, 0x46, 0x55, 0xff, 0x7f, 0x6f, 0xef, 0xe1,
+    0xf8, 0xea, 0x31, 0x92, 0xf0, 0xb5, 0x7b, 0x1e, 0x35, 0x9f, 0x7c, 0x45, 0xfe, 0xdb, 0xc4, 0xda, 0xf4, 0xe1, 0xf5, 0xff,
+    0xec, 0x11, 0x7e, 0x2d, 0xff, 0x75, 0xe7, 0x8e, 0x2b, 0xf3, 0xdf, 0x83, 0x77, 0xc4, 0xaa, 0xaf, 0xe3, 0xe3, 0xdf, 0x6d,
+    0xed, 0xfe, 0xdc, 0x79, 0xd7, 0xff, 0xa3, 0x7e, 0x60, 0x26, 0xff, 0x9f, 0xd2, 0xff, 0x9f, 0xe9, 0x69, 0x8f, 0x5a, 0x71,
+    0xfe, 0x78, 0x76, 0xc5, 0xd9, 0xfc, 0xda, 0xfc, 0xf7, 0xa9, 0x7b, 0xd8, 0xf7, 0xce, 0x7f, 0x5b, 0xbe, 0xca, 0xab, 0xdc,
+    0x77, 0xa3, 0x7b, 0xd4, 0x68, 0xcf, 0x9d, 0x39, 0xaa, 0xdd, 0x33, 0xff, 0xb1, 0xfd, 0xb9, 0x0f, 0x7b, 0xf9, 0x3d, 0x91,
+    0xff, 0xf6, 0x9a, 0xfc, 0xf7, 0xe0, 0xdd, 0x8e, 0x9f, 0xfb, 0x7b, 0xb1, 0xfb, 0x71, 0x91, 0x9e, 0x7c, 0xa6, 0x8f, 0x52,
+    0x95, 0xff, 0x7b, 0x6c, 0x49, 0xe7, 0xff, 0x8a, 0xfb, 0xff, 0xed, 0xb0, 0x87, 0xff, 0x59, 0xf9, 0x3f, 0x7e, 0xfe, 0xd7,
+    0x6e, 0xff, 0xac, 0x2e, 0x9a, 0xff, 0x9a, 0xfb, 0xff, 0xe7, 0x1f, 0xad, 0x67, 0xae, 0x5c, 0x73, 0xf9, 0xbf, 0xd3, 0xf3,
+    0xbf, 0xbd, 0xf9, 0x1f, 0x3d, 0xf5, 0x7e, 0x53, 0xff, 0xbf, 0xf2, 0x3e, 0x7e, 0xfc, 0x9d, 0x9b, 0x7d, 0xef, 0x14, 0xb5,
+    0xd0, 0x9b, 0x5c, 0xf7, 0x4c, 0x7f, 0xf4, 0x39, 0x50, 0xee, 0x2e, 0xdd, 0xd5, 0xef, 0xff, 0xd4, 0xb7, 0xe0, 0x79, 0xeb,
+    0xd3, 0x6f, 0xdc, 0x67, 0xe4, 0x5d, 0x6f, 0xc9, 0xda, 0xbb, 0xce, 0x7c, 0x3e, 0x1b, 0xff, 0x5b, 0xb6, 0x0f, 0x3b, 0xcf,
+    0x6c, 0x5a, 0xe2, 0x19, 0xf7, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xc9, 0xe3,
+    0x17, 0x22, 0x63, 0x0d, 0xa3, 0x75, 0x08, 0x6a, 0xeb, 0x21, 0xf4, 0x2f, 0x75, 0x58, 0x57, 0x3f, 0x7b, 0x76, 0xac, 0x65,
+    0x66, 0x9c, 0x66, 0xed, 0xef, 0x56, 0x55, 0x96, 0xa8, 0x1d, 0x79, 0x3e, 0xae, 0x77, 0xb3, 0x5a, 0xad, 0x39, 0x56, 0x05,
+    0x6d, 0xbd, 0xa6, 0x79, 0xa6, 0x0e, 0x5f, 0xf5, 0xd8, 0xea, 0x9a, 0xfa, 0x68, 0xf3, 0x63, 0xde, 0x6b, 0x2a, 0x8d, 0xd4,
+    0xe5, 0x7f, 0x7d, 0x3b, 0xf4, 0x2f, 0xeb, 0xd1, 0x2f, 0xa8, 0xc4, 0x50, 0xbf, 0x74, 0xee, 0xe7, 0x77, 0xca, 0xff, 0xec,
+    0x31, 0x38, 0xbb, 0xf7, 0xc6, 0x47, 0xdc, 0x8d, 0xaa, 0x4f, 0x5c, 0x5f, 0x5b, 0xa5, 0x15, 0xd5, 0x47, 0xad, 0xcf, 0x7f,
+    0x3b, 0xe9, 0xfc, 0x1f, 0xcd, 0x7f, 0x1f, 0xce, 0x66, 0x73, 0xdf, 0xfc, 0xaf, 0xd7, 0x9d, 0xc8, 0xfe, 0xd5, 0xdc, 0x4c,
+    0x0e, 0xf1, 0x5a, 0xe3, 0xf9, 0xfc, 0xe7, 0x2a, 0x05, 0x9d, 0x7b, 0x6e, 0xbd, 0x3e, 0xff, 0xd1, 0x16, 0xb9, 0x2a, 0xff,
+    0xf1, 0xbd, 0xa4, 0x0f, 0xab, 0x59, 0xdf, 0x31, 0xff, 0xfd, 0xe3, 0xf3, 0x5f, 0x5f, 0xcb, 0x37, 0x9a, 0x80, 0x48, 0x1d,
+    0xf4, 0x99, 0xeb, 0xea, 0xd5, 0x2b, 0xbb, 0xf8, 0x75, 0x57, 0xf4, 0xfc, 0x3f, 0xfa, 0xfd, 0x99, 0x6f, 0x72, 0x75, 0xfe,
+    0xdb, 0x30, 0xe1, 0x3d, 0x91, 0xd2, 0xf8, 0x1d, 0x90, 0x4c, 0xcd, 0xab, 0x6b, 0xf2, 0xdf, 0x42, 0x33, 0x06, 0xc6, 0xcf,
+    0xf3, 0x77, 0xcf, 0x7f, 0xf5, 0xb5, 0x41, 0xf4, 0x8e, 0x41, 0xf5, 0x3a, 0xde, 0xef, 0xfa, 0xbf, 0x17, 0xf4, 0x12, 0x77,
+    0xe4, 0x3f, 0xdb, 0x3e, 0xd1, 0x34, 0xc5, 0xf2, 0x3f, 0x7f, 0x76, 0x8f, 0xcd, 0x26, 0x10, 0x5b, 0xf6, 0xde, 0xfc, 0xd7,
+    0xd6, 0x4f, 0x5d, 0xbf, 0x57, 0xfc, 0xa6, 0xfc, 0x47, 0xaf, 0xff, 0xe7, 0xaf, 0xb8, 0xee, 0x95, 0xff, 0x36, 0x31, 0x3f,
+    0xcf, 0xd9, 0xf9, 0x1f, 0x55, 0x5b, 0x7d, 0x5b, 0xfe, 0xa3, 0xd9, 0x8a, 0xce, 0x67, 0xd4, 0xc2, 0x67, 0xba, 0xb7, 0x9f,
+    0xff, 0xdb, 0x4b, 0xf3, 0x1f, 0x9f, 0x5f, 0xe6, 0x59, 0xf9, 0xaf, 0xee, 0x35, 0x5c, 0x5f, 0x57, 0x2f, 0x7a, 0xb7, 0x31,
+    0x7e, 0x97, 0xf2, 0xdd, 0xfd, 0xff, 0xbe, 0xa1, 0x17, 0x9e, 0x7d, 0xfe, 0x7f, 0x55, 0xfe, 0x77, 0x3d, 0xff, 0x6b, 0xd3,
+    0x73, 0xfe, 0xed, 0x5a, 0x2b, 0xf9, 0xaf, 0xce, 0x7f, 0xe5, 0xdc, 0x89, 0x6b, 0xff, 0xa2, 0xfa, 0xfd, 0x9f, 0xfa, 0x3b,
+    0x69, 0xfb, 0xdf, 0xff, 0xd9, 0xd3, 0x3e, 0x6d, 0x6a, 0xc6, 0xb4, 0xf5, 0xbf, 0xda, 0x26, 0x67, 0x35, 0x8e, 0xad, 0x73,
+    0xfd, 0xdb, 0x37, 0x77, 0xcf, 0x7f, 0xec, 0x6d, 0x83, 0xe8, 0x6f, 0xc6, 0x7f, 0xe3, 0x3e, 0x73, 0x81, 0x7d, 0xe6, 0x5b,
+    0x94, 0x5a, 0xa1, 0xba, 0x85, 0xb4, 0x2a, 0x8e, 0x00, 0xef, 0x7a, 0xcf, 0x7c, 0xd7, 0xbf, 0x07, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xbc, 0xd1, 0x2b, 0xb3, 0x35, 0xb7, 0xff, 0xfc, 0x69, 0x0f, 0x8d, 0x77,
+    0xed, 0xc9, 0xdf, 0xeb, 0xcb, 0x75, 0x30, 0xd6, 0xc6, 0x9d, 0x56, 0xd4, 0x60, 0xa8, 0xae, 0x23, 0x35, 0x6e, 0x97, 0xd9,
+    0x2d, 0x3d, 0x1e, 0x49, 0xbc, 0xba, 0x87, 0x44, 0xc6, 0xf5, 0x64, 0x2a, 0xea, 0x64, 0xe6, 0x12, 0x88, 0x57, 0x42, 0x6c,
+    0x8b, 0x9f, 0x1a, 0xdb, 0x6a, 0x91, 0xf1, 0xe1, 0x3b, 0xf3, 0x1f, 0xab, 0x2d, 0x79, 0x45, 0xa5, 0xb5, 0xf8, 0xde, 0x54,
+    0xbb, 0x86, 0xf5, 0xad, 0x32, 0xb7, 0xb6, 0xb9, 0x9f, 0x67, 0xc6, 0xf4, 0xe7, 0x6a, 0x0f, 0xac, 0xb6, 0x44, 0x9f, 0xaa,
+    0x96, 0xb2, 0xfa, 0x9b, 0x3f, 0xff, 0xde, 0xf1, 0xb2, 0x3e, 0x55, 0x39, 0x62, 0xed, 0x53, 0xbf, 0xff, 0xb7, 0xfb, 0x2a,
+    0x27, 0xbc, 0x23, 0xff, 0x2d, 0x70, 0x94, 0x3d, 0x37, 0xff, 0xf1, 0xe3, 0x54, 0x2b, 0xac, 0xd2, 0xb3, 0x3e, 0x37, 0x46,
+    0x64, 0xe4, 0x6e, 0xbe, 0x46, 0xd0, 0x19, 0x15, 0x35, 0xfe, 0xcc, 0xd7, 0xca, 0x56, 0x9b, 0x4b, 0xff, 0xf7, 0xc7, 0xb8,
+    0x76, 0xd0, 0xdb, 0x9a, 0xcd, 0x7f, 0x0f, 0xd4, 0x1e, 0xa9, 0xa9, 0xc8, 0xf7, 0x9c, 0xfc, 0xcf, 0x5c, 0x6d, 0x9c, 0x97,
+    0xff, 0xf1, 0xcf, 0xeb, 0xaa, 0x6a, 0x57, 0xd6, 0xda, 0x19, 0xd5, 0x5a, 0x6e, 0xa1, 0x2d, 0xd2, 0x83, 0xc7, 0x95, 0x5e,
+    0x5c, 0xcf, 0xbc, 0x7f, 0xf9, 0x7f, 0xf6, 0x4c, 0x3d, 0x9b, 0xff, 0xd5, 0xbf, 0x78, 0x4d, 0xff, 0x7f, 0x65, 0x7f, 0x79,
+    0x52, 0xfe, 0xdb, 0x54, 0x2d, 0xb2, 0x7b, 0x9f, 0xff, 0x2b, 0x6b, 0x6a, 0xd5, 0xe7, 0x3f, 0x96, 0xf0, 0xc8, 0xfe, 0x3e,
+    0x3f, 0xe3, 0xc1, 0xda, 0x76, 0x1b, 0xf7, 0xf2, 0x7b, 0xa2, 0xff, 0x9f, 0xcf, 0x7f, 0x5d, 0x9f, 0x67, 0xed, 0xea, 0xf9,
+    0x8a, 0xfc, 0xf7, 0x44, 0x65, 0xf4, 0xfd, 0xd7, 0xff, 0xa3, 0x7e, 0x62, 0xb4, 0x55, 0xea, 0xe7, 0xa6, 0xaa, 0xac, 0xb6,
+    0xbd, 0x9a, 0xff, 0xcc, 0xf9, 0x3f, 0x7a, 0x1f, 0x22, 0xd3, 0x43, 0x5c, 0xbb, 0x1e, 0xcf, 0x1f, 0x01, 0xd6, 0xfa, 0xff,
+    0x67, 0xdc, 0xff, 0xef, 0xa7, 0x5d, 0xff, 0xb7, 0xd4, 0x2c, 0x46, 0x95, 0xfd, 0xff, 0x96, 0xe8, 0x65, 0x66, 0x8e, 0x36,
+    0x91, 0xd6, 0x5e, 0x6f, 0xb3, 0x2b, 0xcf, 0xff, 0xb9, 0xd9, 0xe6, 0xda, 0xb6, 0x7a, 0xa7, 0x91, 0xfe, 0xff, 0x9e, 0x14,
+    0x56, 0xfc, 0xc5, 0xca, 0xeb, 0xff, 0xeb, 0xe7, 0x66, 0xc8, 0xe4, 0xff, 0xac, 0xde, 0xd3, 0xfe, 0x6f, 0x5e, 0x5b, 0x47,
+    0xfa, 0x0e, 0xd7, 0xff, 0x91, 0xfb, 0xff, 0xd1, 0x9a, 0xc7, 0xe3, 0xb3, 0x71, 0xff, 0xf1, 0x0a, 0xff, 0xcc, 0xb3, 0xf1,
+    0xf9, 0x47, 0x9c, 0xda, 0xeb, 0x7f, 0xf9, 0xdf, 0xf3, 0xdd, 0x2b, 0x6b, 0xe7, 0xee, 0xce, 0xff, 0x5c, 0xcf, 0x2a, 0x36,
+    0x23, 0x62, 0x6e, 0xc6, 0xe4, 0xe8, 0x35, 0x5d, 0x3b, 0x75, 0x4f, 0x6f, 0x17, 0xf4, 0xfe, 0xab, 0x67, 0xe4, 0x79, 0xe7,
+    0xfb, 0x50, 0xef, 0xf8, 0x06, 0x67, 0x1c, 0xcf, 0x7b, 0x61, 0xcf, 0x34, 0x7f, 0xff, 0x2f, 0x7e, 0xb4, 0x6a, 0x17, 0xd4,
+    0x20, 0x3e, 0xfb, 0x2f, 0xc6, 0x9f, 0x56, 0x20, 0xff, 0x6b, 0xf9, 0xdf, 0xdf, 0x4b, 0xaa, 0x7a, 0x87, 0xe1, 0xd3, 0xf6,
+    0x14, 0x2d, 0xf3, 0x29, 0xdb, 0x75, 0xfd, 0x4e, 0xef, 0x5b, 0x5a, 0xc2, 0x3e, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x6b, 0xf4, 0x4f, 0xac, 0x9a, 0xc2, 0x6c, 0x2d, 0xab, 0xbe, 0x30, 0x3a, 0xb3, 0x0d,
+    0xeb, 0x33, 0x67, 0x3f, 0xe7, 0xeb, 0xcc, 0x05, 0x7d, 0xf0, 0x2f, 0x5a, 0x60, 0x4d, 0x7b, 0x78, 0xac, 0xf1, 0xfa, 0x3c,
+    0x0c, 0x5f, 0xd7, 0xb8, 0x25, 0x5a, 0x64, 0x3c, 0x92, 0x77, 0x7d, 0xc6, 0x86, 0xbd, 0xdf, 0xb9, 0xa6, 0x22, 0x7a, 0xa4,
+    0xb6, 0xdf, 0x6c, 0x85, 0x92, 0xb3, 0x5b, 0x25, 0x33, 0x62, 0xbb, 0x05, 0x8e, 0x0d, 0xa3, 0x7a, 0x42, 0x2b, 0xb5, 0xba,
+    0xd6, 0x8e, 0x4f, 0x73, 0x55, 0xf8, 0xd6, 0xc7, 0xa5, 0xcf, 0xd5, 0xf0, 0x6c, 0x1b, 0xeb, 0x5e, 0xaf, 0xef, 0xed, 0xf1,
+    0xa4, 0xe4, 0x96, 0xce, 0x8d, 0xc7, 0xdf, 0xf1, 0x9d, 0xe3, 0x35, 0x6b, 0xe7, 0x3f, 0x27, 0x73, 0xf6, 0x1b, 0xa7, 0x74,
+    0x4f, 0xab, 0xc4, 0xd2, 0x9f, 0xa9, 0x08, 0xb3, 0x9e, 0xff, 0x9a, 0xf5, 0x1e, 0xcf, 0x89, 0x10, 0x9b, 0x65, 0x63, 0xa6,
+    0x86, 0xf7, 0xfb, 0xaa, 0x07, 0xe4, 0xf3, 0xff, 0xa9, 0x35, 0x17, 0x9e, 0xd4, 0x02, 0x7d, 0x31, 0x61, 0x33, 0xbd, 0xf8,
+    0x9a, 0xfc, 0x8f, 0xe7, 0x4a, 0x3a, 0x3f, 0xff, 0x3d, 0xf4, 0x09, 0xf1, 0x9e, 0xdb, 0x1d, 0x97, 0xe6, 0xf2, 0x3f, 0xde,
+    0x83, 0xf6, 0x2c, 0xad, 0x9a, 0x29, 0x29, 0x57, 0x87, 0xbc, 0x05, 0xaf, 0x1e, 0x32, 0x4b, 0x23, 0xb5, 0x11, 0x7a, 0xe8,
+    0x6a, 0x64, 0xcf, 0xf9, 0x3f, 0x7f, 0xfd, 0x3f, 0xdf, 0x6f, 0x8b, 0xdf, 0x3f, 0xc8, 0xec, 0x4b, 0x3b, 0xfa, 0xe1, 0x67,
+    0x2c, 0x8d, 0xd4, 0x86, 0xbb, 0x6a, 0xe9, 0xee, 0xfc, 0xf7, 0xdf, 0xe6, 0x4a, 0x3c, 0x4a, 0xd4, 0xfa, 0xd1, 0x21, 0x77,
+    0x64, 0xd9, 0xdb, 0xd7, 0x59, 0xbd, 0xff, 0xb7, 0xba, 0x96, 0x75, 0x73, 0x99, 0x45, 0xab, 0x52, 0x7e, 0xfd, 0xfd, 0xba,
+    0xf3, 0xff, 0xbe, 0xad, 0xbe, 0x77, 0x69, 0xe6, 0xee, 0xde, 0x5b, 0xf3, 0x3f, 0xd7, 0x6e, 0xef, 0xc8, 0xff, 0xb8, 0x7e,
+    0x6b, 0x2f, 0xba, 0x4f, 0x71, 0xc6, 0x37, 0x9a, 0xad, 0xc0, 0x9f, 0x9b, 0x63, 0xf6, 0x4d, 0xf9, 0x6f, 0x45, 0xfd, 0xff,
+    0x33, 0x97, 0xde, 0xa3, 0xff, 0xdf, 0x5f, 0x90, 0xff, 0xec, 0x73, 0x9d, 0x8a, 0x19, 0x8e, 0xcf, 0xce, 0x7f, 0xf6, 0x98,
+    0x78, 0xaf, 0x6b, 0xe1, 0xec, 0xd2, 0x6c, 0xfe, 0x3f, 0xf5, 0xfc, 0xff, 0x86, 0xfc, 0xe7, 0x9f, 0x65, 0xaf, 0xdc, 0xff,
+    0x3b, 0xef, 0x1d, 0x88, 0xbe, 0x31, 0xff, 0xed, 0x43, 0xcf, 0xff, 0x35, 0xf3, 0xca, 0x3f, 0x2d, 0xff, 0x33, 0x77, 0x99,
+    0x9e, 0x9a, 0xff, 0x8a, 0x8c, 0x54, 0xdd, 0xff, 0xaf, 0x7f, 0x0b, 0x6a, 0x67, 0x75, 0xfa, 0xe7, 0x25, 0x7c, 0x9c, 0xff,
+    0xc8, 0xfb, 0x3f, 0xef, 0xce, 0xff, 0xdc, 0xbb, 0x31, 0xcf, 0xce, 0x7f, 0x4f, 0xce, 0x4a, 0xf3, 0xce, 0xe7, 0xc3, 0xb1,
+    0x39, 0xa6, 0x9f, 0x97, 0xff, 0xfc, 0x3b, 0x65, 0x6f, 0x7d, 0xfe, 0x97, 0x7b, 0x3b, 0xe0, 0x9a, 0xe7, 0x7f, 0x77, 0x7a,
+    0xaf, 0x84, 0xb7, 0xbf, 0x55, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x66, 0xec,
+    0xeb, 0xea, 0x88, 0x9d, 0xb9, 0x71, 0x62, 0xd7, 0x54, 0xbe, 0x8f, 0xd4, 0x29, 0x98, 0x59, 0x9f, 0x78, 0xf5, 0x83, 0x48,
+    0xad, 0xfd, 0xa3, 0x6d, 0x13, 0xad, 0x38, 0x9e, 0xa9, 0x64, 0xb4, 0xeb, 0x3b, 0xae, 0xd7, 0x84, 0x9f, 0xff, 0x0b, 0x73,
+    0x95, 0x1f, 0x63, 0xb9, 0xb8, 0xfb, 0xb6, 0x9e, 0x1b, 0xa5, 0xb9, 0xb3, 0x4e, 0x7c, 0x4f, 0xd6, 0x34, 0xaf, 0x5f, 0xab,
+    0x6c, 0x95, 0xfe, 0xd8, 0x3a, 0x47, 0x5a, 0xb8, 0x27, 0xe6, 0x61, 0x69, 0x13, 0x7b, 0x52, 0x75, 0x75, 0x9f, 0xcc, 0xbc,
+    0x1c, 0xf3, 0x47, 0xe4, 0xca, 0x8a, 0xfd, 0x2d, 0x51, 0xad, 0xff, 0x2e, 0xdb, 0xfa, 0xda, 0xf1, 0xb4, 0xe3, 0x75, 0x7e,
+    0xce, 0x78, 0xdf, 0xb9, 0xd1, 0xe3, 0xcf, 0xf8, 0x36, 0x33, 0xc7, 0xb2, 0xf5, 0xea, 0x3c, 0xef, 0xac, 0xd9, 0xf0, 0xd9,
+    0x63, 0x95, 0xb3, 0x95, 0x05, 0x46, 0xc7, 0xba, 0xe7, 0x54, 0xaf, 0xca, 0xd5, 0xbd, 0x9a, 0x69, 0xc5, 0xb3, 0x96, 0x1d,
+    0xf9, 0x2b, 0x30, 0x0f, 0xdb, 0x95, 0xdf, 0x71, 0x7f, 0xc5, 0x8e, 0xe3, 0x5a, 0x22, 0xf7, 0xde, 0xd6, 0xd9, 0x1a, 0x59,
+    0xd9, 0x4a, 0x30, 0xd1, 0xf3, 0xff, 0x53, 0xf3, 0x3f, 0x9a, 0x8b, 0xe0, 0xe7, 0x59, 0xc9, 0xce, 0x5b, 0x56, 0x31, 0x93,
+    0xc1, 0xea, 0x15, 0xd0, 0x8e, 0x65, 0x57, 0x56, 0xec, 0x3a, 0xfb, 0xbb, 0xc6, 0xdb, 0xa8, 0xaa, 0x9f, 0x18, 0x9d, 0xb3,
+    0x64, 0xb4, 0xec, 0xd3, 0xce, 0xff, 0xfd, 0xf2, 0x65, 0xf3, 0x47, 0xb1, 0xbb, 0xef, 0xdb, 0xfb, 0x67, 0xec, 0x78, 0x42,
+    0xc6, 0x33, 0xb3, 0x4b, 0xed, 0xcc, 0xff, 0x3d, 0xeb, 0x1d, 0xf7, 0xd4, 0xb9, 0x31, 0x5b, 0xab, 0xef, 0x3e, 0xb9, 0xc9,
+    0xcc, 0x64, 0x1c, 0x9b, 0xbf, 0xb9, 0x7a, 0x99, 0xfc, 0xdf, 0x3b, 0xff, 0x73, 0xf7, 0x56, 0xee, 0x57, 0xbd, 0x32, 0x3b,
+    0x4b, 0xef, 0xfb, 0xfb, 0x8b, 0x7d, 0xc3, 0xd3, 0x9a, 0xba, 0xe7, 0x89, 0xf2, 0x7f, 0x97, 0xfe, 0xbf, 0xfc, 0xbf, 0x6f,
+    0x7f, 0xe9, 0xa9, 0x4a, 0xe6, 0xfa, 0xff, 0xf2, 0xff, 0xe6, 0xfc, 0xf7, 0x47, 0x5c, 0xff, 0xef, 0xda, 0x5f, 0xde, 0x76,
+    0xff, 0x3f, 0xfa, 0xb4, 0xf7, 0x29, 0xcf, 0x7a, 0xce, 0x78, 0xfe, 0xb7, 0xe7, 0x93, 0x77, 0x56, 0x43, 0x7f, 0xff, 0xf3,
+    0xde, 0xa7, 0xd4, 0x83, 0xe7, 0xed, 0xfb, 0xa1, 0x36, 0x00, 0xf9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x76, 0xbe, 0xa9, 0x7f, 0x66, 0xc5, 0xf7, 0xc8, 0xa8, 0xcf, 0xb9, 0xcf, 0x04, 0x62, 0x23, 0x75, 0xce,
+    0xac, 0xf8, 0x9e, 0x1f, 0x9d, 0x6f, 0x5b, 0xc2, 0xfb, 0xc6, 0x8a, 0xcb, 0x36, 0xec, 0x49, 0x55, 0x7f, 0x48, 0x15, 0xa4,
+    0x77, 0xcc, 0xcd, 0x01, 0xf2, 0xff, 0x94, 0x0a, 0x69, 0x20, 0xff, 0xf2, 0x0f, 0xf2, 0x2f, 0xff, 0x20, 0xff, 0xf2, 0x0f,
+    0x6f, 0xbf, 0xff, 0x7f, 0xa7, 0x99, 0x6b, 0xf6, 0xd5, 0x29, 0x06, 0xcf, 0xfe, 0x63, 0x47, 0x87, 0xea, 0x65, 0x6d, 0x78,
+    0xdc, 0xf0, 0x8c, 0x10, 0xce, 0xca, 0xff, 0x35, 0xcf, 0xf8, 0x57, 0xf3, 0xaf, 0xff, 0x0f, 0x6f, 0xba, 0x1a, 0x89, 0xbc,
+    0xff, 0x27, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0xe9, 0xaf, 0xff, 0x94, 0x03, 0x88, 0x7f, 0xe0, 0xeb, 0xe2, 0xff,
-    0x0f };
+    0x70, 0x95, 0x7f, 0xff, 0xd3, 0x0e, 0x20, 0xff, 0xc0, 0xc7, 0xe5, 0xff, 0x6f };
 
 // Font glyphs rectangles data (on atlas)
 static const Rectangle sunnyFontRecs[189] = {
@@ -291,93 +289,93 @@
     { 429, 52, 7 , 6 },
     { 444, 52, 6 , 3 },
     { 458, 52, 7 , 10 },
-    { 473, 52, 9 , 10 },
-    { 490, 52, 4 , 4 },
-    { 4, 76, 6 , 7 },
-    { 18, 76, 4 , 5 },
-    { 30, 76, 4 , 5 },
-    { 42, 76, 7 , 11 },
-    { 57, 76, 6 , 9 },
-    { 71, 76, 7 , 12 },
-    { 86, 76, 2 , 2 },
-    { 96, 76, 6 , 10 },
-    { 110, 76, 3 , 5 },
-    { 121, 76, 4 , 5 },
-    { 133, 76, 7 , 6 },
-    { 148, 76, 9 , 10 },
-    { 165, 76, 8 , 7 },
+    { 473, 52, 0 , 0 },
+    { 481, 52, 4 , 4 },
+    { 493, 52, 6 , 7 },
+    { 4, 76, 4 , 5 },
+    { 16, 76, 4 , 5 },
+    { 28, 76, 7 , 11 },
+    { 43, 76, 6 , 9 },
+    { 57, 76, 7 , 12 },
+    { 72, 76, 2 , 2 },
+    { 82, 76, 6 , 10 },
+    { 96, 76, 3 , 5 },
+    { 107, 76, 4 , 5 },
+    { 119, 76, 7 , 6 },
+    { 134, 76, 9 , 10 },
+    { 151, 76, 8 , 7 },
+    { 167, 76, 6 , 11 },
     { 181, 76, 6 , 11 },
-    { 195, 76, 6 , 11 },
-    { 209, 76, 7 , 11 },
-    { 224, 76, 7 , 11 },
-    { 239, 76, 7 , 11 },
-    { 254, 76, 7 , 11 },
-    { 269, 76, 7 , 11 },
-    { 284, 76, 7 , 11 },
-    { 299, 76, 9 , 10 },
-    { 316, 76, 7 , 12 },
-    { 331, 76, 7 , 11 },
-    { 346, 76, 7 , 11 },
-    { 361, 76, 7 , 11 },
-    { 376, 76, 7 , 11 },
-    { 391, 76, 3 , 11 },
-    { 402, 76, 3 , 11 },
-    { 413, 76, 5 , 11 },
-    { 426, 76, 5 , 11 },
-    { 439, 76, 8 , 10 },
-    { 455, 76, 7 , 11 },
-    { 470, 76, 7 , 11 },
-    { 485, 76, 7 , 11 },
+    { 195, 76, 7 , 11 },
+    { 210, 76, 7 , 11 },
+    { 225, 76, 7 , 11 },
+    { 240, 76, 7 , 11 },
+    { 255, 76, 7 , 11 },
+    { 270, 76, 7 , 11 },
+    { 285, 76, 9 , 10 },
+    { 302, 76, 7 , 12 },
+    { 317, 76, 7 , 11 },
+    { 332, 76, 7 , 11 },
+    { 347, 76, 7 , 11 },
+    { 362, 76, 7 , 11 },
+    { 377, 76, 3 , 11 },
+    { 388, 76, 3 , 11 },
+    { 399, 76, 5 , 11 },
+    { 412, 76, 5 , 11 },
+    { 425, 76, 8 , 10 },
+    { 441, 76, 7 , 11 },
+    { 456, 76, 7 , 11 },
+    { 471, 76, 7 , 11 },
+    { 486, 76, 7 , 11 },
     { 4, 100, 7 , 11 },
     { 19, 100, 7 , 11 },
-    { 34, 100, 7 , 11 },
-    { 49, 100, 7 , 7 },
-    { 64, 100, 7 , 13 },
+    { 34, 100, 7 , 7 },
+    { 49, 100, 7 , 13 },
+    { 64, 100, 7 , 11 },
     { 79, 100, 7 , 11 },
     { 94, 100, 7 , 11 },
     { 109, 100, 7 , 11 },
-    { 124, 100, 7 , 11 },
-    { 139, 100, 6 , 11 },
+    { 124, 100, 6 , 11 },
+    { 138, 100, 7 , 10 },
     { 153, 100, 7 , 10 },
-    { 168, 100, 7 , 10 },
-    { 183, 100, 6 , 10 },
-    { 197, 100, 6 , 10 },
-    { 211, 100, 6 , 10 },
-    { 225, 100, 6 , 10 },
-    { 239, 100, 6 , 10 },
-    { 253, 100, 6 , 11 },
-    { 267, 100, 8 , 7 },
-    { 283, 100, 6 , 9 },
-    { 297, 100, 6 , 10 },
-    { 311, 100, 6 , 10 },
-    { 325, 100, 6 , 10 },
-    { 339, 100, 6 , 10 },
-    { 353, 100, 3 , 10 },
-    { 364, 100, 3 , 10 },
-    { 375, 100, 5 , 10 },
-    { 388, 100, 5 , 10 },
-    { 401, 100, 6 , 10 },
-    { 415, 100, 6 , 10 },
-    { 429, 100, 6 , 10 },
-    { 443, 100, 6 , 10 },
-    { 457, 100, 6 , 10 },
-    { 471, 100, 6 , 10 },
-    { 485, 100, 6 , 10 },
-    { 4, 124, 6 , 7 },
-    { 18, 124, 7 , 11 },
+    { 168, 100, 6 , 10 },
+    { 182, 100, 6 , 10 },
+    { 196, 100, 6 , 10 },
+    { 210, 100, 6 , 10 },
+    { 224, 100, 6 , 10 },
+    { 238, 100, 6 , 11 },
+    { 252, 100, 8 , 7 },
+    { 268, 100, 6 , 9 },
+    { 282, 100, 6 , 10 },
+    { 296, 100, 6 , 10 },
+    { 310, 100, 6 , 10 },
+    { 324, 100, 6 , 10 },
+    { 338, 100, 3 , 10 },
+    { 349, 100, 3 , 10 },
+    { 360, 100, 5 , 10 },
+    { 373, 100, 5 , 10 },
+    { 386, 100, 6 , 10 },
+    { 400, 100, 6 , 10 },
+    { 414, 100, 6 , 10 },
+    { 428, 100, 6 , 10 },
+    { 442, 100, 6 , 10 },
+    { 456, 100, 6 , 10 },
+    { 470, 100, 6 , 10 },
+    { 484, 100, 6 , 7 },
+    { 4, 124, 7 , 11 },
+    { 19, 124, 6 , 10 },
     { 33, 124, 6 , 10 },
     { 47, 124, 6 , 10 },
     { 61, 124, 6 , 10 },
-    { 75, 124, 6 , 10 },
+    { 75, 124, 6 , 12 },
     { 89, 124, 6 , 12 },
     { 103, 124, 6 , 12 },
-    { 117, 124, 6 , 12 },
 };
 
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo sunnyFontGlyphs[189] = {
-    { 32, 0, 12, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 0, 2, 3, { 0 }},
     { 34, 0, 2, 6, { 0 }},
     { 35, 0, 2, 8, { 0 }},
@@ -485,7 +483,7 @@
     { 171, 0, 6, 8, { 0 }},
     { 172, 0, 7, 7, { 0 }},
     { 174, 0, 2, 8, { 0 }},
-    { 175, 1, 3, 14, { 0 }},
+    { 175, 0, 0, 0, { 0 }},
     { 176, 0, 2, 5, { 0 }},
     { 177, 0, 4, 7, { 0 }},
     { 178, 0, 2, 5, { 0 }},
diff --git a/raygui/styles/terminal/style_terminal.h b/raygui/styles/terminal/style_terminal.h
--- a/raygui/styles/terminal/style_terminal.h
+++ b/raygui/styles/terminal/style_terminal.h
@@ -7,7 +7,7 @@
 // more info and bugs-report:  github.com/raysan5/raygui                        //
 // feedback and support:       ray[at]raylibtech.com                            //
 //                                                                              //
-// Copyright (c) 2020-2024 raylib technologies (@raylibtech)                    //
+// Copyright (c) 2020-2025 raylib technologies (@raylibtech)                    //
 //                                                                              //
 //////////////////////////////////////////////////////////////////////////////////
 
@@ -15,23 +15,23 @@
 
 // Custom style name: Terminal
 static const GuiStyleProp terminalStyleProps[TERMINAL_STYLE_PROPS_COUNT] = {
-    { 0, 0, 0x1c8d00ff },    // DEFAULT_BORDER_COLOR_NORMAL 
-    { 0, 1, 0x161313ff },    // DEFAULT_BASE_COLOR_NORMAL 
-    { 0, 2, 0x38f620ff },    // DEFAULT_TEXT_COLOR_NORMAL 
-    { 0, 3, 0xc3fbc6ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
-    { 0, 4, 0x43bf2eff },    // DEFAULT_BASE_COLOR_FOCUSED 
-    { 0, 5, 0xdcfadcff },    // DEFAULT_TEXT_COLOR_FOCUSED 
-    { 0, 6, 0x1f5b19ff },    // DEFAULT_BORDER_COLOR_PRESSED 
-    { 0, 7, 0x43ff28ff },    // DEFAULT_BASE_COLOR_PRESSED 
-    { 0, 8, 0x1e6f15ff },    // DEFAULT_TEXT_COLOR_PRESSED 
-    { 0, 9, 0x223b22ff },    // DEFAULT_BORDER_COLOR_DISABLED 
-    { 0, 10, 0x182c18ff },    // DEFAULT_BASE_COLOR_DISABLED 
-    { 0, 11, 0x244125ff },    // DEFAULT_TEXT_COLOR_DISABLED 
-    { 0, 16, 0x00000010 },    // DEFAULT_TEXT_SIZE 
-    { 0, 17, 0x00000000 },    // DEFAULT_TEXT_SPACING 
-    { 0, 18, 0xe6fce3ff },    // DEFAULT_LINE_COLOR 
-    { 0, 19, 0x0c1505ff },    // DEFAULT_BACKGROUND_COLOR 
-    { 0, 20, 0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
+    { 0, 0, (int)0x1c8d00ff },    // DEFAULT_BORDER_COLOR_NORMAL 
+    { 0, 1, (int)0x161313ff },    // DEFAULT_BASE_COLOR_NORMAL 
+    { 0, 2, (int)0x38f620ff },    // DEFAULT_TEXT_COLOR_NORMAL 
+    { 0, 3, (int)0xc3fbc6ff },    // DEFAULT_BORDER_COLOR_FOCUSED 
+    { 0, 4, (int)0x43bf2eff },    // DEFAULT_BASE_COLOR_FOCUSED 
+    { 0, 5, (int)0xdcfadcff },    // DEFAULT_TEXT_COLOR_FOCUSED 
+    { 0, 6, (int)0x1f5b19ff },    // DEFAULT_BORDER_COLOR_PRESSED 
+    { 0, 7, (int)0x43ff28ff },    // DEFAULT_BASE_COLOR_PRESSED 
+    { 0, 8, (int)0x1e6f15ff },    // DEFAULT_TEXT_COLOR_PRESSED 
+    { 0, 9, (int)0x223b22ff },    // DEFAULT_BORDER_COLOR_DISABLED 
+    { 0, 10, (int)0x182c18ff },    // DEFAULT_BASE_COLOR_DISABLED 
+    { 0, 11, (int)0x244125ff },    // DEFAULT_TEXT_COLOR_DISABLED 
+    { 0, 16, (int)0x00000010 },    // DEFAULT_TEXT_SIZE 
+    { 0, 17, (int)0x00000000 },    // DEFAULT_TEXT_SPACING 
+    { 0, 18, (int)0xe6fce3ff },    // DEFAULT_LINE_COLOR 
+    { 0, 19, (int)0x0c1505ff },    // DEFAULT_BACKGROUND_COLOR 
+    { 0, 20, (int)0x00000018 },    // DEFAULT_TEXT_LINE_SPACING 
 };
 
 // WARNING: This style uses a custom font: "Mecha.ttf" (size: 16, spacing: 0)
@@ -330,7 +330,7 @@
 // Font glyphs info data
 // NOTE: No glyphs.image data provided
 static const GlyphInfo terminalFontGlyphs[189] = {
-    { 32, 0, 14, 4, { 0 }},
+    { 32, 0, 0, 4, { 0 }},
     { 33, 1, 3, 3, { 0 }},
     { 34, 1, 3, 5, { 0 }},
     { 35, 1, 3, 8, { 0 }},
@@ -430,29 +430,29 @@
     { 163, 1, 3, 7, { 0 }},
     { 8364, 1, 3, 7, { 0 }},
     { 165, 1, 3, 7, { 0 }},
-    { 352, 0, 14, 4, { 0 }},
-    { 167, 0, 14, 4, { 0 }},
-    { 353, 0, 14, 4, { 0 }},
+    { 352, 0, 0, 0, { 0 }},
+    { 167, 0, 0, 0, { 0 }},
+    { 353, 0, 0, 0, { 0 }},
     { 169, 1, 3, 9, { 0 }},
-    { 170, 0, 14, 4, { 0 }},
-    { 171, 0, 14, 4, { 0 }},
+    { 170, 0, 0, 0, { 0 }},
+    { 171, 0, 0, 0, { 0 }},
     { 172, 1, 8, 7, { 0 }},
     { 174, 1, 3, 9, { 0 }},
     { 175, 1, 1, 7, { 0 }},
     { 176, 1, 0, 5, { 0 }},
     { 177, 1, 7, 7, { 0 }},
-    { 178, 0, 14, 4, { 0 }},
-    { 179, 0, 14, 4, { 0 }},
-    { 381, 0, 14, 4, { 0 }},
+    { 178, 0, 0, 0, { 0 }},
+    { 179, 0, 0, 0, { 0 }},
+    { 381, 0, 0, 0, { 0 }},
     { 181, 1, 6, 7, { 0 }},
     { 182, 1, 3, 9, { 0 }},
     { 183, 1, 8, 3, { 0 }},
-    { 382, 0, 14, 4, { 0 }},
-    { 185, 0, 14, 4, { 0 }},
-    { 186, 0, 14, 4, { 0 }},
-    { 187, 0, 14, 4, { 0 }},
-    { 338, 0, 14, 4, { 0 }},
-    { 339, 0, 14, 4, { 0 }},
+    { 382, 0, 0, 0, { 0 }},
+    { 185, 0, 0, 0, { 0 }},
+    { 186, 0, 0, 0, { 0 }},
+    { 187, 0, 0, 0, { 0 }},
+    { 338, 0, 0, 0, { 0 }},
+    { 339, 0, 0, 0, { 0 }},
     { 376, 1, 1, 7, { 0 }},
     { 191, 1, 3, 7, { 0 }},
     { 192, 1, 0, 7, { 0 }},
diff --git a/raylib/examples/audio/audio_mixed_processor.c b/raylib/examples/audio/audio_mixed_processor.c
--- a/raylib/examples/audio/audio_mixed_processor.c
+++ b/raylib/examples/audio/audio_mixed_processor.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Mixed audio processing
+*   raylib [audio] example - mixed processor
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
 *   Example contributed by hkc (@hatkidchan) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 hkc (@hatkidchan)
+*   Copyright (c) 2023-2025 hkc (@hatkidchan)
 *
 ********************************************************************************************/
 #include "raylib.h"
@@ -28,13 +30,13 @@
 
     for (unsigned int frame = 0; frame < frames; frame++)
     {
-        float *left = &samples[frame * 2 + 0], *right = &samples[frame * 2 + 1];
+        float *left = &samples[frame*2 + 0], *right = &samples[frame*2 + 1];
 
-        *left = powf(fabsf(*left), exponent) * ( (*left < 0.0f)? -1.0f : 1.0f );
-        *right = powf(fabsf(*right), exponent) * ( (*right < 0.0f)? -1.0f : 1.0f );
+        *left = powf(fabsf(*left), exponent)*( (*left < 0.0f)? -1.0f : 1.0f );
+        *right = powf(fabsf(*right), exponent)*( (*right < 0.0f)? -1.0f : 1.0f );
 
-        average += fabsf(*left) / frames;   // accumulating average volume
-        average += fabsf(*right) / frames;
+        average += fabsf(*left)/frames;   // accumulating average volume
+        average += fabsf(*right)/frames;
     }
 
     // Moving history to the left
@@ -53,7 +55,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - processing mixed output");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - mixed processor");
 
     InitAudioDevice();              // Initialize audio device
 
@@ -97,7 +99,7 @@
             DrawRectangle(199, 199, 402, 34, LIGHTGRAY);
             for (int i = 0; i < 400; i++)
             {
-                DrawLine(201 + i, 232 - (int)(averageVolume[i] * 32), 201 + i, 232, MAROON);
+                DrawLine(201 + i, 232 - (int)(averageVolume[i]*32), 201 + i, 232, MAROON);
             }
             DrawRectangleLines(199, 199, 402, 34, GRAY);
 
diff --git a/raylib/examples/audio/audio_module_playing.c b/raylib/examples/audio/audio_module_playing.c
--- a/raylib/examples/audio/audio_module_playing.c
+++ b/raylib/examples/audio/audio_module_playing.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Module playing (streaming)
+*   raylib [audio] example - module playing
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.5, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -35,7 +37,7 @@
 
     SetConfigFlags(FLAG_MSAA_4X_HINT);  // NOTE: Try to enable MSAA 4X
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing (streaming)");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - module playing");
 
     InitAudioDevice();                  // Initialize audio device
 
diff --git a/raylib/examples/audio/audio_music_stream.c b/raylib/examples/audio/audio_music_stream.c
--- a/raylib/examples/audio/audio_music_stream.c
+++ b/raylib/examples/audio/audio_music_stream.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Music playing (streaming)
+*   raylib [audio] example - music stream
 *
-*   Example originally created with raylib 1.3, last time updated with raylib 4.0
+*   Example complexity rating: [★☆☆☆] 1/4
 *
+*   Example originally created with raylib 1.3, last time updated with raylib 4.2
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - music playing (streaming)");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - music stream");
 
     InitAudioDevice();              // Initialize audio device
 
@@ -34,6 +36,12 @@
     float timePlayed = 0.0f;        // Time played normalized [0.0f..1.0f]
     bool pause = false;             // Music playing paused
 
+    float pan = 0.0f;               // Default audio pan center [-1.0f..1.0f]
+    SetMusicPan(music, pan);
+
+    float volume = 0.8f;            // Default audio volume [0.0f..1.0f]
+    SetMusicVolume(music, volume);
+
     SetTargetFPS(30);               // Set our game to run at 30 frames-per-second
     //--------------------------------------------------------------------------------------
 
@@ -43,7 +51,7 @@
         // Update
         //----------------------------------------------------------------------------------
         UpdateMusicStream(music);   // Update music buffer with new stream data
-        
+
         // Restart music playing (stop and play)
         if (IsKeyPressed(KEY_SPACE))
         {
@@ -60,6 +68,34 @@
             else ResumeMusicStream(music);
         }
 
+        // Set audio pan
+        if (IsKeyDown(KEY_LEFT))
+        {
+            pan -= 0.05f;
+            if (pan < -1.0f) pan = -1.0f;
+            SetMusicPan(music, pan);
+        }
+        else if (IsKeyDown(KEY_RIGHT))
+        {
+            pan += 0.05f;
+            if (pan > 1.0f) pan = 1.0f;
+            SetMusicPan(music, pan);
+        }
+
+        // Set audio volume
+        if (IsKeyDown(KEY_DOWN))
+        {
+            volume -= 0.05f;
+            if (volume < 0.0f) volume = 0.0f;
+            SetMusicVolume(music, volume);
+        }
+        else if (IsKeyDown(KEY_UP))
+        {
+            volume += 0.05f;
+            if (volume > 1.0f) volume = 1.0f;
+            SetMusicVolume(music, volume);
+        }
+
         // Get normalized time played for current music stream
         timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music);
 
@@ -74,12 +110,22 @@
 
             DrawText("MUSIC SHOULD BE PLAYING!", 255, 150, 20, LIGHTGRAY);
 
+            DrawText("LEFT-RIGHT for PAN CONTROL", 320, 74, 10, DARKBLUE);
+            DrawRectangle(300, 100, 200, 12, LIGHTGRAY);
+            DrawRectangleLines(300, 100, 200, 12, GRAY);
+            DrawRectangle((int)(300 + (pan + 1.0f)/2.0f*200 - 5), 92, 10, 28, DARKGRAY);
+
             DrawRectangle(200, 200, 400, 12, LIGHTGRAY);
             DrawRectangle(200, 200, (int)(timePlayed*400.0f), 12, MAROON);
             DrawRectangleLines(200, 200, 400, 12, GRAY);
 
             DrawText("PRESS SPACE TO RESTART MUSIC", 215, 250, 20, LIGHTGRAY);
             DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 280, 20, LIGHTGRAY);
+
+            DrawText("UP-DOWN for VOLUME CONTROL", 320, 334, 10, DARKGREEN);
+            DrawRectangle(300, 360, 200, 12, LIGHTGRAY);
+            DrawRectangleLines(300, 360, 200, 12, GRAY);
+            DrawRectangle((int)(300 + volume*200 - 5), 352, 10, 28, DARKGRAY);
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/audio/audio_raw_stream.c b/raylib/examples/audio/audio_raw_stream.c
--- a/raylib/examples/audio/audio_raw_stream.c
+++ b/raylib/examples/audio/audio_raw_stream.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Raw audio streaming
+*   raylib [audio] example - raw stream
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 1.6, last time updated with raylib 4.2
 *
 *   Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox)
 *
 ********************************************************************************************/
 
@@ -60,7 +62,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw audio streaming");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - raw stream");
 
     InitAudioDevice();              // Initialize audio device
 
@@ -106,8 +108,6 @@
     {
         // Update
         //----------------------------------------------------------------------------------
-
-        // Sample mouse input.
         mousePosition = GetMousePosition();
 
         if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
@@ -115,7 +115,7 @@
             float fp = (float)(mousePosition.y);
             frequency = 40.0f + (float)(fp);
 
-            float pan = (float)(mousePosition.x) / (float)screenWidth;
+            float pan = (float)(mousePosition.x)/(float)screenWidth;
             SetAudioStreamPan(stream, pan);
         }
 
@@ -123,7 +123,7 @@
         // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc.
         if (frequency != oldFrequency)
         {
-            // Compute wavelength. Limit size in both directions.
+            // Compute wavelength. Limit size in both directions
             //int oldWavelength = waveLength;
             waveLength = (int)(22050/frequency);
             if (waveLength > MAX_SAMPLES/2) waveLength = MAX_SAMPLES/2;
@@ -141,7 +141,7 @@
             }
 
             // Scale read cursor's position to minimize transition artifacts
-            //readCursor = (int)(readCursor * ((float)waveLength / (float)oldWavelength));
+            //readCursor = (int)(readCursor*((float)waveLength/(float)oldWavelength));
             oldFrequency = frequency;
         }
 
@@ -166,7 +166,7 @@
                 memcpy(writeBuf + writeCursor, data + readCursor, writeLength*sizeof(short));
 
                 // Update cursors and loop audio
-                readCursor = (readCursor + writeLength) % waveLength;
+                readCursor = (readCursor + writeLength)%waveLength;
 
                 writeCursor += writeLength;
             }
diff --git a/raylib/examples/audio/audio_sound_loading.c b/raylib/examples/audio/audio_sound_loading.c
--- a/raylib/examples/audio/audio_sound_loading.c
+++ b/raylib/examples/audio/audio_sound_loading.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Sound loading and playing
+*   raylib [audio] example - sound loading
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.1, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading and playing");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound loading");
 
     InitAudioDevice();      // Initialize audio device
 
diff --git a/raylib/examples/audio/audio_sound_multi.c b/raylib/examples/audio/audio_sound_multi.c
--- a/raylib/examples/audio/audio_sound_multi.c
+++ b/raylib/examples/audio/audio_sound_multi.c
@@ -1,13 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Playing sound multiple times
+*   raylib [audio] example - sound multi
 *
-*   Example originally created with raylib 4.6
+*   Example complexity rating: [★★☆☆] 2/4
 *
+*   Example originally created with raylib 5.0, last time updated with raylib 5.0
+*
+*   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Jeffery Myers (@JeffM2501)
+*   Copyright (c) 2023-2025 Jeffery Myers (@JeffM2501)
 *
 ********************************************************************************************/
 
@@ -27,19 +31,19 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [audio] example - playing sound multiple times");
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound multi");
 
     InitAudioDevice();      // Initialize audio device
 
-    // load the sound list
-    soundArray[0] = LoadSound("resources/sound.wav");         // Load WAV audio file into the first slot as the 'source' sound
-                                                              // this sound owns the sample data
-    for (int i = 1; i < MAX_SOUNDS; i++)
-    {
-        soundArray[i] = LoadSoundAlias(soundArray[0]);        // Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played
-    }
-    currentSound = 0;                                         // set the sound list to the start
+    // Load audio file into the first slot as the 'source' sound,
+    // this sound owns the sample data
+    soundArray[0] = LoadSound("resources/sound.wav");
 
+    // Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played
+    for (int i = 1; i < MAX_SOUNDS; i++) soundArray[i] = LoadSoundAlias(soundArray[0]);
+
+    currentSound = 0;               // Set the sound list to the start
+
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
@@ -50,14 +54,15 @@
         //----------------------------------------------------------------------------------
         if (IsKeyPressed(KEY_SPACE))
         {
-            PlaySound(soundArray[currentSound]);            // play the next open sound slot
-            currentSound++;                                 // increment the sound slot
-            if (currentSound >= MAX_SOUNDS)                 // if the sound slot is out of bounds, go back to 0
-                currentSound = 0;
+            PlaySound(soundArray[currentSound]);    // Play the next open sound slot
+            currentSound++;                         // Increment the sound slot
 
-            // Note: a better way would be to look at the list for the first sound that is not playing and use that slot
-        }
+            // If the sound slot is out of bounds, go back to 0
+            if (currentSound >= MAX_SOUNDS) currentSound = 0;
 
+            // NOTE: Another approach would be to look at the list for the first sound
+            // that is not playing and use that slot
+        }
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -74,9 +79,8 @@
 
     // De-Initialization
     //--------------------------------------------------------------------------------------
-    for (int i = 1; i < MAX_SOUNDS; i++)
-        UnloadSoundAlias(soundArray[i]);     // Unload sound aliases
-    UnloadSound(soundArray[0]);              // Unload source sound data
+    for (int i = 1; i < MAX_SOUNDS; i++) UnloadSoundAlias(soundArray[i]); // Unload sound aliases
+    UnloadSound(soundArray[0]); // Unload source sound data
 
     CloseAudioDevice();     // Close audio device
 
diff --git a/raylib/examples/audio/audio_sound_positioning.c b/raylib/examples/audio/audio_sound_positioning.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/audio/audio_sound_positioning.c
@@ -0,0 +1,131 @@
+/*******************************************************************************************
+*
+*   raylib [audio] example - sound positioning
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Le Juez Victor (@Bigfoot71) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Le Juez Victor (@Bigfoot71)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, float maxDist);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - sound positioning");
+
+    InitAudioDevice();
+
+    Sound sound = LoadSound("resources/coin.wav");
+
+    Camera camera = {
+        .position = (Vector3) { 0, 5, 5 },
+        .target = (Vector3) { 0, 0, 0 },
+        .up = (Vector3) { 0, 1, 0 },
+        .fovy = 60,
+        .projection = CAMERA_PERSPECTIVE
+    };
+
+    DisableCursor();
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FREE);
+
+        float th = (float)GetTime();
+
+        Vector3 spherePos = {
+            .x = 5.0f*cosf(th),
+            .y = 0.0f,
+            .z = 5.0f*sinf(th)
+        };
+
+        SetSoundPosition(camera, sound, spherePos, 20.0f);
+
+        if (!IsSoundPlaying(sound)) PlaySound(sound);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                DrawGrid(10, 2);
+                DrawSphere(spherePos, 0.5f, RED);
+            EndMode3D();
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadSound(sound);
+    CloseAudioDevice();     // Close audio device
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Set sound 3d position
+static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, float maxDist)
+{
+    // Calculate direction vector and distance between listener and sound source
+    Vector3 direction = Vector3Subtract(position, listener.position);
+    float distance = Vector3Length(direction);
+
+    // Apply logarithmic distance attenuation and clamp between 0-1
+    float attenuation = 1.0f/(1.0f + (distance/maxDist));
+    attenuation = Clamp(attenuation, 0.0f, 1.0f);
+
+    // Calculate normalized vectors for spatial positioning
+    Vector3 normalizedDirection = Vector3Normalize(direction);
+    Vector3 forward = Vector3Normalize(Vector3Subtract(listener.target, listener.position));
+    Vector3 right = Vector3Normalize(Vector3CrossProduct(listener.up, forward));
+
+    // Reduce volume for sounds behind the listener
+    float dotProduct = Vector3DotProduct(forward, normalizedDirection);
+    if (dotProduct < 0.0f) attenuation *= (1.0f + dotProduct*0.5f);
+
+    // Set stereo panning based on sound position relative to listener
+    float pan = 0.5f + 0.5f*Vector3DotProduct(normalizedDirection, right);
+
+    // Apply final sound properties
+    SetSoundVolume(sound, attenuation);
+    SetSoundPan(sound, pan);
+}
diff --git a/raylib/examples/audio/audio_spectrum_visualizer.c b/raylib/examples/audio/audio_spectrum_visualizer.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/audio/audio_spectrum_visualizer.c
@@ -0,0 +1,285 @@
+/*******************************************************************************************
+*
+*   raylib [audio] example - spectrum visualizer
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 6.0, last time updated with raylib 5.6-dev
+*
+*   Inspired by Inigo Quilez's https://www.shadertoy.com/
+*   Resources/specification: https://gist.github.com/soulthreads/2efe50da4be1fb5f7ab60ff14ca434b8
+*
+*   Example created by created by IANN (@meisei4) reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 IANN (@meisei4)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#define MONO                           1
+#define SAMPLE_RATE                    44100
+#define SAMPLE_RATE_F                  44100.0f
+#define FFT_WINDOW_SIZE                1024
+#define BUFFER_SIZE                    512
+#define PER_SAMPLE_BIT_DEPTH           16
+#define AUDIO_STREAM_RING_BUFFER_SIZE  (FFT_WINDOW_SIZE*2)
+#define EFFECTIVE_SAMPLE_RATE          (SAMPLE_RATE_F*0.5f)
+#define WINDOW_TIME                    ((double)FFT_WINDOW_SIZE/(double)EFFECTIVE_SAMPLE_RATE)
+#define FFT_HISTORICAL_SMOOTHING_DUR   2.0f
+#define MIN_DECIBELS                   (-100.0f) // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/minDecibels
+#define MAX_DECIBELS                   (-30.0f)  // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/maxDecibels
+#define INVERSE_DECIBEL_RANGE          (1.0f/(MAX_DECIBELS - MIN_DECIBELS))
+#define DB_TO_LINEAR_SCALE             (20.0f/2.302585092994046f)
+#define SMOOTHING_TIME_CONSTANT        0.8f // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/smoothingTimeConstant
+#define TEXTURE_HEIGHT                 1
+#define FFT_ROW                        0
+#define UNUSED_CHANNEL                 0.0f
+
+typedef struct FFTComplex { float real, imaginary; } FFTComplex;
+
+typedef struct FFTData {
+    FFTComplex *spectrum;
+    FFTComplex *workBuffer;
+    float *prevMagnitudes;
+    float (*fftHistory)[BUFFER_SIZE];
+    int fftHistoryLen;
+    int historyPos;
+    double lastFftTime;
+    float tapbackPos;
+} FFTData;
+
+static void CaptureFrame(FFTData *fftData, const float *audioSamples);
+static void RenderFrame(const FFTData *fftData, Image *fftImage);
+static void CooleyTukeyFFTSlow(FFTComplex *spectrum, int n);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //-----------------------------------------------------------------------------------     ---
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [audio] example - spectrum visualizer");
+
+    Image fftImage = GenImageColor(BUFFER_SIZE, TEXTURE_HEIGHT, WHITE);
+    Texture2D fftTexture = LoadTextureFromImage(fftImage);
+    RenderTexture2D bufferA = LoadRenderTexture(screenWidth, screenHeight);
+    Vector2 iResolution = { (float)screenWidth, (float)screenHeight };
+
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/fft.fs", GLSL_VERSION));
+
+    int iResolutionLocation = GetShaderLocation(shader, "iResolution");
+    int iChannel0Location = GetShaderLocation(shader, "iChannel0");
+    SetShaderValue(shader, iResolutionLocation, &iResolution, SHADER_UNIFORM_VEC2);
+    SetShaderValueTexture(shader, iChannel0Location, fftTexture);
+
+    InitAudioDevice();
+    SetAudioStreamBufferSizeDefault(AUDIO_STREAM_RING_BUFFER_SIZE);
+
+    // WARNING: Memory out-of-bounds on PLATFORM_WEB
+    Wave wav = LoadWave("resources/country.mp3");
+    WaveFormat(&wav, SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO);
+
+    AudioStream audioStream = LoadAudioStream(SAMPLE_RATE, PER_SAMPLE_BIT_DEPTH, MONO);
+    PlayAudioStream(audioStream);
+
+    int fftHistoryLen = (int)ceilf(FFT_HISTORICAL_SMOOTHING_DUR/WINDOW_TIME) + 1;
+
+    FFTData fft = {
+        .spectrum = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
+        .workBuffer = RL_CALLOC(sizeof(FFTComplex), FFT_WINDOW_SIZE),
+        .prevMagnitudes = RL_CALLOC(BUFFER_SIZE, sizeof(float)),
+        .fftHistory = RL_CALLOC(fftHistoryLen, sizeof(float[BUFFER_SIZE])),
+        .fftHistoryLen = fftHistoryLen,
+        .historyPos = 0,
+        .lastFftTime = 0.0,
+        .tapbackPos = 0.01f
+    };
+
+    int wavCursor = 0;
+    const short *wavPCM16 = wav.data;
+
+    short chunkSamples[AUDIO_STREAM_RING_BUFFER_SIZE] = { 0 };
+    float audioSamples[FFT_WINDOW_SIZE] = { 0 };
+
+    SetTargetFPS(60);
+    //----------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        while (IsAudioStreamProcessed(audioStream))
+        {
+            for (int i = 0; i < AUDIO_STREAM_RING_BUFFER_SIZE; i++)
+            {
+                int left = (wav.channels == 2)? wavPCM16[wavCursor*2 + 0] : wavPCM16[wavCursor];
+                int right = (wav.channels == 2)? wavPCM16[wavCursor*2 + 1] : left;
+                chunkSamples[i] = (short)((left + right)/2);
+
+                if (++wavCursor >= wav.frameCount) wavCursor = 0;
+            }
+
+            UpdateAudioStream(audioStream, chunkSamples, AUDIO_STREAM_RING_BUFFER_SIZE);
+
+            for (int i = 0; i < FFT_WINDOW_SIZE; i++) audioSamples[i] = (chunkSamples[i*2] + chunkSamples[i*2 + 1])*0.5f/32767.0f;
+        }
+
+        CaptureFrame(&fft, audioSamples);
+        RenderFrame(&fft, &fftImage);
+        UpdateTexture(fftTexture, fftImage.data);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginShaderMode(shader);
+                SetShaderValueTexture(shader, iChannel0Location, fftTexture);
+                DrawTextureRec(bufferA.texture,
+                    (Rectangle){ 0, 0, (float)screenWidth, (float)-screenHeight },
+                    (Vector2){ 0, 0 }, WHITE);
+            EndShaderMode();
+
+        EndDrawing();
+        //------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);
+    UnloadRenderTexture(bufferA);
+    UnloadTexture(fftTexture);
+    UnloadImage(fftImage);
+    UnloadAudioStream(audioStream);
+    UnloadWave(wav);
+    CloseAudioDevice();
+
+    RL_FREE(fft.spectrum);
+    RL_FREE(fft.workBuffer);
+    RL_FREE(fft.prevMagnitudes);
+    RL_FREE(fft.fftHistory);
+
+    CloseWindow();        // Close window and OpenGL context
+    //----------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Cooley–Tukey FFT https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm#Data_reordering,_bit_reversal,_and_in-place_algorithms
+static void CooleyTukeyFFTSlow(FFTComplex *spectrum, int n)
+{
+    int j = 0;
+    for (int i = 1; i < n - 1; i++)
+    {
+        int bit = n >> 1;
+        while (j >= bit)
+        {
+            j -= bit;
+            bit >>= 1;
+        }
+        j += bit;
+        if (i < j)
+        {
+            FFTComplex temp = spectrum[i];
+            spectrum[i] = spectrum[j];
+            spectrum[j] = temp;
+        }
+    }
+
+    for (int len = 2; len <= n; len <<= 1)
+    {
+        float angle = -2.0f*PI/len;
+        FFTComplex twiddleUnit = { cosf(angle), sinf(angle) };
+        for (int i = 0; i < n; i += len)
+        {
+            FFTComplex twiddleCurrent = { 1.0f, 0.0f };
+            for (int j = 0; j < len/2; j++)
+            {
+                FFTComplex even = spectrum[i + j];
+                FFTComplex odd = spectrum[i + j + len/2];
+                FFTComplex twiddledOdd = {
+                    odd.real*twiddleCurrent.real - odd.imaginary*twiddleCurrent.imaginary,
+                    odd.real*twiddleCurrent.imaginary + odd.imaginary*twiddleCurrent.real
+                };
+
+                spectrum[i + j].real = even.real + twiddledOdd.real;
+                spectrum[i + j].imaginary = even.imaginary + twiddledOdd.imaginary;
+                spectrum[i + j + len/2].real = even.real - twiddledOdd.real;
+                spectrum[i + j + len/2].imaginary = even.imaginary - twiddledOdd.imaginary;
+
+                float twiddleRealNext = twiddleCurrent.real*twiddleUnit.real - twiddleCurrent.imaginary*twiddleUnit.imaginary;
+                twiddleCurrent.imaginary = twiddleCurrent.real*twiddleUnit.imaginary + twiddleCurrent.imaginary*twiddleUnit.real;
+                twiddleCurrent.real = twiddleRealNext;
+            }
+        }
+    }
+}
+
+static void CaptureFrame(FFTData *fftData, const float *audioSamples)
+{
+    for (int i = 0; i < FFT_WINDOW_SIZE; i++)
+    {
+        float x = (2.0f*PI*i)/(FFT_WINDOW_SIZE - 1.0f);
+        float blackmanWeight  = 0.42f - 0.5f*cosf(x) + 0.08f*cosf(2.0f*x); // https://en.wikipedia.org/wiki/Window_function#Blackman_window
+        fftData->workBuffer[i].real = audioSamples[i]*blackmanWeight;
+        fftData->workBuffer[i].imaginary = 0.0f;
+    }
+
+    CooleyTukeyFFTSlow(fftData->workBuffer, FFT_WINDOW_SIZE);
+    memcpy(fftData->spectrum, fftData->workBuffer, sizeof(FFTComplex)*FFT_WINDOW_SIZE);
+
+    float smoothedSpectrum[BUFFER_SIZE];
+
+    for (int bin = 0; bin < BUFFER_SIZE; bin++)
+    {
+        float re = fftData->workBuffer[bin].real;
+        float im = fftData->workBuffer[bin].imaginary;
+        float linearMagnitude = sqrtf(re*re + im*im)/FFT_WINDOW_SIZE;
+
+        float smoothedMagnitude = SMOOTHING_TIME_CONSTANT*fftData->prevMagnitudes[bin] + (1.0f - SMOOTHING_TIME_CONSTANT)*linearMagnitude;
+        fftData->prevMagnitudes[bin] = smoothedMagnitude;
+
+        float db = logf(fmaxf(smoothedMagnitude, 1e-40f))*DB_TO_LINEAR_SCALE;
+        float normalized = (db - MIN_DECIBELS)*INVERSE_DECIBEL_RANGE;
+        smoothedSpectrum[bin] = Clamp(normalized, 0.0f, 1.0f);
+    }
+
+    fftData->lastFftTime = GetTime();
+    memcpy(fftData->fftHistory[fftData->historyPos], smoothedSpectrum, sizeof(smoothedSpectrum));
+    fftData->historyPos = (fftData->historyPos + 1)%fftData->fftHistoryLen;
+}
+
+static void RenderFrame(const FFTData *fftData, Image *fftImage)
+{
+    double framesSinceTapback = floor(fftData->tapbackPos/WINDOW_TIME);
+    framesSinceTapback = Clamp(framesSinceTapback, 0.0, fftData->fftHistoryLen - 1);
+
+    int historyPosition = (fftData->historyPos - 1 - (int)framesSinceTapback)%fftData->fftHistoryLen;
+    if (historyPosition < 0) historyPosition += fftData->fftHistoryLen;
+
+    const float *amplitude = fftData->fftHistory[historyPosition];
+    for (int bin = 0; bin < BUFFER_SIZE; bin++) ImageDrawPixel(fftImage, bin, FFT_ROW, ColorFromNormalized((Vector4){ amplitude[bin], UNUSED_CHANNEL, UNUSED_CHANNEL, UNUSED_CHANNEL }));
+}
diff --git a/raylib/examples/audio/audio_stream_effects.c b/raylib/examples/audio/audio_stream_effects.c
--- a/raylib/examples/audio/audio_stream_effects.c
+++ b/raylib/examples/audio/audio_stream_effects.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [audio] example - Music stream processing effects
+*   raylib [audio] example - stream effects
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 5.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -15,7 +17,9 @@
 
 #include <stdlib.h> // Required for: NULL
 
-// Required delay effect variables
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
 static float *delayBuffer = NULL;
 static unsigned int delayBufferSize = 0;
 static unsigned int delayReadIndex = 2;
@@ -51,7 +55,7 @@
 
     float timePlayed = 0.0f;        // Time played normalized [0.0f..1.0f]
     bool pause = false;             // Music playing paused
-    
+
     bool enableEffectLPF = false;   // Enable effect low-pass-filter
     bool enableEffectDelay = false; // Enable effect delay (1 second)
 
@@ -96,7 +100,7 @@
             if (enableEffectDelay) AttachAudioStreamProcessor(music.stream, AudioProcessEffectDelay);
             else DetachAudioStreamProcessor(music.stream, AudioProcessEffectDelay);
         }
-        
+
         // Get normalized time played for current music stream
         timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music);
 
@@ -117,7 +121,7 @@
 
             DrawText("PRESS SPACE TO RESTART MUSIC", 215, 230, 20, LIGHTGRAY);
             DrawText("PRESS P TO PAUSE/RESUME MUSIC", 208, 260, 20, LIGHTGRAY);
-            
+
             DrawText(TextFormat("PRESS F TO TOGGLE LPF EFFECT: %s", enableEffectLPF? "ON" : "OFF"), 200, 320, 20, GRAY);
             DrawText(TextFormat("PRESS D TO TOGGLE DELAY EFFECT: %s", enableEffectDelay? "ON" : "OFF"), 180, 350, 20, GRAY);
 
@@ -146,8 +150,8 @@
 static void AudioProcessEffectLPF(void *buffer, unsigned int frames)
 {
     static float low[2] = { 0.0f, 0.0f };
-    static const float cutoff = 70.0f / 44100.0f; // 70 Hz lowpass filter
-    const float k = cutoff / (cutoff + 0.1591549431f); // RC filter formula
+    static const float cutoff = 70.0f/44100.0f; // 70 Hz lowpass filter
+    const float k = cutoff/(cutoff + 0.1591549431f); // RC filter formula
 
     // Converts the buffer data before using it
     float *bufferData = (float *)buffer;
@@ -156,8 +160,8 @@
         const float l = bufferData[i];
         const float r = bufferData[i + 1];
 
-        low[0] += k * (l - low[0]);
-        low[1] += k * (r - low[1]);
+        low[0] += k*(l - low[0]);
+        low[1] += k*(r - low[1]);
         bufferData[i] = low[0];
         bufferData[i + 1] = low[1];
     }
diff --git a/raylib/examples/core/core_2d_camera.c b/raylib/examples/core/core_2d_camera.c
--- a/raylib/examples/core/core_2d_camera.c
+++ b/raylib/examples/core/core_2d_camera.c
@@ -1,17 +1,20 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - 2D Camera system
+*   raylib [core] example - 2d camera
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.5, last time updated with raylib 3.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
+#include <math.h>
 
 #define MAX_BUILDINGS   100
 
@@ -42,7 +45,11 @@
 
         spacing += (int)buildings[i].width;
 
-        buildColors[i] = (Color){ GetRandomValue(200, 240), GetRandomValue(200, 240), GetRandomValue(200, 250), 255 };
+        buildColors[i] = (Color){
+            (unsigned char)GetRandomValue(200, 240),
+            (unsigned char)GetRandomValue(200, 240),
+            (unsigned char)GetRandomValue(200, 250),
+            255};
     }
 
     Camera2D camera = { 0 };
@@ -75,7 +82,8 @@
         else if (camera.rotation < -40) camera.rotation = -40;
 
         // Camera zoom controls
-        camera.zoom += ((float)GetMouseWheelMove()*0.05f);
+        // Uses log scaling to provide consistent zoom speed
+        camera.zoom = expf(logf(camera.zoom) + ((float)GetMouseWheelMove()*0.1f));
 
         if (camera.zoom > 3.0f) camera.zoom = 3.0f;
         else if (camera.zoom < 0.1f) camera.zoom = 0.1f;
@@ -117,8 +125,8 @@
             DrawRectangle( 10, 10, 250, 113, Fade(SKYBLUE, 0.5f));
             DrawRectangleLines( 10, 10, 250, 113, BLUE);
 
-            DrawText("Free 2d camera controls:", 20, 20, 10, BLACK);
-            DrawText("- Right/Left to move Offset", 40, 40, 10, DARKGRAY);
+            DrawText("Free 2D camera controls:", 20, 20, 10, BLACK);
+            DrawText("- Right/Left to move player", 40, 40, 10, DARKGRAY);
             DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, DARKGRAY);
             DrawText("- A / S to Rotate", 40, 80, 10, DARKGRAY);
             DrawText("- R to reset Zoom and Rotation", 40, 100, 10, DARKGRAY);
diff --git a/raylib/examples/core/core_2d_camera_mouse_zoom.c b/raylib/examples/core/core_2d_camera_mouse_zoom.c
--- a/raylib/examples/core/core_2d_camera_mouse_zoom.c
+++ b/raylib/examples/core/core_2d_camera_mouse_zoom.c
@@ -2,12 +2,16 @@
 *
 *   raylib [core] example - 2d camera mouse zoom
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
+*   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 Jeffery Myers (@JeffM2501)
+*   Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501)
 *
 ********************************************************************************************/
 
@@ -19,7 +23,7 @@
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main ()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
@@ -31,9 +35,9 @@
     Camera2D camera = { 0 };
     camera.zoom = 1.0f;
 
-    int zoomMode = 0;   // 0-Mouse Wheel, 1-Mouse Move
+    int zoomMode = 0;       // 0-Mouse Wheel, 1-Mouse Move
 
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    SetTargetFPS(60);       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
     // Main game loop
@@ -43,9 +47,9 @@
         //----------------------------------------------------------------------------------
         if (IsKeyPressed(KEY_ONE)) zoomMode = 0;
         else if (IsKeyPressed(KEY_TWO)) zoomMode = 1;
-        
+
         // Translate based on mouse right click
-        if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
+        if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
         {
             Vector2 delta = GetMouseDelta();
             delta = Vector2Scale(delta, -1.0f/camera.zoom);
@@ -64,20 +68,20 @@
                 // Set the offset to where the mouse is
                 camera.offset = GetMousePosition();
 
-                // Set the target to match, so that the camera maps the world space point 
+                // Set the target to match, so that the camera maps the world space point
                 // under the cursor to the screen space point under the cursor at any zoom
                 camera.target = mouseWorldPos;
 
                 // Zoom increment
-                float scaleFactor = 1.0f + (0.25f*fabsf(wheel));
-                if (wheel < 0) scaleFactor = 1.0f/scaleFactor;
-                camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f);
+                // Uses log scaling to provide consistent zoom speed
+                float scale = 0.2f*wheel;
+                camera.zoom = Clamp(expf(logf(camera.zoom)+scale), 0.125f, 64.0f);
             }
         }
         else
         {
-            // Zoom based on mouse left click
-            if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+            // Zoom based on mouse right click
+            if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
             {
                 // Get the world point that is under the mouse
                 Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);
@@ -85,17 +89,18 @@
                 // Set the offset to where the mouse is
                 camera.offset = GetMousePosition();
 
-                // Set the target to match, so that the camera maps the world space point 
+                // Set the target to match, so that the camera maps the world space point
                 // under the cursor to the screen space point under the cursor at any zoom
                 camera.target = mouseWorldPos;
             }
-            if (IsMouseButtonDown(MOUSE_BUTTON_LEFT))
+
+            if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
             {
                 // Zoom increment
+                // Uses log scaling to provide consistent zoom speed
                 float deltaX = GetMouseDelta().x;
-                float scaleFactor = 1.0f + (0.01f*fabsf(deltaX));
-                if (deltaX < 0) scaleFactor = 1.0f/scaleFactor;
-                camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f);
+                float scale = 0.005f*deltaX;
+                camera.zoom = Clamp(expf(logf(camera.zoom)+scale), 0.125f, 64.0f);
             }
         }
         //----------------------------------------------------------------------------------
@@ -106,8 +111,7 @@
             ClearBackground(RAYWHITE);
 
             BeginMode2D(camera);
-
-                // Draw the 3d grid, rotated 90 degrees and centered around 0,0 
+                // Draw the 3d grid, rotated 90 degrees and centered around 0,0
                 // just so we have something in the XY plane
                 rlPushMatrix();
                     rlTranslatef(0, 25*50, 0);
@@ -117,13 +121,18 @@
 
                 // Draw a reference circle
                 DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 50, MAROON);
-                
             EndMode2D();
 
+            // Draw mouse reference
+            //Vector2 mousePos = GetWorldToScreen2D(GetMousePosition(), camera)
+            DrawCircleV(GetMousePosition(), 4, DARKGRAY);
+            DrawTextEx(GetFontDefault(), TextFormat("[%i, %i]", GetMouseX(), GetMouseY()),
+                Vector2Add(GetMousePosition(), (Vector2){ -44, -24 }), 20, 2, BLACK);
+
             DrawText("[1][2] Select mouse zoom mode (Wheel or Move)", 20, 20, 20, DARKGRAY);
-            if (zoomMode == 0) DrawText("Mouse right button drag to move, mouse wheel to zoom", 20, 50, 20, DARKGRAY);
-            else DrawText("Mouse right button drag to move, mouse press and move to zoom", 20, 50, 20, DARKGRAY);
-        
+            if (zoomMode == 0) DrawText("Mouse left button drag to move, mouse wheel to zoom", 20, 50, 20, DARKGRAY);
+            else DrawText("Mouse left button drag to move, mouse press and move to zoom", 20, 50, 20, DARKGRAY);
+
         EndDrawing();
         //----------------------------------------------------------------------------------
     }
@@ -132,5 +141,6 @@
     //--------------------------------------------------------------------------------------
     CloseWindow();        // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
+
     return 0;
 }
diff --git a/raylib/examples/core/core_2d_camera_platformer.c b/raylib/examples/core/core_2d_camera_platformer.c
--- a/raylib/examples/core/core_2d_camera_platformer.c
+++ b/raylib/examples/core/core_2d_camera_platformer.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - 2D Camera platformer
+*   raylib [core] example - 2d camera platformer
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.0
 *
 *   Example contributed by arvyy (@arvyy) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 arvyy (@arvyy)
+*   Copyright (c) 2019-2025 arvyy (@arvyy)
 *
 ********************************************************************************************/
 
@@ -20,6 +22,9 @@
 #define PLAYER_JUMP_SPD 350.0f
 #define PLAYER_HOR_SPD 200.0f
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 typedef struct Player {
     Vector2 position;
     float speed;
@@ -33,7 +38,7 @@
 } EnvItem;
 
 //----------------------------------------------------------------------------------
-// Module functions declaration
+// Module Functions Declaration
 //----------------------------------------------------------------------------------
 void UpdatePlayer(Player *player, EnvItem *envItems, int envItemsLength, float delta);
 void UpdateCameraCenter(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height);
@@ -52,7 +57,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera platformer");
 
     Player player = { 0 };
     player.position = (Vector2){ 400, 280 };
@@ -135,7 +140,7 @@
 
                 Rectangle playerRect = { player.position.x - 20, player.position.y - 40, 40.0f, 40.0f };
                 DrawRectangleRec(playerRect, RED);
-                
+
                 DrawCircleV(player.position, 5.0f, GOLD);
 
             EndMode2D();
@@ -292,7 +297,7 @@
 
     Vector2 bboxWorldMin = GetScreenToWorld2D((Vector2){ (1 - bbox.x)*0.5f*width, (1 - bbox.y)*0.5f*height }, *camera);
     Vector2 bboxWorldMax = GetScreenToWorld2D((Vector2){ (1 + bbox.x)*0.5f*width, (1 + bbox.y)*0.5f*height }, *camera);
-    camera->offset = (Vector2){ (1 - bbox.x)*0.5f * width, (1 - bbox.y)*0.5f*height };
+    camera->offset = (Vector2){ (1 - bbox.x)*0.5f*width, (1 - bbox.y)*0.5f*height };
 
     if (player->position.x < bboxWorldMin.x) camera->target.x = player->position.x;
     if (player->position.y < bboxWorldMin.y) camera->target.y = player->position.y;
diff --git a/raylib/examples/core/core_2d_camera_split_screen.c b/raylib/examples/core/core_2d_camera_split_screen.c
--- a/raylib/examples/core/core_2d_camera_split_screen.c
+++ b/raylib/examples/core/core_2d_camera_split_screen.c
@@ -2,7 +2,9 @@
 *
 *   raylib [core] example - 2d camera split screen
 *
-*   Addapted from the core_3d_camera_split_screen example: 
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Addapted from the core_3d_camera_split_screen example:
 *       https://github.com/raysan5/raylib/blob/master/examples/core/core_3d_camera_split_screen.c
 *
 *   Example originally created with raylib 4.5, last time updated with raylib 4.5
@@ -12,7 +14,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Gabriel dos Santos Sanches (@gabrielssanches)
+*   Copyright (c) 2023-2025 Gabriel dos Santos Sanches (@gabrielssanches)
 *
 ********************************************************************************************/
 
@@ -79,9 +81,9 @@
         //----------------------------------------------------------------------------------
         BeginTextureMode(screenCamera1);
             ClearBackground(RAYWHITE);
-            
+
             BeginMode2D(camera1);
-            
+
                 // Draw full scene with first camera
                 for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++)
                 {
@@ -104,17 +106,17 @@
                 DrawRectangleRec(player1, RED);
                 DrawRectangleRec(player2, BLUE);
             EndMode2D();
-            
+
             DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f));
             DrawText("PLAYER1: W/S/A/D to move", 10, 10, 10, MAROON);
-            
+
         EndTextureMode();
 
         BeginTextureMode(screenCamera2);
             ClearBackground(RAYWHITE);
-            
+
             BeginMode2D(camera2);
-            
+
                 // Draw full scene with second camera
                 for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++)
                 {
@@ -136,21 +138,21 @@
 
                 DrawRectangleRec(player1, RED);
                 DrawRectangleRec(player2, BLUE);
-                
+
             EndMode2D();
-            
+
             DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f));
             DrawText("PLAYER2: UP/DOWN/LEFT/RIGHT to move", 10, 10, 10, DARKBLUE);
-            
+
         EndTextureMode();
 
         // Draw both views render textures to the screen side by side
         BeginDrawing();
             ClearBackground(BLACK);
-            
+
             DrawTextureRec(screenCamera1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE);
             DrawTextureRec(screenCamera2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE);
-            
+
             DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY);
         EndDrawing();
     }
diff --git a/raylib/examples/core/core_3d_camera_first_person.c b/raylib/examples/core/core_3d_camera_first_person.c
--- a/raylib/examples/core/core_3d_camera_first_person.c
+++ b/raylib/examples/core/core_3d_camera_first_person.c
@@ -2,12 +2,14 @@
 *
 *   raylib [core] example - 3d camera first person
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 1.3
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -98,8 +100,8 @@
                 camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
                 camera.projection = CAMERA_ORTHOGRAPHIC;
                 camera.fovy = 20.0f; // near plane width in CAMERA_ORTHOGRAPHIC
-                CameraYaw(&camera, -135 * DEG2RAD, true);
-                CameraPitch(&camera, -45 * DEG2RAD, true, true, false);
+                CameraYaw(&camera, -135*DEG2RAD, true);
+                CameraPitch(&camera, -45*DEG2RAD, true, true, false);
             }
             else if (camera.projection == CAMERA_ORTHOGRAPHIC)
             {
@@ -115,9 +117,8 @@
 
         // Update camera computes movement internally depending on the camera mode
         // Some default standard keyboard/mouse inputs are hardcoded to simplify use
-        // For advance camera controls, it's reecommended to compute camera movement manually
+        // For advanced camera controls, it's recommended to compute camera movement manually
         UpdateCamera(&camera, cameraMode);                  // Update camera
-
 /*
         // Camera PRO usage example (EXPERIMENTAL)
         // This new camera function allows custom movement/rotation values to be directly provided
@@ -125,7 +126,7 @@
         UpdateCameraPro(&camera,
             (Vector3){
                 (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f -      // Move forward-backward
-                (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f,    
+                (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f,
                 (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f -   // Move right-left
                 (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f,
                 0.0f                                                // Move up-down
diff --git a/raylib/examples/core/core_3d_camera_fps.c b/raylib/examples/core/core_3d_camera_fps.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_3d_camera_fps.c
@@ -0,0 +1,329 @@
+﻿/*******************************************************************************************
+*
+*   raylib [core] example - 3d camera fps
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Agnis Aldiņš (@nezvers)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Movement constants
+#define GRAVITY         32.0f
+#define MAX_SPEED       20.0f
+#define CROUCH_SPEED     5.0f
+#define JUMP_FORCE      12.0f
+#define MAX_ACCEL      150.0f
+// Grounded drag
+#define FRICTION         0.86f
+// Increasing air drag, increases strafing speed
+#define AIR_DRAG         0.98f
+// Responsiveness for turning movement direction to looked direction
+#define CONTROL         15.0f
+#define CROUCH_HEIGHT    0.0f
+#define STAND_HEIGHT     1.0f
+#define BOTTOM_HEIGHT    0.5f
+
+#define NORMALIZE_INPUT  0
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Body structure
+typedef struct {
+    Vector3 position;
+    Vector3 velocity;
+    Vector3 dir;
+    bool isGrounded;
+} Body;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static Vector2 sensitivity = { 0.001f, 0.001f };
+
+static Body player = { 0 };
+static Vector2 lookRotation = { 0 };
+static float headTimer = 0.0f;
+static float walkLerp = 0.0f;
+static float headLerp = STAND_HEIGHT;
+static Vector2 lean = { 0 };
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void DrawLevel(void);
+static void UpdateCameraFPS(Camera *camera);
+static void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera fps");
+
+    // Initialize camera variables
+    // NOTE: UpdateCameraFPS() takes care of the rest
+    Camera camera = { 0 };
+    camera.fovy = 60.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+    camera.position = (Vector3){
+        player.position.x,
+        player.position.y + (BOTTOM_HEIGHT + headLerp),
+        player.position.z,
+    };
+
+    UpdateCameraFPS(&camera); // Update camera parameters
+
+    DisableCursor();        // Limit cursor to relative movement inside the window
+
+    SetTargetFPS(60);       // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        Vector2 mouseDelta = GetMouseDelta();
+        lookRotation.x -= mouseDelta.x*sensitivity.x;
+        lookRotation.y += mouseDelta.y*sensitivity.y;
+
+        char sideway = (IsKeyDown(KEY_D) - IsKeyDown(KEY_A));
+        char forward = (IsKeyDown(KEY_W) - IsKeyDown(KEY_S));
+        bool crouching = IsKeyDown(KEY_LEFT_CONTROL);
+        UpdateBody(&player, lookRotation.x, sideway, forward, IsKeyPressed(KEY_SPACE), crouching);
+
+        float delta = GetFrameTime();
+        headLerp = Lerp(headLerp, (crouching ? CROUCH_HEIGHT : STAND_HEIGHT), 20.0f*delta);
+        camera.position = (Vector3){
+            player.position.x,
+            player.position.y + (BOTTOM_HEIGHT + headLerp),
+            player.position.z,
+        };
+
+        if (player.isGrounded && ((forward != 0) || (sideway != 0)))
+        {
+            headTimer += delta*3.0f;
+            walkLerp = Lerp(walkLerp, 1.0f, 10.0f*delta);
+            camera.fovy = Lerp(camera.fovy, 55.0f, 5.0f*delta);
+        }
+        else
+        {
+            walkLerp = Lerp(walkLerp, 0.0f, 10.0f*delta);
+            camera.fovy = Lerp(camera.fovy, 60.0f, 5.0f*delta);
+        }
+
+        lean.x = Lerp(lean.x, sideway*0.02f, 10.0f*delta);
+        lean.y = Lerp(lean.y, forward*0.015f, 10.0f*delta);
+
+        UpdateCameraFPS(&camera);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                DrawLevel();
+            EndMode3D();
+
+            // Draw info box
+            DrawRectangle(5, 5, 330, 75, Fade(SKYBLUE, 0.5f));
+            DrawRectangleLines(5, 5, 330, 75, BLUE);
+
+            DrawText("Camera controls:", 15, 15, 10, BLACK);
+            DrawText("- Move keys: W, A, S, D, Space, Left-Ctrl", 15, 30, 10, BLACK);
+            DrawText("- Look around: arrow keys or mouse", 15, 45, 10, BLACK);
+            DrawText(TextFormat("- Velocity Len: (%06.3f)", Vector2Length((Vector2){ player.velocity.x, player.velocity.z })), 15, 60, 10, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Update body considering current world state
+void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold)
+{
+    Vector2 input = (Vector2){ (float)side, (float)-forward };
+
+#if defined(NORMALIZE_INPUT)
+    // Slow down diagonal movement
+    if ((side != 0) && (forward != 0)) input = Vector2Normalize(input);
+#endif
+
+    float delta = GetFrameTime();
+
+    if (!body->isGrounded) body->velocity.y -= GRAVITY*delta;
+
+    if (body->isGrounded && jumpPressed)
+    {
+        body->velocity.y = JUMP_FORCE;
+        body->isGrounded = false;
+
+        // Sound can be played at this moment
+        //SetSoundPitch(fxJump, 1.0f + (GetRandomValue(-100, 100)*0.001));
+        //PlaySound(fxJump);
+    }
+
+    Vector3 front = (Vector3){ sinf(rot), 0.f, cosf(rot) };
+    Vector3 right = (Vector3){ cosf(-rot), 0.f, sinf(-rot) };
+
+    Vector3 desiredDir = (Vector3){ input.x*right.x + input.y*front.x, 0.0f, input.x*right.z + input.y*front.z, };
+    body->dir = Vector3Lerp(body->dir, desiredDir, CONTROL*delta);
+
+    float decel = (body->isGrounded ? FRICTION : AIR_DRAG);
+    Vector3 hvel = (Vector3){ body->velocity.x*decel, 0.0f, body->velocity.z*decel };
+
+    float hvelLength = Vector3Length(hvel); // Magnitude
+    if (hvelLength < (MAX_SPEED*0.01f)) hvel = (Vector3){ 0 };
+
+    // This is what creates strafing
+    float speed = Vector3DotProduct(hvel, body->dir);
+
+    // Whenever the amount of acceleration to add is clamped by the maximum acceleration constant,
+    // a Player can make the speed faster by bringing the direction closer to horizontal velocity angle
+    // More info here: https://youtu.be/v3zT3Z5apaM?t=165
+    float maxSpeed = (crouchHold? CROUCH_SPEED : MAX_SPEED);
+    float accel = Clamp(maxSpeed - speed, 0.f, MAX_ACCEL*delta);
+    hvel.x += body->dir.x*accel;
+    hvel.z += body->dir.z*accel;
+
+    body->velocity.x = hvel.x;
+    body->velocity.z = hvel.z;
+
+    body->position.x += body->velocity.x*delta;
+    body->position.y += body->velocity.y*delta;
+    body->position.z += body->velocity.z*delta;
+
+    // Fancy collision system against the floor
+    if (body->position.y <= 0.0f)
+    {
+        body->position.y = 0.0f;
+        body->velocity.y = 0.0f;
+        body->isGrounded = true; // Enable jumping
+    }
+}
+
+// Update camera for FPS behaviour
+static void UpdateCameraFPS(Camera *camera)
+{
+    const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    const Vector3 targetOffset = (Vector3){ 0.0f, 0.0f, -1.0f };
+
+    // Left and right
+    Vector3 yaw = Vector3RotateByAxisAngle(targetOffset, up, lookRotation.x);
+
+    // Clamp view up
+    float maxAngleUp = Vector3Angle(up, yaw);
+    maxAngleUp -= 0.001f; // Avoid numerical errors
+    if ( -(lookRotation.y) > maxAngleUp) { lookRotation.y = -maxAngleUp; }
+
+    // Clamp view down
+    float maxAngleDown = Vector3Angle(Vector3Negate(up), yaw);
+    maxAngleDown *= -1.0f; // Downwards angle is negative
+    maxAngleDown += 0.001f; // Avoid numerical errors
+    if ( -(lookRotation.y) < maxAngleDown) { lookRotation.y = -maxAngleDown; }
+
+    // Up and down
+    Vector3 right = Vector3Normalize(Vector3CrossProduct(yaw, up));
+
+    // Rotate view vector around right axis
+    float pitchAngle = -lookRotation.y - lean.y;
+    pitchAngle = Clamp(pitchAngle, -PI/2 + 0.0001f, PI/2 - 0.0001f); // Clamp angle so it doesn't go past straight up or straight down
+    Vector3 pitch = Vector3RotateByAxisAngle(yaw, right, pitchAngle);
+
+    // Head animation
+    // Rotate up direction around forward axis
+    float headSin = sinf(headTimer*PI);
+    float headCos = cosf(headTimer*PI);
+    const float stepRotation = 0.01f;
+    camera->up = Vector3RotateByAxisAngle(up, pitch, headSin*stepRotation + lean.x);
+
+    // Camera BOB
+    const float bobSide = 0.1f;
+    const float bobUp = 0.15f;
+    Vector3 bobbing = Vector3Scale(right, headSin*bobSide);
+    bobbing.y = fabsf(headCos*bobUp);
+
+    camera->position = Vector3Add(camera->position, Vector3Scale(bobbing, walkLerp));
+    camera->target = Vector3Add(camera->position, pitch);
+}
+
+// Draw game level
+static void DrawLevel(void)
+{
+    const int floorExtent = 25;
+    const float tileSize = 5.0f;
+    const Color tileColor1 = (Color){ 150, 200, 200, 255 };
+
+    // Floor tiles
+    for (int y = -floorExtent; y < floorExtent; y++)
+    {
+        for (int x = -floorExtent; x < floorExtent; x++)
+        {
+            if ((y & 1) && (x & 1))
+            {
+                DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, tileColor1);
+            }
+            else if (!(y & 1) && !(x & 1))
+            {
+                DrawPlane((Vector3){ x*tileSize, 0.0f, y*tileSize}, (Vector2){ tileSize, tileSize }, LIGHTGRAY);
+            }
+        }
+    }
+
+    const Vector3 towerSize = (Vector3){ 16.0f, 32.0f, 16.0f };
+    const Color towerColor = (Color){ 150, 200, 200, 255 };
+
+    Vector3 towerPos = (Vector3){ 16.0f, 16.0f, 16.0f };
+    DrawCubeV(towerPos, towerSize, towerColor);
+    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
+
+    towerPos.x *= -1;
+    DrawCubeV(towerPos, towerSize, towerColor);
+    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
+
+    towerPos.z *= -1;
+    DrawCubeV(towerPos, towerSize, towerColor);
+    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
+
+    towerPos.x *= -1;
+    DrawCubeV(towerPos, towerSize, towerColor);
+    DrawCubeWiresV(towerPos, towerSize, DARKBLUE);
+
+    // Red sun
+    DrawSphere((Vector3){ 300.0f, 300.0f, 0.0f }, 100.0f, (Color){ 255, 0, 0, 255 });
+}
diff --git a/raylib/examples/core/core_3d_camera_free.c b/raylib/examples/core/core_3d_camera_free.c
--- a/raylib/examples/core/core_3d_camera_free.c
+++ b/raylib/examples/core/core_3d_camera_free.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Initialize 3d camera free
+*   raylib [core] example - 3d camera free
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 1.3
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -47,7 +49,7 @@
         //----------------------------------------------------------------------------------
         UpdateCamera(&camera, CAMERA_FREE);
 
-        if (IsKeyPressed('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+        if (IsKeyPressed(KEY_Z)) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
         //----------------------------------------------------------------------------------
 
         // Draw
diff --git a/raylib/examples/core/core_3d_camera_mode.c b/raylib/examples/core/core_3d_camera_mode.c
--- a/raylib/examples/core/core_3d_camera_mode.c
+++ b/raylib/examples/core/core_3d_camera_mode.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Initialize 3d camera mode
+*   raylib [core] example - 3d camera mode
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/core/core_3d_camera_split_screen.c b/raylib/examples/core/core_3d_camera_split_screen.c
--- a/raylib/examples/core/core_3d_camera_split_screen.c
+++ b/raylib/examples/core/core_3d_camera_split_screen.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - 3d cmaera split screen
+*   raylib [core] example - 3d camera split screen
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 3.7, last time updated with raylib 4.0
 *
 *   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Jeffery Myers (@JeffM2501)
+*   Copyright (c) 2021-2025 Jeffery Myers (@JeffM2501)
 *
 ********************************************************************************************/
 
@@ -45,11 +47,11 @@
     cameraPlayer2.position.x = -3.0f;
     cameraPlayer2.position.y = 3.0f;
 
-    RenderTexture screenPlayer2 = LoadRenderTexture(screenWidth / 2, screenHeight);
+    RenderTexture screenPlayer2 = LoadRenderTexture(screenWidth/2, screenHeight);
 
     // Build a flipped rectangle the size of the split view to use for drawing later
     Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenPlayer1.texture.width, (float)-screenPlayer1.texture.height };
-    
+
     // Grid data
     int count = 5;
     float spacing = 4;
@@ -63,7 +65,7 @@
         // Update
         //----------------------------------------------------------------------------------
         // If anyone moves this frame, how far will they move based on the time since the last frame
-        // this moves thigns at 10 world units per second, regardless of the actual FPS
+        // this moves things at 10 world units per second, regardless of the actual FPS
         float offsetThisFrame = 10.0f*GetFrameTime();
 
         // Move Player1 forward and backwards (no turning)
@@ -96,9 +98,9 @@
         // Draw Player1 view to the render texture
         BeginTextureMode(screenPlayer1);
             ClearBackground(SKYBLUE);
-            
+
             BeginMode3D(cameraPlayer1);
-            
+
                 // Draw scene: grid of cube trees on a plane to make a "world"
                 DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane
 
@@ -114,20 +116,20 @@
                 // Draw a cube at each player's position
                 DrawCube(cameraPlayer1.position, 1, 1, 1, RED);
                 DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE);
-                
+
             EndMode3D();
-            
+
             DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f));
             DrawText("PLAYER1: W/S to move", 10, 10, 20, MAROON);
-            
+
         EndTextureMode();
 
         // Draw Player2 view to the render texture
         BeginTextureMode(screenPlayer2);
             ClearBackground(SKYBLUE);
-            
+
             BeginMode3D(cameraPlayer2);
-            
+
                 // Draw scene: grid of cube trees on a plane to make a "world"
                 DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane
 
@@ -143,21 +145,21 @@
                 // Draw a cube at each player's position
                 DrawCube(cameraPlayer1.position, 1, 1, 1, RED);
                 DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE);
-                
+
             EndMode3D();
-            
+
             DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f));
             DrawText("PLAYER2: UP/DOWN to move", 10, 10, 20, DARKBLUE);
-            
+
         EndTextureMode();
 
         // Draw both views render textures to the screen side by side
         BeginDrawing();
             ClearBackground(BLACK);
-            
+
             DrawTextureRec(screenPlayer1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE);
             DrawTextureRec(screenPlayer2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE);
-            
+
             DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY);
         EndDrawing();
     }
diff --git a/raylib/examples/core/core_3d_picking.c b/raylib/examples/core/core_3d_picking.c
--- a/raylib/examples/core/core_3d_picking.c
+++ b/raylib/examples/core/core_3d_picking.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Picking in 3d mode
+*   raylib [core] example - 3d picking
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -99,7 +101,7 @@
 
             DrawText("Try clicking on the box with your mouse!", 240, 10, 20, DARKGRAY);
 
-            if (collision.hit) DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30)) / 2, (int)(screenHeight * 0.1f), 30, GREEN);
+            if (collision.hit) DrawText("BOX SELECTED", (screenWidth - MeasureText("BOX SELECTED", 30))/2, (int)(screenHeight*0.1f), 30, GREEN);
 
             DrawText("Right click mouse to toggle camera controls", 10, 430, 10, GRAY);
 
diff --git a/raylib/examples/core/core_automation_events.c b/raylib/examples/core/core_automation_events.c
--- a/raylib/examples/core/core_automation_events.c
+++ b/raylib/examples/core/core_automation_events.c
@@ -2,6 +2,8 @@
 *
 *   raylib [core] example - automation events
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 5.0
 *
 *   Example based on 2d_camera_platformer example by arvyy (@arvyy)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -22,6 +24,9 @@
 
 #define MAX_ENVIRONMENT_ELEMENTS    5
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 typedef struct Player {
     Vector2 position;
     float speed;
@@ -34,7 +39,6 @@
     Color color;
 } EnvElement;
 
-
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -52,7 +56,7 @@
     player.position = (Vector2){ 400, 280 };
     player.speed = 0;
     player.canJump = false;
-    
+
     // Define environment elements (platforms)
     EnvElement envElements[MAX_ENVIRONMENT_ELEMENTS] = {
         {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY },
@@ -68,13 +72,13 @@
     camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
     camera.rotation = 0.0f;
     camera.zoom = 1.0f;
-    
+
     // Automation events
     AutomationEventList aelist = LoadAutomationEventList(0);  // Initialize list of automation events to record new events
     SetAutomationEventList(&aelist);
     bool eventRecording = false;
     bool eventPlaying = false;
-    
+
     unsigned int frameCounter = 0;
     unsigned int playFrameCounter = 0;
     unsigned int currentPlayFrame = 0;
@@ -88,7 +92,7 @@
         // Update
         //----------------------------------------------------------------------------------
         float deltaTime = 0.015f;//GetFrameTime();
-       
+
         // Dropped files logic
         //----------------------------------------------------------------------------------
         if (IsFileDropped())
@@ -100,14 +104,14 @@
             {
                 UnloadAutomationEventList(aelist);
                 aelist = LoadAutomationEventList(droppedFiles.paths[0]);
-                
+
                 eventRecording = false;
-                
+
                 // Reset scene state to play
                 eventPlaying = true;
                 playFrameCounter = 0;
                 currentPlayFrame = 0;
-                
+
                 player.position = (Vector2){ 400, 280 };
                 player.speed = 0;
                 player.canJump = false;
@@ -172,7 +176,7 @@
         //----------------------------------------------------------------------------------
 
         // Events playing
-        // NOTE: Logic must be before Camera update because it depends on mouse-wheel value, 
+        // NOTE: Logic must be before Camera update because it depends on mouse-wheel value,
         // that can be set by the played event... but some other inputs could be affected
         //----------------------------------------------------------------------------------
         if (eventPlaying)
@@ -226,7 +230,7 @@
         if (min.x > 0) camera.offset.x = screenWidth/2 - min.x;
         if (min.y > 0) camera.offset.y = screenHeight/2 - min.y;
         //----------------------------------------------------------------------------------
-        
+
         // Events management
         if (IsKeyPressed(KEY_S))    // Toggle events recording
         {
@@ -236,12 +240,12 @@
                 {
                     StopAutomationEventRecording();
                     eventRecording = false;
-                    
+
                     ExportAutomationEventList(aelist, "automation.rae");
-                    
+
                     TraceLog(LOG_INFO, "RECORDED FRAMES: %i", aelist.count);
                 }
-                else 
+                else
                 {
                     SetAutomationEventBaseFrame(180);
                     StartAutomationEventRecording();
@@ -291,7 +295,7 @@
                 DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED);
 
             EndMode2D();
-            
+
             // Draw game controls
             DrawRectangle(10, 10, 290, 145, Fade(SKYBLUE, 0.5f));
             DrawRectangleLines(10, 10, 290, 145, Fade(BLUE, 0.8f));
@@ -321,7 +325,7 @@
 
                 if (((frameCounter/15)%2) == 1) DrawText(TextFormat("PLAYING RECORDED EVENTS... [%i]", currentPlayFrame), 50, 170, 10, DARKGREEN);
             }
-            
+
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/core/core_basic_screen_manager.c b/raylib/examples/core/core_basic_screen_manager.c
--- a/raylib/examples/core/core_basic_screen_manager.c
+++ b/raylib/examples/core/core_basic_screen_manager.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] examples - basic screen manager
+*   raylib [core] example - basic screen manager
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: This example illustrates a very simple screen manager based on a states machines
 *
 *   Example originally created with raylib 4.0, last time updated with raylib 4.0
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -46,7 +48,7 @@
     {
         // Update
         //----------------------------------------------------------------------------------
-        switch(currentScreen)
+        switch (currentScreen)
         {
             case LOGO:
             {
diff --git a/raylib/examples/core/core_basic_window.c b/raylib/examples/core/core_basic_window.c
--- a/raylib/examples/core/core_basic_window.c
+++ b/raylib/examples/core/core_basic_window.c
@@ -1,12 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Basic window
+*   raylib [core] example - basic window
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Welcome to raylib!
 *
-*   To test examples, just press F6 and execute raylib_compile_execute script
+*   To test examples, just press F6 and execute 'raylib_compile_execute' script
 *   Note that compiled executable is placed in the same folder as .c file
 *
+*   To test the examples on Web, press F6 and execute 'raylib_compile_execute_web' script
+*   Web version of the program is generated in the same folder as .c file
+*
 *   You can find all basic examples on C:\raylib\raylib\examples folder or
 *   raylib official webpage: www.raylib.com
 *
@@ -17,7 +22,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/core/core_basic_window_web.c b/raylib/examples/core/core_basic_window_web.c
deleted file mode 100644
--- a/raylib/examples/core/core_basic_window_web.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [core] example - Basic window (adapted for HTML5 platform)
-*
-*   NOTE: This example is prepared to compile for PLATFORM_WEB, and PLATFORM_DESKTOP
-*   As you will notice, code structure is slightly diferent to the other examples...
-*   To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning
-*
-*   Example originally created with raylib 1.3, last time updated with raylib 1.3
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-//#define PLATFORM_WEB
-
-#if defined(PLATFORM_WEB)
-    #include <emscripten/emscripten.h>
-#endif
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-const int screenWidth = 800;
-const int screenHeight = 450;
-
-//----------------------------------------------------------------------------------
-// Module functions declaration
-//----------------------------------------------------------------------------------
-void UpdateDrawFrame(void);     // Update and Draw one frame
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
-
-#if defined(PLATFORM_WEB)
-    emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
-#else
-    SetTargetFPS(60);   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        UpdateDrawFrame();
-    }
-#endif
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-void UpdateDrawFrame(void)
-{
-    // Update
-    //----------------------------------------------------------------------------------
-    // TODO: Update your variables here
-    //----------------------------------------------------------------------------------
-
-    // Draw
-    //----------------------------------------------------------------------------------
-    BeginDrawing();
-
-        ClearBackground(RAYWHITE);
-
-        DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
-
-    EndDrawing();
-    //----------------------------------------------------------------------------------
-}
diff --git a/raylib/examples/core/core_clipboard_text.c b/raylib/examples/core/core_clipboard_text.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_clipboard_text.c
@@ -0,0 +1,164 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - clipboard text
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Ananth S (@Ananth1839) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ananth S (@Ananth1839)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+#define MAX_TEXT_SAMPLES    5
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - clipboard text");
+
+    // Define some sample texts
+    const char *sampleTexts[MAX_TEXT_SAMPLES] = {
+        "Hello from raylib!",
+        "The quick brown fox jumps over the lazy dog",
+        "Clipboard operations are useful!",
+        "raylib is a simple and easy-to-use library",
+        "Copy and paste me!"
+    };
+
+    const char *clipboardText = NULL;
+    char inputBuffer[256] = "Hello from raylib!"; // Random initial string
+
+    // UI required variables
+    bool textBoxEditMode = false;
+
+    bool btnCutPressed = false;
+    bool btnCopyPressed = false;
+    bool btnPastePressed = false;
+    bool btnClearPressed = false;
+    bool btnRandomPressed = false;
+
+    // Set UI style
+    GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+    GuiSetIconScale(2);
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Handle button interactions
+        if (btnCutPressed)
+        {
+            SetClipboardText(inputBuffer);
+            clipboardText = GetClipboardText();
+            inputBuffer[0] = '\0'; // Quick solution to clear text
+            //memset(inputBuffer, 0, 256); // Clear full buffer properly
+        }
+
+        if (btnCopyPressed)
+        {
+            SetClipboardText(inputBuffer); // Copy text to clipboard
+            clipboardText = GetClipboardText(); // Get text from clipboard
+        }
+
+        if (btnPastePressed)
+        {
+            // Paste text from clipboard
+            clipboardText = GetClipboardText();
+            if (clipboardText != NULL) TextCopy(inputBuffer, clipboardText);
+        }
+
+        if (btnClearPressed)
+        {
+            inputBuffer[0] = '\0'; // Quick solution to clear text
+            //memset(inputBuffer, 0, 256); // Clear full buffer properly
+        }
+
+        if (btnRandomPressed)
+        {
+            // Get random text from sample list
+            TextCopy(inputBuffer, sampleTexts[GetRandomValue(0, MAX_TEXT_SAMPLES - 1)]);
+        }
+
+        // Quick cut/copy/paste with keyboard shortcuts
+        if (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL))
+        {
+            if (IsKeyPressed(KEY_X))
+            {
+                SetClipboardText(inputBuffer);
+                inputBuffer[0] = '\0'; // Quick solution to clear text
+            }
+
+            if (IsKeyPressed(KEY_C)) SetClipboardText(inputBuffer);
+
+            if (IsKeyPressed(KEY_V))
+            {
+                clipboardText = GetClipboardText();
+                if (clipboardText != NULL) TextCopy(inputBuffer, clipboardText);
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+        ClearBackground(RAYWHITE);
+
+        // Draw instructions
+        GuiLabel((Rectangle){ 50, 20, 700, 36 }, "Use the BUTTONS or KEY SHORTCUTS:");
+        DrawText("[CTRL+X] - CUT | [CTRL+C] COPY | [CTRL+V] | PASTE", 50, 60, 20, MAROON);
+
+        // Draw text box
+        if (GuiTextBox((Rectangle){ 50, 120, 652, 40 }, inputBuffer, 256, textBoxEditMode)) textBoxEditMode = !textBoxEditMode;
+
+        // Random text button
+        btnRandomPressed = GuiButton((Rectangle){ 50 + 652 + 8, 120, 40, 40 }, "#77#");
+
+        // Draw buttons
+        btnCutPressed = GuiButton((Rectangle){ 50, 180, 158, 40 }, "#17#CUT");
+        btnCopyPressed = GuiButton((Rectangle){ 50 + 165, 180, 158, 40 }, "#16#COPY");
+        btnPastePressed = GuiButton((Rectangle){ 50 + 165*2, 180, 158, 40 }, "#18#PASTE");
+        btnClearPressed = GuiButton((Rectangle){ 50 + 165*3, 180, 158, 40 }, "#143#CLEAR");
+
+        // Draw clipboard status
+        GuiSetState(STATE_DISABLED);
+        GuiLabel((Rectangle){ 50, 260, 700, 40 }, "Clipboard current text data:");
+        GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+        GuiTextBox((Rectangle){ 50, 300, 700, 40 }, (char *)clipboardText, 256, false);
+        GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+        GuiLabel((Rectangle){ 50, 360, 700, 40 }, "Try copying text from other applications and pasting here!");
+        GuiSetState(STATE_NORMAL);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_compute_hash.c b/raylib/examples/core/core_compute_hash.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_compute_hash.c
@@ -0,0 +1,143 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - compute hash
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static char *GetDataAsHexText(const unsigned int *data, int dataSize);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - compute hash");
+
+    // UI controls variables
+    char textInput[96] = "The quick brown fox jumps over the lazy dog.";
+    bool textBoxEditMode = false;
+    bool btnComputeHashes = false;
+
+    // Data hash values
+    unsigned int hashCRC32 = 0;
+    unsigned int *hashMD5 = NULL;
+    unsigned int *hashSHA1 = NULL;
+    unsigned int *hashSHA256 = NULL;
+
+    // Base64 encoded data
+    char *base64Text = NULL;
+    int base64TextSize = 0;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (btnComputeHashes)
+        {
+            int textInputLen = strlen(textInput);
+
+            // Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
+            base64Text = EncodeDataBase64((unsigned char *)textInput, textInputLen, &base64TextSize);
+
+            hashCRC32 = ComputeCRC32((unsigned char *)textInput, textInputLen);     // Compute CRC32 hash code (4 bytes)
+            hashMD5 = ComputeMD5((unsigned char *)textInput, textInputLen);         // Compute MD5 hash code, returns static int[4] (16 bytes)
+            hashSHA1 = ComputeSHA1((unsigned char *)textInput, textInputLen);       // Compute SHA1 hash code, returns static int[5] (20 bytes)
+            hashSHA256 = ComputeSHA256((unsigned char *)textInput, textInputLen);   // Compute SHA256 hash code, returns static int[8] (32 bytes)
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+            GuiSetStyle(DEFAULT, TEXT_SPACING, 2);
+            GuiLabel((Rectangle){ 40, 26, 720, 32 }, "INPUT DATA (TEXT):");
+            GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+            GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+
+            if (GuiTextBox((Rectangle){ 40, 64, 720, 32 }, textInput, 95, textBoxEditMode)) textBoxEditMode = !textBoxEditMode;
+
+            btnComputeHashes = GuiButton((Rectangle){ 40, 64 + 40, 720, 32 }, "COMPUTE INPUT DATA HASHES");
+
+            GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
+            GuiSetStyle(DEFAULT, TEXT_SPACING, 2);
+            GuiLabel((Rectangle){ 40, 160, 720, 32 }, "INPUT DATA HASH VALUES:");
+            GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+            GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+
+            GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+            GuiLabel((Rectangle){ 40, 200, 120, 32 }, "CRC32 [32 bit]:");
+            GuiTextBox((Rectangle){ 40 + 120, 200, 720 - 120, 32 }, GetDataAsHexText(&hashCRC32, 1), 120, false);
+            GuiLabel((Rectangle){ 40, 200 + 36, 120, 32 }, "MD5 [128 bit]:");
+            GuiTextBox((Rectangle){ 40 + 120, 200 + 36, 720 - 120, 32 }, GetDataAsHexText(hashMD5, 4), 120, false);
+            GuiLabel((Rectangle){ 40, 200 + 36*2, 120, 32 }, "SHA1 [160 bit]:");
+            GuiTextBox((Rectangle){ 40 + 120, 200 + 36*2, 720 - 120, 32 }, GetDataAsHexText(hashSHA1, 5), 120, false);
+            GuiLabel((Rectangle){ 40, 200 + 36*3, 120, 32 }, "SHA256 [256 bit]:");
+            GuiTextBox((Rectangle){ 40 + 120, 200 + 36*3, 720 - 120, 32 }, GetDataAsHexText(hashSHA256, 8), 120, false);
+
+            GuiSetState(STATE_FOCUSED);
+            GuiLabel((Rectangle){ 40, 200 + 36*5 - 30, 320, 32 }, "BONUS - BAS64 ENCODED STRING:");
+            GuiSetState(STATE_NORMAL);
+            GuiLabel((Rectangle){ 40, 200 + 36*5, 120, 32 }, "BASE64 ENCODING:");
+            GuiTextBox((Rectangle){ 40 + 120, 200 + 36*5, 720 - 120, 32 }, base64Text, 120, false);
+            GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    MemFree(base64Text);    // Free Base64 text data
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+static char *GetDataAsHexText(const unsigned int *data, int dataSize)
+{
+    static char text[128] = { 0 };
+    memset(text, 0, 128);
+
+    if ((data != NULL) && (dataSize > 0) && (dataSize < ((128/8) - 1)))
+    {
+        for (int i = 0; i < dataSize; i++) TextCopy(text + i*8, TextFormat("%08X", data[i]));
+    }
+    else TextCopy(text, "00000000");
+
+    return text;
+}
diff --git a/raylib/examples/core/core_custom_frame_control.c b/raylib/examples/core/core_custom_frame_control.c
--- a/raylib/examples/core/core_custom_frame_control.c
+++ b/raylib/examples/core/core_custom_frame_control.c
@@ -2,6 +2,8 @@
 *
 *   raylib [core] example - custom frame control
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   NOTE: WARNING: This is an example for advanced users willing to have full control over
 *   the frame processes. By default, EndDrawing() calls the following processes:
 *       1. Draw remaining batch data: rlDrawRenderBatchActive()
@@ -10,7 +12,7 @@
 *       4. PollInputEvents()
 *
 *   To avoid steps 2, 3 and 4, flag SUPPORT_CUSTOM_FRAME_CONTROL can be enabled in
-*   config.h (it requires recompiling raylib). This way those steps are up to the user.
+*   config.h (it requires recompiling raylib). This way those steps are up to the user
 *
 *   Note that enabling this flag invalidates some functions:
 *       - GetFrameTime()
@@ -22,7 +24,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -37,7 +39,7 @@
     //--------------------------------------------------------------------------------------
     const int screenWidth = 800;
     const int screenHeight = 450;
-    
+
     InitWindow(screenWidth, screenHeight, "raylib [core] example - custom frame control");
 
     // Custom timming variables
@@ -46,11 +48,11 @@
     double updateDrawTime = 0.0;        // Update + Draw time
     double waitTime = 0.0;              // Wait time (if target fps required)
     float deltaTime = 0.0f;             // Frame time (Update + Draw + Wait time)
-    
+
     float timeCounter = 0.0f;           // Accumulative time counter (seconds)
     float position = 0.0f;              // Circle position
     bool pause = false;                 // Pause control flag
-    
+
     int targetFPS = 60;                 // Our initial target fps
     //--------------------------------------------------------------------------------------
 
@@ -59,13 +61,15 @@
     {
         // Update
         //----------------------------------------------------------------------------------
-        PollInputEvents();              // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
-        
+        #ifndef PLATFORM_WEB            // NOTE: On non web platforms the PollInputEvents just works before the inputs checks
+            PollInputEvents();          // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
+        #endif
+
         if (IsKeyPressed(KEY_SPACE)) pause = !pause;
-        
+
         if (IsKeyPressed(KEY_UP)) targetFPS += 20;
         else if (IsKeyPressed(KEY_DOWN)) targetFPS -= 20;
-        
+
         if (targetFPS < 0) targetFPS = 0;
 
         if (!pause)
@@ -74,6 +78,10 @@
             if (position >= GetScreenWidth()) position = 0;
             timeCounter += deltaTime;   // We count time (seconds)
         }
+
+        #ifdef PLATFORM_WEB             // NOTE: On web platform for some reason the PollInputEvents only works after the inputs check, so just call it after check all your inputs (on web)
+            PollInputEvents();          // Poll input events (SUPPORT_CUSTOM_FRAME_CONTROL)
+        #endif
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -83,32 +91,35 @@
             ClearBackground(RAYWHITE);
 
             for (int i = 0; i < GetScreenWidth()/200; i++) DrawRectangle(200*i, 0, 1, GetScreenHeight(), SKYBLUE);
-            
+
             DrawCircle((int)position, GetScreenHeight()/2 - 25, 50, RED);
-            
+
             DrawText(TextFormat("%03.0f ms", timeCounter*1000.0f), (int)position - 40, GetScreenHeight()/2 - 100, 20, MAROON);
             DrawText(TextFormat("PosX: %03.0f", position), (int)position - 50, GetScreenHeight()/2 + 40, 20, BLACK);
-            
+
             DrawText("Circle is moving at a constant 200 pixels/sec,\nindependently of the frame rate.", 10, 10, 20, DARKGRAY);
             DrawText("PRESS SPACE to PAUSE MOVEMENT", 10, GetScreenHeight() - 60, 20, GRAY);
             DrawText("PRESS UP | DOWN to CHANGE TARGET FPS", 10, GetScreenHeight() - 30, 20, GRAY);
             DrawText(TextFormat("TARGET FPS: %i", targetFPS), GetScreenWidth() - 220, 10, 20, LIME);
-            DrawText(TextFormat("CURRENT FPS: %i", (int)(1.0f/deltaTime)), GetScreenWidth() - 220, 40, 20, GREEN);
+            if (deltaTime != 0)
+            {
+                DrawText(TextFormat("CURRENT FPS: %i", (int)(1.0f/deltaTime)), GetScreenWidth() - 220, 40, 20, GREEN);
+            }
 
         EndDrawing();
 
-        // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL, 
+        // NOTE: In case raylib is configured to SUPPORT_CUSTOM_FRAME_CONTROL,
         // Events polling, screen buffer swap and frame time control must be managed by the user
 
         SwapScreenBuffer();         // Flip the back buffer to screen (front buffer)
-        
+
         currentTime = GetTime();
         updateDrawTime = currentTime - previousTime;
-        
+
         if (targetFPS > 0)          // We want a fixed frame rate
         {
             waitTime = (1.0f/(float)targetFPS) - updateDrawTime;
-            if (waitTime > 0.0) 
+            if (waitTime > 0.0)
             {
                 WaitTime((float)waitTime);
                 currentTime = GetTime();
diff --git a/raylib/examples/core/core_custom_logging.c b/raylib/examples/core/core_custom_logging.c
--- a/raylib/examples/core/core_custom_logging.c
+++ b/raylib/examples/core/core_custom_logging.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Custom logging
+*   raylib [core] example - custom logging
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
 *   Example contributed by Pablo Marcos Oltra (@pamarcos) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,17 +11,17 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Pablo Marcos Oltra (@pamarcos) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 
-#include <stdio.h>                  // Required for: fopen(), fclose(), fputc(), fwrite(), printf(), fprintf(), funopen()
-#include <time.h>                   // Required for: time_t, tm, time(), localtime(), strftime()
+#include <stdio.h>          // Required for: printf(), vprintf(), fprintf()
+#include <time.h>           // Required for: time_t, tm, time(), localtime(), strftime()
 
 // Custom logging function
-void CustomLog(int msgType, const char *text, va_list args)
+void CustomTraceLog(int msgType, const char *text, va_list args)
 {
     char timeStr[64] = { 0 };
     time_t now = time(NULL);
@@ -52,7 +54,7 @@
     const int screenHeight = 450;
 
     // Set custom logger
-    SetTraceLogCallback(CustomLog);
+    SetTraceLogCallback(CustomTraceLog);
 
     InitWindow(screenWidth, screenHeight, "raylib [core] example - custom logging");
 
diff --git a/raylib/examples/core/core_delta_time.c b/raylib/examples/core/core_delta_time.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_delta_time.c
@@ -0,0 +1,112 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - delta time
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - delta time");
+
+    int currentFps = 60;
+
+    // Store the position for the both of the circles
+    Vector2 deltaCircle = { 0, (float)screenHeight/3.0f };
+    Vector2 frameCircle = { 0, (float)screenHeight*(2.0f/3.0f) };
+
+    // The speed applied to both circles
+    const float speed = 10.0f;
+    const float circleRadius = 32.0f;
+
+    SetTargetFPS(currentFps);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose()) // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Adjust the FPS target based on the mouse wheel
+        float mouseWheel = GetMouseWheelMove();
+        if (mouseWheel != 0)
+        {
+            currentFps += (int)mouseWheel;
+            if (currentFps < 0) currentFps = 0;
+            SetTargetFPS(currentFps);
+        }
+
+        // GetFrameTime() returns the time it took to draw the last frame, in seconds (usually called delta time)
+        // Uses the delta time to make the circle look like it's moving at a "consistent" speed regardless of FPS
+
+        // Multiply by 6.0 (an arbitrary value) in order to make the speed
+        // visually closer to the other circle (at 60 fps), for comparison
+        deltaCircle.x += GetFrameTime()*6.0f*speed;
+        // This circle can move faster or slower visually depending on the FPS
+        frameCircle.x += 0.1f*speed;
+
+        // If either circle is off the screen, reset it back to the start
+        if (deltaCircle.x > screenWidth) deltaCircle.x = 0;
+        if (frameCircle.x > screenWidth) frameCircle.x = 0;
+
+        // Reset both circles positions
+        if (IsKeyPressed(KEY_R))
+        {
+            deltaCircle.x = 0;
+            frameCircle.x = 0;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw both circles to the screen
+            DrawCircleV(deltaCircle, circleRadius, RED);
+            DrawCircleV(frameCircle, circleRadius, BLUE);
+
+            // Draw the help text
+            // Determine what help text to show depending on the current FPS target
+            const char *fpsText = 0;
+            if (currentFps <= 0) fpsText = TextFormat("FPS: unlimited (%i)", GetFPS());
+            else fpsText = TextFormat("FPS: %i (target: %i)", GetFPS(), currentFps);
+            DrawText(fpsText, 10, 10, 20, DARKGRAY);
+            DrawText(TextFormat("Frame time: %02.02f ms", GetFrameTime()), 10, 30, 20, DARKGRAY);
+            DrawText("Use the scroll wheel to change the fps limit, r to reset", 10, 50, 20, DARKGRAY);
+
+            // Draw the text above the circles
+            DrawText("FUNC: x += GetFrameTime()*speed", 10, 90, 20, RED);
+            DrawText("FUNC: x += speed", 10, 240, 20, BLUE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_directory_files.c b/raylib/examples/core/core_directory_files.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_directory_files.c
@@ -0,0 +1,100 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - directory files
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Hugo ARNAL (@hugoarnal) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Hugo ARNAL (@hugoarnal)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+#define MAX_FILEPATH_SIZE       1024
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - directory files");
+
+    char directory[MAX_FILEPATH_SIZE] = { 0 };
+    strcpy(directory, GetWorkingDirectory());
+
+    FilePathList files = LoadDirectoryFiles(directory);
+
+    int btnBackPressed = false;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (btnBackPressed)
+        {
+            TextCopy(directory, GetPrevDirectoryPath(directory));
+            UnloadDirectoryFiles(files);
+            files = LoadDirectoryFiles(directory);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            DrawText(directory, 100, 40, 20, DARKGRAY);
+
+            btnBackPressed = GuiButton((Rectangle){ 40.0f, 38.0f, 48, 24 }, "<");
+
+            for (int i = 0; i < (int)files.count; i++)
+            {
+                Color color = Fade(LIGHTGRAY, 0.3f);
+
+                if (!IsPathFile(files.paths[i]) && DirectoryExists(files.paths[i]))
+                {
+                    if (GuiButton((Rectangle){0.0f, 85.0f + 40.0f*(float)i, screenWidth, 40}, ""))
+                    {
+                        TextCopy(directory, files.paths[i]);
+                        UnloadDirectoryFiles(files);
+                        files = LoadDirectoryFiles(directory);
+                        continue;
+                    }
+                }
+
+                DrawRectangle(0, 85 + 40*i, screenWidth, 40, color);
+                DrawText(GetFileName(files.paths[i]), 120, 100 + 40*i, 10, GRAY);
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadDirectoryFiles(files);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_drop_files.c b/raylib/examples/core/core_drop_files.c
--- a/raylib/examples/core/core_drop_files.c
+++ b/raylib/examples/core/core_drop_files.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Windows drop files
+*   raylib [core] example - drop files
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?)
 *
 *   Example originally created with raylib 1.3, last time updated with raylib 4.2
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/core/core_highdpi_demo.c b/raylib/examples/core/core_highdpi_demo.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_highdpi_demo.c
@@ -0,0 +1,134 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - highdpi demo
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 5.5
+*
+*   Example contributed by Jonathan Marler (@marler8997) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jonathan Marler (@marler8997)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_HIGHDPI | FLAG_WINDOW_RESIZABLE);
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi demo");
+    SetWindowMinSize(450, 450);
+
+    int logicalGridDescY = 120;
+    int logicalGridLabelY = logicalGridDescY + 30;
+    int logicalGridTop = logicalGridLabelY + 30;
+    int logicalGridBottom = logicalGridTop + 80;
+    int pixelGridTop = logicalGridBottom - 20;
+    int pixelGridBottom = pixelGridTop + 80;
+    int pixelGridLabelY = pixelGridBottom + 30;
+    int pixelGridDescY = pixelGridLabelY + 30;
+    int cellSize = 50;
+    float cellSizePx = (float)cellSize;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        int monitorCount = GetMonitorCount();
+
+        if ((monitorCount > 1) && IsKeyPressed(KEY_N))
+        {
+            SetWindowMonitor((GetCurrentMonitor() + 1)%monitorCount);
+        }
+
+        int currentMonitor = GetCurrentMonitor();
+        Vector2 dpiScale = GetWindowScaleDPI();
+        cellSizePx = ((float)cellSize)/dpiScale.x;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            int windowCenter = GetScreenWidth()/2;
+            DrawTextCenter(TextFormat("Dpi Scale: %f", dpiScale.x), windowCenter, 30, 40, DARKGRAY);
+            DrawTextCenter(TextFormat("Monitor: %d/%d ([N] next monitor)", currentMonitor+1, monitorCount), windowCenter, 70, 20, LIGHTGRAY);
+            DrawTextCenter(TextFormat("Window is %d \"logical points\" wide", GetScreenWidth()), windowCenter, logicalGridDescY, 20, ORANGE);
+
+            bool odd = true;
+            for (int i = cellSize; i < GetScreenWidth(); i += cellSize, odd = !odd)
+            {
+                if (odd) DrawRectangle(i, logicalGridTop, cellSize, logicalGridBottom-logicalGridTop, ORANGE);
+
+                DrawTextCenter(TextFormat("%d", i), i, logicalGridLabelY, 10, LIGHTGRAY);
+                DrawLine(i, logicalGridLabelY + 10, i, logicalGridBottom, GRAY);
+            }
+
+            odd = true;
+            const int minTextSpace = 30;
+            int lastTextX = -minTextSpace;
+            for (int i = cellSize; i < GetRenderWidth(); i += cellSize, odd = !odd)
+            {
+                int x = (int)(((float)i)/dpiScale.x);
+                if (odd) DrawRectangle(x, pixelGridTop, (int)cellSizePx, pixelGridBottom - pixelGridTop, CLITERAL(Color){ 0, 121, 241, 100 });
+
+                DrawLine(x, pixelGridTop, (int)(((float)i)/dpiScale.x), pixelGridLabelY - 10, GRAY);
+
+                if ((x - lastTextX) >= minTextSpace)
+                {
+                    DrawTextCenter(TextFormat("%d", i), x, pixelGridLabelY, 10, LIGHTGRAY);
+                    lastTextX = x;
+                }
+            }
+
+            DrawTextCenter(TextFormat("Window is %d \"physical pixels\" wide", GetRenderWidth()), windowCenter, pixelGridDescY, 20, BLUE);
+
+            const char *text = "Can you see this?";
+            Vector2 size = MeasureTextEx(GetFontDefault(), text, 20, 3);
+            Vector2 pos = (Vector2){ GetScreenWidth() - size.x - 5, GetScreenHeight() - size.y - 5 };
+            DrawTextEx(GetFontDefault(), text, pos, 20, 3, LIGHTGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color)
+{
+    Vector2 size = MeasureTextEx(GetFontDefault(), text, (float)fontSize, 3);
+    Vector2 pos = (Vector2){ x - size.x/2, y - size.y/2 };
+    DrawTextEx(GetFontDefault(), text, pos, (float)fontSize, 3, color);
+}
diff --git a/raylib/examples/core/core_highdpi_testbed.c b/raylib/examples/core/core_highdpi_testbed.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_highdpi_testbed.c
@@ -0,0 +1,107 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - highdpi testbed
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Ramon Santamaria (@raysan5) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_WINDOW_HIGHDPI);
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - highdpi testbed");
+
+    Vector2 scaleDpi = GetWindowScaleDPI();
+    Vector2 mousePos = GetMousePosition();
+    int currentMonitor = GetCurrentMonitor();
+    Vector2 windowPos = GetWindowPosition();
+
+    int gridSpacing = 40;   // Grid spacing in pixels
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        mousePos = GetMousePosition();
+        currentMonitor = GetCurrentMonitor();
+        scaleDpi = GetWindowScaleDPI();
+        windowPos = GetWindowPosition();
+
+        if (IsKeyPressed(KEY_SPACE)) ToggleBorderlessWindowed();
+        if (IsKeyPressed(KEY_F)) ToggleFullscreen();
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw grid
+            for (int h = 0; h < GetScreenHeight()/gridSpacing + 1; h++)
+            {
+                DrawText(TextFormat("%02i", h*gridSpacing), 4, h*gridSpacing - 4, 10, GRAY);
+                DrawLine(24, h*gridSpacing, GetScreenWidth(), h*gridSpacing, LIGHTGRAY);
+            }
+            for (int v = 0; v < GetScreenWidth()/gridSpacing + 1; v++)
+            {
+                DrawText(TextFormat("%02i", v*gridSpacing), v*gridSpacing - 10, 4, 10, GRAY);
+                DrawLine(v*gridSpacing, 20, v*gridSpacing, GetScreenHeight(), LIGHTGRAY);
+            }
+
+            // Draw UI info
+            DrawText(TextFormat("CURRENT MONITOR: %i/%i (%ix%i)", currentMonitor + 1, GetMonitorCount(), 
+                GetMonitorWidth(currentMonitor), GetMonitorHeight(currentMonitor)), 50, 50, 20, DARKGRAY);
+            DrawText(TextFormat("WINDOW POSITION: %ix%i", (int)windowPos.x, (int)windowPos.y), 50, 90, 20, DARKGRAY);
+            DrawText(TextFormat("SCREEN SIZE: %ix%i", GetScreenWidth(), GetScreenHeight()), 50, 130, 20, DARKGRAY);
+            DrawText(TextFormat("RENDER SIZE: %ix%i", GetRenderWidth(), GetRenderHeight()), 50, 170, 20, DARKGRAY);
+            DrawText(TextFormat("SCALE FACTOR: %.1fx%.1f", scaleDpi.x, scaleDpi.y), 50, 210, 20, GRAY);
+
+            // Draw reference rectangles, top-left and bottom-right corners
+            DrawRectangle(0, 0, 30, 60, RED);
+            DrawRectangle(GetScreenWidth() - 30, GetScreenHeight() - 60, 30, 60, BLUE);
+
+            // Draw mouse position
+            DrawCircleV(GetMousePosition(), 20, MAROON);
+            DrawRectangle(mousePos.x - 25, mousePos.y, 50, 2, BLACK);
+            DrawRectangle(mousePos.x, mousePos.y - 25, 2, 50, BLACK);
+            DrawText(TextFormat("[%i,%i]", GetMouseX(), GetMouseY()), mousePos.x - 44,
+                (mousePos.y > GetScreenHeight() - 60)? mousePos.y - 46 : mousePos.y + 30, 20, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+
+    // TODO: Unload all loaded resources at this point
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_input_actions.c b/raylib/examples/core/core_input_actions.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_input_actions.c
@@ -0,0 +1,203 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - input actions
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Jett (@JettMonstersGoBoom) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jett (@JettMonstersGoBoom)
+*
+********************************************************************************************/
+
+// Simple example for decoding input as actions, allowing remapping of input to different keys or gamepad buttons
+// For example instead of using `IsKeyDown(KEY_LEFT)`, you can use `IsActionDown(ACTION_LEFT)`
+// which can be reassigned to e.g. KEY_A and also assigned to a gamepad button. the action will trigger with either gamepad or keys
+
+#include "raylib.h"
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef enum ActionType {
+    NO_ACTION = 0,
+    ACTION_UP,
+    ACTION_DOWN,
+    ACTION_LEFT,
+    ACTION_RIGHT,
+    ACTION_FIRE,
+    MAX_ACTION
+} ActionType;
+
+// Key and button inputs
+typedef struct ActionInput {
+    int key;
+    int button;
+} ActionInput;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static int gamepadIndex = 0; // Gamepad default index
+static ActionInput actionInputs[MAX_ACTION] = { 0 };
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static bool IsActionPressed(int action);    // Check action key/button pressed
+static bool IsActionReleased(int action);   // Check action key/button released
+static bool IsActionDown(int action);       // Check action key/button down
+
+static void SetActionsDefault(void);        // Set the "default" keyset
+static void SetActionsCursor(void);         // Set the "alternate" keyset
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input actions");
+
+    // Set default actions
+    char actionSet = 0;
+    SetActionsDefault();
+    bool releaseAction = false;
+
+    Vector2 position = (Vector2){ 400.0f, 200.0f };
+    Vector2 size = (Vector2){ 40.0f, 40.0f };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose()) // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        gamepadIndex = 0; //  Set gamepad being checked
+
+        if (IsActionDown(ACTION_UP)) position.y -= 2;
+        if (IsActionDown(ACTION_DOWN)) position.y += 2;
+        if (IsActionDown(ACTION_LEFT)) position.x -= 2;
+        if (IsActionDown(ACTION_RIGHT)) position.x += 2;
+        if (IsActionPressed(ACTION_FIRE))
+        {
+            position.x = (screenWidth-size.x)/2;
+            position.y = (screenHeight-size.y)/2;
+        }
+
+        // Register release action for one frame
+        releaseAction = false;
+        if (IsActionReleased(ACTION_FIRE)) releaseAction = true;
+
+        // Switch control scheme by pressing TAB
+        if (IsKeyPressed(KEY_TAB))
+        {
+            actionSet = !actionSet;
+            if (actionSet == 0) SetActionsDefault();
+            else SetActionsCursor();
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(GRAY);
+
+            DrawRectangleV(position, size, releaseAction? BLUE : RED);
+
+            DrawText((actionSet == 0)? "Current input set: WASD (default)" : "Current input set: Cursor", 10, 10, 20, WHITE);
+            DrawText("Use TAB key to toggles Actions keyset", 10, 50, 20, GREEN);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Check action key/button pressed
+// NOTE: Combines key pressed and gamepad button pressed in one action
+static bool IsActionPressed(int action)
+{
+    bool result = false;
+
+    if (action < MAX_ACTION) result = (IsKeyPressed(actionInputs[action].key) || IsGamepadButtonPressed(gamepadIndex, actionInputs[action].button));
+
+    return result;
+}
+
+// Check action key/button released
+// NOTE: Combines key released and gamepad button released in one action
+static bool IsActionReleased(int action)
+{
+    bool result = false;
+
+    if (action < MAX_ACTION) result = (IsKeyReleased(actionInputs[action].key) || IsGamepadButtonReleased(gamepadIndex, actionInputs[action].button));
+
+    return result;
+}
+
+// Check action key/button down
+// NOTE: Combines key down and gamepad button down in one action
+static bool IsActionDown(int action)
+{
+    bool result = false;
+
+    if (action < MAX_ACTION) result = (IsKeyDown(actionInputs[action].key) || IsGamepadButtonDown(gamepadIndex, actionInputs[action].button));
+
+    return result;
+}
+
+// Set the "default" keyset
+// NOTE: Here WASD and gamepad buttons on the left side for movement
+static void SetActionsDefault(void)
+{
+    actionInputs[ACTION_UP].key = KEY_W;
+    actionInputs[ACTION_DOWN].key = KEY_S;
+    actionInputs[ACTION_LEFT].key = KEY_A;
+    actionInputs[ACTION_RIGHT].key = KEY_D;
+    actionInputs[ACTION_FIRE].key = KEY_SPACE;
+
+    actionInputs[ACTION_UP].button = GAMEPAD_BUTTON_LEFT_FACE_UP;
+    actionInputs[ACTION_DOWN].button = GAMEPAD_BUTTON_LEFT_FACE_DOWN;
+    actionInputs[ACTION_LEFT].button = GAMEPAD_BUTTON_LEFT_FACE_LEFT;
+    actionInputs[ACTION_RIGHT].button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT;
+    actionInputs[ACTION_FIRE].button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
+}
+
+// Set the "alternate" keyset
+// NOTE: Here cursor keys and gamepad buttons on the right side for movement
+static void SetActionsCursor(void)
+{
+    actionInputs[ACTION_UP].key = KEY_UP;
+    actionInputs[ACTION_DOWN].key = KEY_DOWN;
+    actionInputs[ACTION_LEFT].key = KEY_LEFT;
+    actionInputs[ACTION_RIGHT].key = KEY_RIGHT;
+    actionInputs[ACTION_FIRE].key = KEY_SPACE;
+
+    actionInputs[ACTION_UP].button = GAMEPAD_BUTTON_RIGHT_FACE_UP;
+    actionInputs[ACTION_DOWN].button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN;
+    actionInputs[ACTION_LEFT].button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT;
+    actionInputs[ACTION_RIGHT].button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT;
+    actionInputs[ACTION_FIRE].button = GAMEPAD_BUTTON_LEFT_FACE_DOWN;
+}
diff --git a/raylib/examples/core/core_input_gamepad.c b/raylib/examples/core/core_input_gamepad.c
--- a/raylib/examples/core/core_input_gamepad.c
+++ b/raylib/examples/core/core_input_gamepad.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Gamepad input
+*   raylib [core] example - input gamepad
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: This example requires a Gamepad connected to the system
 *         raylib is configured to work with the following gamepads:
 *                - Xbox 360 Controller (Xbox 360, Xbox One)
@@ -13,16 +15,16 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 
 // NOTE: Gamepad name ID depends on drivers and OS
-#define XBOX360_LEGACY_NAME_ID  "Xbox Controller"
-#define XBOX360_NAME_ID     "Xbox 360 Controller"
-#define PS3_NAME_ID         "Sony PLAYSTATION(R)3 Controller"
+#define XBOX_ALIAS_1 "xbox"
+#define XBOX_ALIAS_2 "x-box"
+#define PS_ALIAS     "playstation"
 
 //------------------------------------------------------------------------------------
 // Program main entry point
@@ -36,11 +38,21 @@
 
     SetConfigFlags(FLAG_MSAA_4X_HINT);  // Set MSAA 4X hint before windows creation
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad input");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input gamepad");
 
     Texture2D texPs3Pad = LoadTexture("resources/ps3.png");
     Texture2D texXboxPad = LoadTexture("resources/xbox.png");
 
+    // Set axis deadzones
+    const float leftStickDeadzoneX = 0.1f;
+    const float leftStickDeadzoneY = 0.1f;
+    const float rightStickDeadzoneX = 0.1f;
+    const float rightStickDeadzoneY = 0.1f;
+    const float leftTriggerDeadzone = -0.9f;
+    const float rightTriggerDeadzone = -0.9f;
+
+    Rectangle vibrateButton = { 0 };
+
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
@@ -51,7 +63,12 @@
     {
         // Update
         //----------------------------------------------------------------------------------
-        // ...
+        if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--;
+        if (IsKeyPressed(KEY_RIGHT)) gamepad++;
+        Vector2 mousePosition = GetMousePosition();
+
+        vibrateButton = (Rectangle){ 10, 70.0f + 20*GetGamepadAxisCount(gamepad) + 20, 75, 24 };
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && CheckCollisionPointRec(mousePosition, vibrateButton)) SetGamepadVibration(gamepad, 1.0, 1.0, 1.0);
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -60,14 +77,28 @@
 
             ClearBackground(RAYWHITE);
 
-            if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--;
-            if (IsKeyPressed(KEY_RIGHT)) gamepad++;
-
             if (IsGamepadAvailable(gamepad))
             {
                 DrawText(TextFormat("GP%d: %s", gamepad, GetGamepadName(gamepad)), 10, 10, 10, BLACK);
 
-                if (TextIsEqual(GetGamepadName(gamepad), XBOX360_LEGACY_NAME_ID) || TextIsEqual(GetGamepadName(gamepad), XBOX360_NAME_ID))
+                // Get axis values
+                float leftStickX = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X);
+                float leftStickY = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y);
+                float rightStickX = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X);
+                float rightStickY = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y);
+                float leftTrigger = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER);
+                float rightTrigger = GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER);
+
+                // Calculate deadzones
+                if (leftStickX > -leftStickDeadzoneX && leftStickX < leftStickDeadzoneX) leftStickX = 0.0f;
+                if (leftStickY > -leftStickDeadzoneY && leftStickY < leftStickDeadzoneY) leftStickY = 0.0f;
+                if (rightStickX > -rightStickDeadzoneX && rightStickX < rightStickDeadzoneX) rightStickX = 0.0f;
+                if (rightStickY > -rightStickDeadzoneY && rightStickY < rightStickDeadzoneY) rightStickY = 0.0f;
+                if (leftTrigger < leftTriggerDeadzone) leftTrigger = -1.0f;
+                if (rightTrigger < rightTriggerDeadzone) rightTrigger = -1.0f;
+
+                if ((TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_1) > -1) ||
+                    (TextFindIndex(TextToLower(GetGamepadName(gamepad)), XBOX_ALIAS_2) > -1))
                 {
                     DrawTexture(texXboxPad, 0, 0, DARKGRAY);
 
@@ -95,32 +126,29 @@
                     if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(536, 61, 20, RED);
 
                     // Draw axis: left joystick
-
                     Color leftGamepadColor = BLACK;
                     if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
                     DrawCircle(259, 152, 39, BLACK);
                     DrawCircle(259, 152, 34, LIGHTGRAY);
-                    DrawCircle(259 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X)*20),
-                               152 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y)*20), 25, leftGamepadColor);
+                    DrawCircle(259 + (int)(leftStickX*20), 152 + (int)(leftStickY*20), 25, leftGamepadColor);
 
                     // Draw axis: right joystick
                     Color rightGamepadColor = BLACK;
                     if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
                     DrawCircle(461, 237, 38, BLACK);
                     DrawCircle(461, 237, 33, LIGHTGRAY);
-                    DrawCircle(461 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X)*20),
-                               237 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y)*20), 25, rightGamepadColor);
+                    DrawCircle(461 + (int)(rightStickX*20), 237 + (int)(rightStickY*20), 25, rightGamepadColor);
 
                     // Draw axis: left-right triggers
                     DrawRectangle(170, 30, 15, 70, GRAY);
                     DrawRectangle(604, 30, 15, 70, GRAY);
-                    DrawRectangle(170, 30, 15, (int)(((1 + GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER))/2)*70), RED);
-                    DrawRectangle(604, 30, 15, (int)(((1 + GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER))/2)*70), RED);
+                    DrawRectangle(170, 30, 15, (int)(((1 + leftTrigger)/2)*70), RED);
+                    DrawRectangle(604, 30, 15, (int)(((1 + rightTrigger)/2)*70), RED);
 
                     //DrawText(TextFormat("Xbox axis LT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 40, 10, BLACK);
                     //DrawText(TextFormat("Xbox axis RT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 60, 10, BLACK);
                 }
-                else if (TextIsEqual(GetGamepadName(gamepad), PS3_NAME_ID))
+                else if (TextFindIndex(TextToLower(GetGamepadName(gamepad)), PS_ALIAS) > -1)
                 {
                     DrawTexture(texPs3Pad, 0, 0, DARKGRAY);
 
@@ -150,46 +178,98 @@
                     // Draw axis: left joystick
                     Color leftGamepadColor = BLACK;
                     if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
-                    DrawCircle(319, 255, 35, leftGamepadColor);
+                    DrawCircle(319, 255, 35, BLACK);
                     DrawCircle(319, 255, 31, LIGHTGRAY);
-                    DrawCircle(319 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X) * 20),
-                               255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y) * 20), 25, leftGamepadColor);
+                    DrawCircle(319 + (int)(leftStickX*20), 255 + (int)(leftStickY*20), 25, leftGamepadColor);
 
                     // Draw axis: right joystick
                     Color rightGamepadColor = BLACK;
                     if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
                     DrawCircle(475, 255, 35, BLACK);
                     DrawCircle(475, 255, 31, LIGHTGRAY);
-                    DrawCircle(475 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X) * 20),
-                               255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y) * 20), 25, rightGamepadColor);
+                    DrawCircle(475 + (int)(rightStickX*20), 255 + (int)(rightStickY*20), 25, rightGamepadColor);
 
                     // Draw axis: left-right triggers
                     DrawRectangle(169, 48, 15, 70, GRAY);
                     DrawRectangle(611, 48, 15, 70, GRAY);
-                    DrawRectangle(169, 48, 15, (int)(((1 - GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER)) / 2) * 70), RED);
-                    DrawRectangle(611, 48, 15, (int)(((1 - GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER)) / 2) * 70), RED);
+                    DrawRectangle(169, 48, 15, (int)(((1 + leftTrigger)/2)*70), RED);
+                    DrawRectangle(611, 48, 15, (int)(((1 + rightTrigger)/2)*70), RED);
                 }
                 else
                 {
-                    DrawText("- GENERIC GAMEPAD -", 280, 180, 20, GRAY);
+                    // Draw background: generic
+                    DrawRectangleRounded((Rectangle){ 175, 110, 460, 220}, 0.3f, 16, DARKGRAY);
 
-                    // TODO: Draw generic gamepad
+                    // Draw buttons: basic
+                    DrawCircle(365, 170, 12, RAYWHITE);
+                    DrawCircle(405, 170, 12, RAYWHITE);
+                    DrawCircle(445, 170, 12, RAYWHITE);
+                    DrawCircle(516, 191, 17, RAYWHITE);
+                    DrawCircle(551, 227, 17, RAYWHITE);
+                    DrawCircle(587, 191, 17, RAYWHITE);
+                    DrawCircle(551, 155, 17, RAYWHITE);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawCircle(365, 170, 10, RED);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(405, 170, 10, GREEN);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawCircle(445, 170, 10, BLUE);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(516, 191, 15, GOLD);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(551, 227, 15, BLUE);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(587, 191, 15, GREEN);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(551, 155, 15, RED);
+
+                    // Draw buttons: d-pad
+                    DrawRectangle(245, 145, 28, 88, RAYWHITE);
+                    DrawRectangle(215, 174, 88, 29, RAYWHITE);
+                    DrawRectangle(247, 147, 24, 84, BLACK);
+                    DrawRectangle(217, 176, 84, 25, BLACK);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(247, 147, 24, 29, RED);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(247, 147 + 54, 24, 30, RED);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(217, 176, 30, 25, RED);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(217 + 54, 176, 30, 25, RED);
+
+                    // Draw buttons: left-right back
+                    DrawRectangleRounded((Rectangle){ 215, 98, 100, 10}, 0.5f, 16, DARKGRAY);
+                    DrawRectangleRounded((Rectangle){ 495, 98, 100, 10}, 0.5f, 16, DARKGRAY);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawRectangleRounded((Rectangle){ 215, 98, 100, 10}, 0.5f, 16, RED);
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawRectangleRounded((Rectangle){ 495, 98, 100, 10}, 0.5f, 16, RED);
+
+                    // Draw axis: left joystick
+                    Color leftGamepadColor = BLACK;
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED;
+                    DrawCircle(345, 260, 40, BLACK);
+                    DrawCircle(345, 260, 35, LIGHTGRAY);
+                    DrawCircle(345 + (int)(leftStickX*20), 260 + (int)(leftStickY*20), 25, leftGamepadColor);
+
+                    // Draw axis: right joystick
+                    Color rightGamepadColor = BLACK;
+                    if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED;
+                    DrawCircle(465, 260, 40, BLACK);
+                    DrawCircle(465, 260, 35, LIGHTGRAY);
+                    DrawCircle(465 + (int)(rightStickX*20), 260 + (int)(rightStickY*20), 25, rightGamepadColor);
+
+                    // Draw axis: left-right triggers
+                    DrawRectangle(151, 110, 15, 70, GRAY);
+                    DrawRectangle(644, 110, 15, 70, GRAY);
+                    DrawRectangle(151, 110, 15, (int)(((1 + leftTrigger)/2)*70), RED);
+                    DrawRectangle(644, 110, 15, (int)(((1 + rightTrigger)/2)*70), RED);
                 }
 
-                DrawText(TextFormat("DETECTED AXIS [%i]:", GetGamepadAxisCount(0)), 10, 50, 10, MAROON);
+                DrawText(TextFormat("DETECTED AXIS [%i]:", GetGamepadAxisCount(gamepad)), 10, 50, 10, MAROON);
 
-                for (int i = 0; i < GetGamepadAxisCount(0); i++)
+                for (int i = 0; i < GetGamepadAxisCount(gamepad); i++)
                 {
-                    DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(0, i)), 20, 70 + 20*i, 10, DARKGRAY);
+                    DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(gamepad, i)), 20, 70 + 20*i, 10, DARKGRAY);
                 }
 
+                // Draw vibrate button
+                DrawRectangleRec(vibrateButton, SKYBLUE);
+                DrawText("VIBRATE", (int)(vibrateButton.x + 14), (int)(vibrateButton.y + 1), 10, DARKGRAY);
+
                 if (GetGamepadButtonPressed() != GAMEPAD_BUTTON_UNKNOWN) DrawText(TextFormat("DETECTED BUTTON: %i", GetGamepadButtonPressed()), 10, 430, 10, RED);
                 else DrawText("DETECTED BUTTON: NONE", 10, 430, 10, GRAY);
             }
             else
             {
                 DrawText(TextFormat("GP%d: NOT DETECTED", gamepad), 10, 10, 10, GRAY);
-
                 DrawTexture(texXboxPad, 0, 0, LIGHTGRAY);
             }
 
diff --git a/raylib/examples/core/core_input_gamepad_info.c b/raylib/examples/core/core_input_gamepad_info.c
deleted file mode 100644
--- a/raylib/examples/core/core_input_gamepad_info.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [core] example - Gamepad information
-*
-*   NOTE: This example requires a Gamepad connected to the system
-*         Check raylib.h for buttons configuration
-*
-*   Example originally created with raylib 4.6, last time updated with raylib 4.6
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT);  // Set MSAA 4X hint before windows creation
-
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad information");
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // TODO: Update your variables here
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            for (int i = 0, y = 5; i < 4; i++)     // MAX_GAMEPADS = 4
-            {
-                if (IsGamepadAvailable(i))
-                {
-                    DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 10, BLACK);
-                    y += 11;
-                    DrawText(TextFormat("\tAxis count:   %d", GetGamepadAxisCount(i)), 10, y, 10, BLACK);
-                    y += 11;
-
-                    for (int axis = 0; axis < GetGamepadAxisCount(i); axis++)
-                    {
-                        DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 10, BLACK);
-                        y += 11;
-                    }
-
-                    for (int button = 0; button < 32; button++)
-                    {
-                        DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 10, BLACK);
-                        y += 11;
-                    }
-                }
-            }
-
-            DrawFPS(GetScreenWidth() - 100, 100);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-}
diff --git a/raylib/examples/core/core_input_gestures.c b/raylib/examples/core/core_input_gestures.c
--- a/raylib/examples/core/core_input_gestures.c
+++ b/raylib/examples/core/core_input_gestures.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Input Gestures Detection
+*   raylib [core] example - input gestures
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.4, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -116,4 +118,6 @@
     //--------------------------------------------------------------------------------------
     CloseWindow();        // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
+
+    return 0;
 }
diff --git a/raylib/examples/core/core_input_gestures_testbed.c b/raylib/examples/core/core_input_gestures_testbed.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_input_gestures_testbed.c
@@ -0,0 +1,315 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - input gestures testbed
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 5.6-dev
+*
+*   Example contributed by ubkp (@ubkp) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2023-2025 ubkp (@ubkp)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <math.h>       // Required for the protractor angle graphic drawing
+
+#define GESTURE_LOG_SIZE    20
+#define MAX_TOUCH_COUNT     32
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static char const *GetGestureName(int gesture); // Get text string for gesture value
+static Color GetGestureColor(int gesture); // Get color for gesture value
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures testbed");
+
+    Vector2 messagePosition = { 160, 7 };
+
+    // Last gesture variables definitions
+    int lastGesture = 0;
+    Vector2 lastGesturePosition = { 165, 130 };
+
+    // Gesture log variables definitions
+    // NOTE: The gesture log uses an array (as an inverted circular queue) to store the performed gestures
+    char gestureLog[GESTURE_LOG_SIZE][12] = { "" };
+    // NOTE: The index for the inverted circular queue (moving from last to first direction, then looping around)
+    int gestureLogIndex = GESTURE_LOG_SIZE;
+    int previousGesture = 0;
+
+    // Log mode values:
+    // - 0 shows repeated events
+    // - 1 hides repeated events
+    // - 2 shows repeated events but hide hold events
+    // - 3 hides repeated events and hide hold events
+    int logMode = 1;
+
+    Color gestureColor = { 0, 0, 0, 255 };
+    Rectangle logButton1 = { 53, 7, 48, 26 };
+    Rectangle logButton2 = { 108, 7, 36, 26 };
+    Vector2 gestureLogPosition = { 10, 10 };
+
+    // Protractor variables definitions
+    float angleLength = 90.0f;
+    float currentAngleDegrees = 0.0f;
+    Vector2 finalVector = { 0.0f, 0.0f };
+    Vector2 protractorPosition = { 266.0f, 315.0f };
+
+    SetTargetFPS(60);   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //--------------------------------------------------------------------------------------
+        // Handle common gestures data
+        int i, ii; // Iterators that will be reused by all for loops
+        const int currentGesture = GetGestureDetected();
+        const float currentDragDegrees = GetGestureDragAngle();
+        const float currentPitchDegrees = GetGesturePinchAngle();
+        const int touchCount = GetTouchPointCount();
+
+        // Handle last gesture
+        if ((currentGesture != 0) && (currentGesture != 4) && (currentGesture != previousGesture))
+            lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display
+
+        // Handle gesture log
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+        {
+            if (CheckCollisionPointRec(GetMousePosition(), logButton1))
+            {
+                switch (logMode)
+                {
+                    case 3: logMode = 2; break;
+                    case 2: logMode = 3; break;
+                    case 1: logMode = 0; break;
+                    default: logMode = 1; break;
+                }
+            }
+            else if (CheckCollisionPointRec(GetMousePosition(), logButton2))
+            {
+                switch (logMode)
+                {
+                    case 3: logMode = 1; break;
+                    case 2: logMode = 0; break;
+                    case 1: logMode = 3; break;
+                    default: logMode = 2; break;
+                }
+            }
+        }
+
+        int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled
+        if (currentGesture !=0)
+        {
+            if (logMode == 3) // 3 hides repeated events and hide hold events
+            {
+                if (((currentGesture != 4) && (currentGesture != previousGesture)) || (currentGesture < 3)) fillLog = 1;
+            }
+            else if (logMode == 2) // 2 shows repeated events but hide hold events
+            {
+                if (currentGesture != 4) fillLog = 1;
+            }
+            else if (logMode == 1) // 1 hides repeated events
+            {
+                if (currentGesture != previousGesture) fillLog = 1;
+            }
+            else  // 0 shows repeated events
+            {
+                fillLog = 1;
+            }
+        }
+
+        if (fillLog) // If one of the conditions from logMode was met, fill the gesture log
+        {
+            previousGesture = currentGesture;
+            gestureColor = GetGestureColor(currentGesture);
+            if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE;
+            gestureLogIndex--;
+
+            // Copy the gesture respective name to the gesture log array
+            TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture));
+        }
+
+        // Handle protractor
+        if (currentGesture > 255) currentAngleDegrees = currentPitchDegrees; // Pinch In and Pinch Out
+        else if (currentGesture > 15) currentAngleDegrees = currentDragDegrees; // Swipe Right, Swipe Left, Swipe Up and Swipe Down
+        else if (currentGesture > 0) currentAngleDegrees = 0.0f; // Tap, Doubletap, Hold and Grab
+
+        float currentAngleRadians = ((currentAngleDegrees + 90.0f)*PI/180); // Convert the current angle to Radians
+        // Calculate the final vector for display
+        finalVector = (Vector2){ (angleLength*sinf(currentAngleRadians)) + protractorPosition.x,
+            (angleLength*cosf(currentAngleRadians)) + protractorPosition.y };
+
+        // Handle touch and mouse pointer points
+        Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 };
+        Vector2 mousePosition = { 0 };
+        if (currentGesture != GESTURE_NONE)
+        {
+            if (touchCount != 0)
+            {
+                for (i = 0; i < touchCount; i++) touchPosition[i] = GetTouchPosition(i); // Fill the touch positions
+            }
+            else mousePosition = GetMousePosition();
+        }
+        //--------------------------------------------------------------------------------------
+
+        // Draw
+        //--------------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw common elements
+            DrawText("*", (int)messagePosition.x + 5, (int)messagePosition.y + 5, 10, BLACK);
+            DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", (int)messagePosition.x + 15, (int)messagePosition.y + 5, 10, BLACK);
+            DrawText("*", (int)messagePosition.x + 5, (int)messagePosition.y + 35, 10, BLACK);
+            DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.", (int)messagePosition.x + 15, (int)messagePosition.y + 35, 10, BLACK);
+
+            // Draw last gesture
+            DrawText("Last gesture", (int)lastGesturePosition.x + 33, (int)lastGesturePosition.y - 47, 20, BLACK);
+            DrawText("Swipe         Tap       Pinch  Touch", (int)lastGesturePosition.x + 17, (int)lastGesturePosition.y - 18, 10, BLACK);
+            DrawRectangle((int)lastGesturePosition.x + 20, (int)lastGesturePosition.y, 20, 20, lastGesture == GESTURE_SWIPE_UP ? RED : LIGHTGRAY);
+            DrawRectangle((int)lastGesturePosition.x, (int)lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_LEFT ? RED : LIGHTGRAY);
+            DrawRectangle((int)lastGesturePosition.x + 40, (int)lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_RIGHT ? RED : LIGHTGRAY);
+            DrawRectangle((int)lastGesturePosition.x + 20, (int)lastGesturePosition.y + 40, 20, 20, lastGesture == GESTURE_SWIPE_DOWN ? RED : LIGHTGRAY);
+            DrawCircle((int)lastGesturePosition.x + 80, (int)lastGesturePosition.y + 16, 10, lastGesture == GESTURE_TAP ? BLUE : LIGHTGRAY);
+            DrawRing( (Vector2){lastGesturePosition.x + 103, lastGesturePosition.y + 16}, 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == GESTURE_DRAG ? LIME : LIGHTGRAY);
+            DrawCircle((int)lastGesturePosition.x + 80, (int)lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY);
+            DrawCircle((int)lastGesturePosition.x + 103, (int)lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY);
+            DrawTriangle((Vector2){ lastGesturePosition.x + 122, lastGesturePosition.y + 16 }, (Vector2){ lastGesturePosition.x + 137, lastGesturePosition.y + 26 }, (Vector2){lastGesturePosition.x + 137, lastGesturePosition.y + 6 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY);
+            DrawTriangle((Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 6 }, (Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 26 }, (Vector2){ lastGesturePosition.x + 162, lastGesturePosition.y + 16 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY);
+            DrawTriangle((Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 33 }, (Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 140, lastGesturePosition.y + 43 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY);
+            DrawTriangle((Vector2){ lastGesturePosition.x + 144, lastGesturePosition.y + 43 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 33 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY);
+            for (i = 0; i < 4; i++) DrawCircle((int)lastGesturePosition.x + 180, (int)lastGesturePosition.y + 7 + i*15, 5, touchCount <= i? LIGHTGRAY : gestureColor);
+
+            // Draw gesture log
+            DrawText("Log", (int)gestureLogPosition.x, (int)gestureLogPosition.y, 20, BLACK);
+
+            // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle)
+            for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1)%GESTURE_LOG_SIZE) DrawText(gestureLog[ii], (int)gestureLogPosition.x, (int)gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY));
+            Color logButton1Color, logButton2Color;
+            switch (logMode)
+            {
+                case 3:  logButton1Color=MAROON; logButton2Color=MAROON; break;
+                case 2:  logButton1Color=GRAY;   logButton2Color=MAROON; break;
+                case 1:  logButton1Color=MAROON; logButton2Color=GRAY;   break;
+                default: logButton1Color=GRAY;   logButton2Color=GRAY;   break;
+            }
+            DrawRectangleRec(logButton1, logButton1Color);
+            DrawText("Hide", (int)logButton1.x + 7, (int)logButton1.y + 3, 10, WHITE);
+            DrawText("Repeat", (int)logButton1.x + 7, (int)logButton1.y + 13, 10, WHITE);
+            DrawRectangleRec(logButton2, logButton2Color);
+            DrawText("Hide", (int)logButton1.x + 62, (int)logButton1.y + 3, 10, WHITE);
+            DrawText("Hold", (int)logButton1.x + 62, (int)logButton1.y + 13, 10, WHITE);
+
+            // Draw protractor
+            DrawText("Angle", (int)protractorPosition.x + 55, (int)protractorPosition.y + 76, 10, BLACK);
+            const char *angleString = TextFormat("%f", currentAngleDegrees);
+            const int angleStringDot = TextFindIndex(angleString, ".");
+            const char *angleStringTrim = TextSubtext(angleString, 0, angleStringDot + 3);
+            DrawText( angleStringTrim, (int)protractorPosition.x + 55, (int)protractorPosition.y + 92, 20, gestureColor);
+            DrawCircleV(protractorPosition, 80.0f, WHITE);
+            DrawLineEx((Vector2){ protractorPosition.x - 90, protractorPosition.y }, (Vector2){ protractorPosition.x + 90, protractorPosition.y }, 3.0f, LIGHTGRAY);
+            DrawLineEx((Vector2){ protractorPosition.x, protractorPosition.y - 90 }, (Vector2){ protractorPosition.x, protractorPosition.y + 90 }, 3.0f, LIGHTGRAY);
+            DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y - 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y + 45 }, 3.0f, GREEN);
+            DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y + 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y - 45 }, 3.0f, GREEN);
+            DrawText("0", (int)protractorPosition.x + 96, (int)protractorPosition.y - 9, 20, BLACK);
+            DrawText("30", (int)protractorPosition.x + 74, (int)protractorPosition.y - 68, 20, BLACK);
+            DrawText("90", (int)protractorPosition.x - 11, (int)protractorPosition.y - 110, 20, BLACK);
+            DrawText("150", (int)protractorPosition.x - 100, (int)protractorPosition.y - 68, 20, BLACK);
+            DrawText("180", (int)protractorPosition.x - 124, (int)protractorPosition.y - 9, 20, BLACK);
+            DrawText("210", (int)protractorPosition.x - 100, (int)protractorPosition.y + 50, 20, BLACK);
+            DrawText("270", (int)protractorPosition.x - 18, (int)protractorPosition.y + 92, 20, BLACK);
+            DrawText("330", (int)protractorPosition.x + 72, (int)protractorPosition.y + 50, 20, BLACK);
+            if (currentAngleDegrees != 0.0f) DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor);
+
+            // Draw touch and mouse pointer points
+            if (currentGesture != GESTURE_NONE)
+            {
+                if ( touchCount != 0 )
+                {
+                    for (i = 0; i < touchCount; i++)
+                    {
+                        DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f));
+                        DrawCircleV(touchPosition[i], 5.0f, gestureColor);
+                    }
+
+                    if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8.0f : 12.0f), gestureColor);
+                }
+                else
+                {
+                    DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f));
+                    DrawCircleV(mousePosition, 5.0f, gestureColor);
+                }
+            }
+
+        EndDrawing();
+        //--------------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Get text string for gesture value
+static char const *GetGestureName(int gesture)
+{
+    switch (gesture)
+    {
+        case 0: return "None"; break;
+        case 1: return "Tap"; break;
+        case 2: return "Double Tap"; break;
+        case 4: return "Hold"; break;
+        case 8: return "Drag"; break;
+        case 16: return "Swipe Right"; break;
+        case 32: return "Swipe Left"; break;
+        case 64: return "Swipe Up"; break;
+        case 128: return "Swipe Down"; break;
+        case 256: return "Pinch In"; break;
+        case 512: return "Pinch Out"; break;
+        default:  return "Unknown"; break;
+    }
+}
+
+// Get color for gesture value
+static Color GetGestureColor(int gesture)
+{
+    switch (gesture)
+    {
+        case 0: return BLACK; break;
+        case 1: return BLUE; break;
+        case 2: return SKYBLUE; break;
+        case 4: return BLACK; break;
+        case 8: return LIME; break;
+        case 16: return RED; break;
+        case 32: return RED; break;
+        case 64: return RED; break;
+        case 128: return RED; break;
+        case 256: return VIOLET; break;
+        case 512: return ORANGE; break;
+        default: return BLACK; break;
+    }
+}
diff --git a/raylib/examples/core/core_input_gestures_web.c b/raylib/examples/core/core_input_gestures_web.c
deleted file mode 100644
--- a/raylib/examples/core/core_input_gestures_web.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [core] example - Input Gestures for Web
-*
-*   Example originally created with raylib 4.6-dev, last time updated with raylib 4.6-dev
-*
-*   Example contributed by ubkp (@ubkp) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2023 ubkp (@ubkp)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "math.h"       // Required for the protractor angle graphic drawing
-
-#if defined(PLATFORM_WEB)
-    #include <emscripten/emscripten.h> // Required for the Web/HTML5
-#endif
-
-//--------------------------------------------------------------------------------------
-// Global definitions and declarations
-//--------------------------------------------------------------------------------------
-
-// Common variables definitions
-//--------------------------------------------------------------------------------------
-int screenWidth = 800;                  // Update depending on web canvas
-const int screenHeight = 450;
-Vector2 messagePosition = { 160, 7 };
-
-// Last gesture variables definitions
-//--------------------------------------------------------------------------------------
-int lastGesture = 0;
-Vector2 lastGesturePosition = { 165, 130 };
-
-// Gesture log variables definitions and functions declarations
-//--------------------------------------------------------------------------------------
-#define GESTURE_LOG_SIZE 20
-char gestureLog[GESTURE_LOG_SIZE][12] = { "" }; // The gesture log uses an array (as an inverted circular queue) to store the performed gestures
-int gestureLogIndex = GESTURE_LOG_SIZE;         // The index for the inverted circular queue (moving from last to first direction, then looping around)
-int previousGesture = 0;
-
-char const *GetGestureName(int i)
-{
-   switch (i)  {
-      case 0:   return "None";        break;
-      case 1:   return "Tap";         break;
-      case 2:   return "Double Tap";  break;
-      case 4:   return "Hold";        break;
-      case 8:   return "Drag";        break;
-      case 16:  return "Swipe Right"; break;
-      case 32:  return "Swipe Left";  break;
-      case 64:  return "Swipe Up";    break;
-      case 128: return "Swipe Down";  break;
-      case 256: return "Pinch In";    break;
-      case 512: return "Pinch Out";   break;
-      default:  return "Unknown";     break;
-   }
-}
-
-Color GetGestureColor(int i)
-{
-   switch (i)  {
-      case 0:   return BLACK;   break;
-      case 1:   return BLUE;    break;
-      case 2:   return SKYBLUE; break;
-      case 4:   return BLACK;   break;
-      case 8:   return LIME;    break;
-      case 16:  return RED;     break;
-      case 32:  return RED;     break;
-      case 64:  return RED;     break;
-      case 128: return RED;     break;
-      case 256: return VIOLET;  break;
-      case 512: return ORANGE;  break;
-      default:  return BLACK;   break;
-   }
-}
-
-int logMode = 1; // Log mode values: 0 shows repeated events; 1 hides repeated events; 2 shows repeated events but hide hold events; 3 hides repeated events and hide hold events
-
-Color gestureColor = { 0, 0, 0, 255 };
-Rectangle logButton1 = { 53, 7, 48, 26 };
-Rectangle logButton2 = { 108, 7, 36, 26 };
-Vector2 gestureLogPosition = { 10, 10 };
-
-// Protractor variables definitions
-//--------------------------------------------------------------------------------------
-float angleLength = 90.0f;
-float currentAngleDegrees = 0.0f;
-Vector2 finalVector = { 0.0f, 0.0f };
-char currentAngleStr[7] = "";
-Vector2 protractorPosition = { 266.0f, 315.0f };
-
-// Update
-//--------------------------------------------------------------------------------------
-void Update(void)
-{
-    // Handle common
-    //--------------------------------------------------------------------------------------
-    int i, ii; // Iterators that will be reused by all for loops
-    const int currentGesture = GetGestureDetected();
-    const float currentDragDegrees = GetGestureDragAngle();
-    const float currentPitchDegrees = GetGesturePinchAngle();
-    const int touchCount = GetTouchPointCount();
-
-    // Handle last gesture
-    //--------------------------------------------------------------------------------------
-    if ((currentGesture != 0) && (currentGesture != 4) && (currentGesture != previousGesture)) lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display
-
-    // Handle gesture log
-    //--------------------------------------------------------------------------------------
-    if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
-    {
-        if (CheckCollisionPointRec(GetMousePosition(), logButton1))
-        {
-            switch (logMode)
-            {
-                case 3:  logMode=2; break;
-                case 2:  logMode=3; break;
-                case 1:  logMode=0; break;
-                default: logMode=1; break;
-            }
-        }
-        else if (CheckCollisionPointRec(GetMousePosition(), logButton2))
-        {
-            switch (logMode)
-            {
-                case 3:  logMode=1; break;
-                case 2:  logMode=0; break;
-                case 1:  logMode=3; break;
-                default: logMode=2; break;
-            }
-        }
-    }
-
-    int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled
-    if (currentGesture !=0)
-    {
-        if (logMode == 3) // 3 hides repeated events and hide hold events
-        {
-            if (((currentGesture != 4) && (currentGesture != previousGesture)) || (currentGesture < 3)) fillLog = 1;
-        }
-        else if (logMode == 2) // 2 shows repeated events but hide hold events
-        {
-            if (currentGesture != 4) fillLog = 1;
-        }
-        else if (logMode == 1) // 1 hides repeated events
-        {
-            if (currentGesture != previousGesture) fillLog = 1;
-        }
-        else  // 0 shows repeated events
-        {
-            fillLog = 1;
-        }
-    }
-
-    if (fillLog) // If one of the conditions from logMode was met, fill the gesture log
-    {
-        previousGesture = currentGesture;
-        gestureColor = GetGestureColor(currentGesture);
-        if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE;
-        gestureLogIndex--;
-
-        // Copy the gesture respective name to the gesture log array
-        TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture));
-    }
-
-    // Handle protractor
-    //--------------------------------------------------------------------------------------
-    if (currentGesture > 255) // aka Pinch In and Pinch Out
-    {
-        currentAngleDegrees = currentPitchDegrees;
-    }
-    else if (currentGesture > 15) // aka Swipe Right, Swipe Left, Swipe Up and Swipe Down
-    {
-        currentAngleDegrees = currentDragDegrees;
-    }
-    else if (currentGesture > 0) // aka Tap, Doubletap, Hold and Grab
-    {
-        currentAngleDegrees = 0.0f;
-    }
-
-    float currentAngleRadians = ((currentAngleDegrees +90.0f)*PI/180); // Convert the current angle to Radians
-    finalVector = (Vector2){ (angleLength*sinf(currentAngleRadians)) + protractorPosition.x, (angleLength*cosf(currentAngleRadians)) + protractorPosition.y }; // Calculate the final vector for display
-
-    // Handle touch and mouse pointer points
-    //--------------------------------------------------------------------------------------
-    #define MAX_TOUCH_COUNT     32
-
-    Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 };
-    Vector2 mousePosition = {0, 0};
-    if (currentGesture != GESTURE_NONE)
-    {
-        if (touchCount != 0)
-        {
-            for (i = 0; i < touchCount; i++) touchPosition[i] = GetTouchPosition(i); // Fill the touch positions
-        }
-        else mousePosition = GetMousePosition();
-    }
-
-    // Draw
-    //--------------------------------------------------------------------------------------
-    BeginDrawing();
-
-        ClearBackground(RAYWHITE);
-
-        // Draw common
-        //--------------------------------------------------------------------------------------
-        DrawText("*", messagePosition.x + 5, messagePosition.y + 5, 10, BLACK);
-        DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", messagePosition.x + 15, messagePosition.y + 5, 10, BLACK);
-        DrawText("*", messagePosition.x + 5, messagePosition.y + 35, 10, BLACK);
-        DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.", messagePosition.x + 15,  messagePosition.y + 35, 10, BLACK);
-
-        // Draw last gesture
-        //--------------------------------------------------------------------------------------
-        DrawText("Last gesture", lastGesturePosition.x + 33, lastGesturePosition.y - 47, 20, BLACK);
-        DrawText("Swipe         Tap       Pinch  Touch", lastGesturePosition.x + 17, lastGesturePosition.y - 18, 10, BLACK);
-        DrawRectangle(lastGesturePosition.x + 20, lastGesturePosition.y, 20, 20, lastGesture == GESTURE_SWIPE_UP ? RED : LIGHTGRAY);
-        DrawRectangle(lastGesturePosition.x, lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_LEFT ? RED : LIGHTGRAY);
-        DrawRectangle(lastGesturePosition.x + 40, lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_RIGHT ? RED : LIGHTGRAY);
-        DrawRectangle(lastGesturePosition.x + 20, lastGesturePosition.y + 40, 20, 20, lastGesture == GESTURE_SWIPE_DOWN ? RED : LIGHTGRAY);
-        DrawCircle(lastGesturePosition.x + 80, lastGesturePosition.y + 16, 10, lastGesture == GESTURE_TAP ? BLUE : LIGHTGRAY);
-        DrawRing( (Vector2){lastGesturePosition.x + 103, lastGesturePosition.y + 16}, 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == GESTURE_DRAG ? LIME : LIGHTGRAY);
-        DrawCircle(lastGesturePosition.x + 80, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY);
-        DrawCircle(lastGesturePosition.x + 103, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY);
-        DrawTriangle((Vector2){ lastGesturePosition.x + 122, lastGesturePosition.y + 16 }, (Vector2){ lastGesturePosition.x + 137, lastGesturePosition.y + 26 }, (Vector2){ lastGesturePosition.x + 137, lastGesturePosition.y + 6 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY);
-        DrawTriangle((Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 6 }, (Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 26 }, (Vector2){ lastGesturePosition.x + 162, lastGesturePosition.y + 16 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY);
-        DrawTriangle((Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 33 }, (Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 140, lastGesturePosition.y + 43 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY);
-        DrawTriangle((Vector2){ lastGesturePosition.x + 144, lastGesturePosition.y + 43 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 33 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY);
-        for (i = 0; i < 4; i++) DrawCircle(lastGesturePosition.x + 180, lastGesturePosition.y + 7 + i*15, 5, touchCount <= i? LIGHTGRAY : gestureColor);
-
-        // Draw gesture log
-        //--------------------------------------------------------------------------------------
-        DrawText("Log", gestureLogPosition.x, gestureLogPosition.y, 20, BLACK);
-
-        // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle)
-        for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], gestureLogPosition.x, gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY));
-        Color logButton1Color, logButton2Color;
-        switch (logMode)
-        {
-            case 3:  logButton1Color=MAROON; logButton2Color=MAROON; break;
-            case 2:  logButton1Color=GRAY;   logButton2Color=MAROON; break;
-            case 1:  logButton1Color=MAROON; logButton2Color=GRAY;   break;
-            default: logButton1Color=GRAY;   logButton2Color=GRAY;   break;
-        }
-        DrawRectangleRec(logButton1, logButton1Color);
-        DrawText("Hide", logButton1.x + 7, logButton1.y + 3, 10, WHITE);
-        DrawText("Repeat", logButton1.x + 7, logButton1.y + 13, 10, WHITE);
-        DrawRectangleRec(logButton2, logButton2Color);
-        DrawText("Hide", logButton1.x + 62, logButton1.y + 3, 10, WHITE);
-        DrawText("Hold", logButton1.x + 62, logButton1.y + 13, 10, WHITE);
-
-        // Draw protractor
-        //--------------------------------------------------------------------------------------
-        DrawText("Angle", protractorPosition.x + 55, protractorPosition.y + 76, 10, BLACK);
-        const char *angleString = TextFormat("%f", currentAngleDegrees);
-        const int angleStringDot = TextFindIndex(angleString, ".");
-        const char *angleStringTrim = TextSubtext(angleString, 0, angleStringDot + 3);
-        DrawText( angleStringTrim, protractorPosition.x + 55, protractorPosition.y + 92, 20, gestureColor);
-        DrawCircle(protractorPosition.x, protractorPosition.y, 80.0f, WHITE);
-        DrawLineEx((Vector2){ protractorPosition.x - 90, protractorPosition.y }, (Vector2){ protractorPosition.x + 90, protractorPosition.y }, 3.0f, LIGHTGRAY);
-        DrawLineEx((Vector2){ protractorPosition.x, protractorPosition.y - 90 }, (Vector2){ protractorPosition.x, protractorPosition.y + 90 }, 3.0f, LIGHTGRAY);
-        DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y - 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y + 45 }, 3.0f, GREEN);
-        DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y + 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y - 45 }, 3.0f, GREEN);
-        DrawText("0", protractorPosition.x + 96, protractorPosition.y - 9, 20, BLACK);
-        DrawText("30", protractorPosition.x + 74, protractorPosition.y - 68, 20, BLACK);
-        DrawText("90", protractorPosition.x - 11, protractorPosition.y - 110, 20, BLACK);
-        DrawText("150", protractorPosition.x - 100, protractorPosition.y - 68, 20, BLACK);
-        DrawText("180", protractorPosition.x - 124, protractorPosition.y - 9, 20, BLACK);
-        DrawText("210", protractorPosition.x - 100, protractorPosition.y + 50, 20, BLACK);
-        DrawText("270", protractorPosition.x - 18, protractorPosition.y + 92, 20, BLACK);
-        DrawText("330", protractorPosition.x + 72, protractorPosition.y + 50, 20, BLACK);
-        if (currentAngleDegrees != 0.0f) DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor);
-
-        // Draw touch and mouse pointer points
-        //--------------------------------------------------------------------------------------
-        if (currentGesture != GESTURE_NONE)
-        {
-            if ( touchCount != 0 )
-            {
-                for (i = 0; i < touchCount; i++)
-                {
-                    DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f));
-                    DrawCircleV(touchPosition[i], 5.0f, gestureColor);
-                }
-
-                if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8 : 12), gestureColor);
-            }
-            else
-            {
-                DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f));
-                DrawCircleV(mousePosition, 5.0f, gestureColor);
-            }
-        }
-
-    EndDrawing();
-    //--------------------------------------------------------------------------------------
-
-}
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web");
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    //--------------------------------------------------------------------------------------
-    #if defined(PLATFORM_WEB)
-        emscripten_set_main_loop(Update, 0, 1);
-    #else
-        SetTargetFPS(60);
-        while (!WindowShouldClose()) Update(); // Detect window close button or ESC key
-    #endif
-    //--------------------------------------------------------------------------------------
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow(); // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/core/core_input_keys.c b/raylib/examples/core/core_input_keys.c
--- a/raylib/examples/core/core_input_keys.c
+++ b/raylib/examples/core/core_input_keys.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Keyboard input
+*   raylib [core] example - input keys
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard input");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input keys");
 
     Vector2 ballPosition = { (float)screenWidth/2, (float)screenHeight/2 };
 
diff --git a/raylib/examples/core/core_input_mouse.c b/raylib/examples/core/core_input_mouse.c
--- a/raylib/examples/core/core_input_mouse.c
+++ b/raylib/examples/core/core_input_mouse.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Mouse input
+*   raylib [core] example - input mouse
 *
-*   Example originally created with raylib 1.0, last time updated with raylib 4.0
+*   Example complexity rating: [★☆☆☆] 1/4
 *
+*   Example originally created with raylib 1.0, last time updated with raylib 5.5
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - mouse input");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - input mouse");
 
     Vector2 ballPosition = { -100.0f, -100.0f };
     Color ballColor = DARKBLUE;
@@ -36,6 +38,12 @@
     {
         // Update
         //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_H))
+        {
+            if (IsCursorHidden()) ShowCursor();
+            else HideCursor();
+        }
+
         ballPosition = GetMousePosition();
 
         if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) ballColor = MAROON;
@@ -56,6 +64,10 @@
             DrawCircleV(ballPosition, 40, ballColor);
 
             DrawText("move ball with mouse and click mouse button to change color", 10, 10, 20, DARKGRAY);
+            DrawText("Press 'H' to toggle cursor visibility", 10, 30, 20, DARKGRAY);
+
+            if (IsCursorHidden()) DrawText("CURSOR HIDDEN", 20, 60, 20, RED);
+            else DrawText("CURSOR VISIBLE", 20, 60, 20, LIME);
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/core/core_input_mouse_wheel.c b/raylib/examples/core/core_input_mouse_wheel.c
--- a/raylib/examples/core/core_input_mouse_wheel.c
+++ b/raylib/examples/core/core_input_mouse_wheel.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] examples - Mouse wheel input
+*   raylib [core] example - input mouse wheel
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.1, last time updated with raylib 1.3
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/core/core_input_multitouch.c b/raylib/examples/core/core_input_multitouch.c
--- a/raylib/examples/core/core_input_multitouch.c
+++ b/raylib/examples/core/core_input_multitouch.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Input multitouch
+*   raylib [core] example - input multitouch
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 2.1, last time updated with raylib 2.5
 *
 *   Example contributed by Berni (@Berni8k) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -42,9 +44,9 @@
         // Get the touch point count ( how many fingers are touching the screen )
         int tCount = GetTouchPointCount();
         // Clamp touch points available ( set the maximum touch points allowed )
-        if(tCount > MAX_TOUCH_POINTS) tCount = MAX_TOUCH_POINTS;
+        if (tCount > MAX_TOUCH_POINTS) tCount = MAX_TOUCH_POINTS;
         // Get touch points positions
-        for (int i = 0; i < tCount; ++i) touchPositions[i] = GetTouchPosition(i);
+        for (int i = 0; i < tCount; i++) touchPositions[i] = GetTouchPosition(i);
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -52,8 +54,8 @@
         BeginDrawing();
 
             ClearBackground(RAYWHITE);
-            
-            for (int i = 0; i < tCount; ++i)
+
+            for (int i = 0; i < tCount; i++)
             {
                 // Make sure point is not (0, 0) as this means there is no touch for it
                 if ((touchPositions[i].x > 0) && (touchPositions[i].y > 0))
diff --git a/raylib/examples/core/core_input_virtual_controls.c b/raylib/examples/core/core_input_virtual_controls.c
--- a/raylib/examples/core/core_input_virtual_controls.c
+++ b/raylib/examples/core/core_input_virtual_controls.c
@@ -2,21 +2,33 @@
 *
 *   raylib [core] example - input virtual controls
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 5.0
 *
-*   Example create by GreenSnakeLinux (@GreenSnakeLinux),
-*   lighter by oblerion (@oblerion) and 
-*   reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by GreenSnakeLinux (@GreenSnakeLinux),
+*   reviewed by Ramon Santamaria (@raysan5), oblerion (@oblerion) and danilwhale (@danilwhale)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2024-2025 GreenSnakeLinux (@GreenSnakeLinux) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
+
 #include <math.h>
+
+typedef enum {
+    BUTTON_NONE = -1,
+    BUTTON_UP,
+    BUTTON_LEFT,
+    BUTTON_RIGHT,
+    BUTTON_DOWN,
+    BUTTON_MAX
+} PadButton;
+
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
@@ -29,88 +41,102 @@
 
     InitWindow(screenWidth, screenHeight, "raylib [core] example - input virtual controls");
 
-    const int dpadX = 90;
-    const int dpadY = 300;
-    const int dpadRad = 25;//radius of each pad
-    Color dpadColor = BLUE;
-    int dpadKeydown = -1;//-1 if not down, else 0,1,2,3 
+    Vector2 padPosition = { 100, 350 };
+    float buttonRadius = 30;
 
-    
-    const float dpadCollider[4][2]= // collider array with x,y position
-    {
-        {dpadX,dpadY-dpadRad*1.5},//up
-        {dpadX-dpadRad*1.5,dpadY},//left
-        {dpadX+dpadRad*1.5,dpadY},//right
-        {dpadX,dpadY+dpadRad*1.5}//down
+    Vector2 buttonPositions[BUTTON_MAX] = {
+        { padPosition.x,padPosition.y - buttonRadius*1.5f },  // Up
+        { padPosition.x - buttonRadius*1.5f, padPosition.y }, // Left
+        { padPosition.x + buttonRadius*1.5f, padPosition.y }, // Right
+        { padPosition.x, padPosition.y + buttonRadius*1.5f }  // Down
     };
-    const char dpadLabel[4]="XYBA";//label of Dpad
 
-    float playerX=100;
-    float playerY=100;
+    const char *buttonLabels[BUTTON_MAX] = {
+        "Y",    // Up
+        "X",    // Left
+        "B",    // Right
+        "A"     // Down
+    };
 
+    Color buttonLabelColors[BUTTON_MAX] = {
+        YELLOW, // Up
+        BLUE,   // Left
+        RED,    // Right
+        GREEN   // Down
+    };
+
+    int pressedButton = BUTTON_NONE;
+    Vector2 inputPosition = { 0, 0 };
+
+    Vector2 playerPosition = { (float)screenWidth/2, (float)screenHeight/2 };
+    float playerSpeed = 75;
+
     SetTargetFPS(60);
     //--------------------------------------------------------------------------------------
 
     // Main game loop
     while (!WindowShouldClose())    // Detect window close button or ESC key
     {
-	// Update 
-	//--------------------------------------------------------------------------
-        dpadKeydown = -1; //reset
-        float inputX=0;
-        float inputY=0;
-        if(GetTouchPointCount()>0)
-        {//use touch pos
-            inputX = GetTouchX();
-            inputY = GetTouchY();
-        }
-        else
-        {//use mouse pos
-            inputX = GetMouseX();
-            inputY = GetMouseY();
-        }
-        for(int i=0;i<4;i++)
+        // Update
+        //--------------------------------------------------------------------------
+        if ((GetTouchPointCount() > 0)) inputPosition = GetTouchPosition(0); // Use touch position
+        else inputPosition = GetMousePosition(); // Use mouse position
+
+        // Reset pressed button to none
+        pressedButton = BUTTON_NONE;
+
+        // Make sure user is pressing left mouse button if they're from desktop
+        if ((GetTouchPointCount() > 0) ||
+            ((GetTouchPointCount() == 0) && IsMouseButtonDown(MOUSE_BUTTON_LEFT)))
         {
-            //test distance each collider and input < radius
-            if( fabsf(dpadCollider[i][1]-inputY) + fabsf(dpadCollider[i][0]-inputX) < dpadRad)
+            // Find nearest D-Pad button to the input position
+            for (int i = 0; i < BUTTON_MAX; i++)
             {
-                dpadKeydown = i;
-                break;
+                float distX = fabsf(buttonPositions[i].x - inputPosition.x);
+                float distY = fabsf(buttonPositions[i].y - inputPosition.y);
+
+                if ((distX + distY < buttonRadius))
+                {
+                    pressedButton = i;
+                    break;
+                }
             }
         }
-        // move player
-        switch(dpadKeydown){
-            case 0: playerY -= 50*GetFrameTime();
-            break;
-            case 1: playerX -= 50*GetFrameTime();
-            break;
-            case 2: playerX += 50*GetFrameTime();
-            break;
-            case 3: playerY += 50*GetFrameTime();
-            default:;
+
+        // Move player according to pressed button
+        switch (pressedButton)
+        {
+            case BUTTON_UP: playerPosition.y -= playerSpeed*GetFrameTime(); break;
+            case BUTTON_LEFT: playerPosition.x -= playerSpeed*GetFrameTime(); break;
+            case BUTTON_RIGHT: playerPosition.x += playerSpeed*GetFrameTime(); break;
+            case BUTTON_DOWN: playerPosition.y += playerSpeed*GetFrameTime(); break;
+            default: break;
         };
-	//--------------------------------------------------------------------------
-	    
-	// Draw 
-	//--------------------------------------------------------------------------
-       BeginDrawing();
+        //--------------------------------------------------------------------------
+
+        // Draw
+        //--------------------------------------------------------------------------
+        BeginDrawing();
+
             ClearBackground(RAYWHITE);
-            for(int i=0;i<4;i++)
+
+            // Draw world
+            DrawCircleV(playerPosition, 50, MAROON);
+
+            // Draw GUI
+            for (int i = 0; i < BUTTON_MAX; i++)
             {
-                //draw all pad
-                DrawCircle(dpadCollider[i][0],dpadCollider[i][1],dpadRad,dpadColor);
-                if(i!=dpadKeydown)
-                {
-                    //draw label
-                    DrawText(TextSubtext(dpadLabel,i,1),
-                             dpadCollider[i][0]-5,
-                             dpadCollider[i][1]-5,16,BLACK);
-                }
+                DrawCircleV(buttonPositions[i], buttonRadius, (i == pressedButton)? DARKGRAY : BLACK);
 
+                DrawText(buttonLabels[i],
+                    (int)buttonPositions[i].x - 7, (int)buttonPositions[i].y - 8,
+                    20, buttonLabelColors[i]);
             }
-            DrawText("Player",playerX,playerY,16,BLACK);
+
+            DrawText("move the player with D-Pad buttons", 10, 10, 20, DARKGRAY);
+
         EndDrawing();
-	//--------------------------------------------------------------------------
+        //--------------------------------------------------------------------------
     }
 
     // De-Initialization
@@ -120,4 +146,3 @@
 
     return 0;
 }
-
diff --git a/raylib/examples/core/core_keyboard_testbed.c b/raylib/examples/core/core_keyboard_testbed.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_keyboard_testbed.c
@@ -0,0 +1,333 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - keyboard testbed
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   NOTE: raylib defined keys refer to ENG-US Keyboard layout, 
+*   mapping to other layouts is up to the user
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2026 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define  KEY_REC_SPACING      4       // Space in pixels between key rectangles
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static const char *GetKeyText(int key);
+static void GuiKeyboardKey(Rectangle bounds, int key);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - keyboard testbed");
+    SetExitKey(KEY_NULL); // Avoid exit on KEY_ESCAPE
+
+    // Keyboard line 01
+    int line01KeyWidths[15] = { 0 };
+    for (int i = 0; i < 15; i++) line01KeyWidths[i] = 45;
+    line01KeyWidths[13] = 62;   // PRINTSCREEN
+    int line01Keys[15] = { 
+        KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, 
+        KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, 
+        KEY_F12, KEY_PRINT_SCREEN, KEY_PAUSE 
+    };
+    
+    // Keyboard line 02
+    int line02KeyWidths[15] = { 0 };
+    for (int i = 0; i < 15; i++) line02KeyWidths[i] = 45;
+    line02KeyWidths[0] = 25;    // GRAVE
+    line02KeyWidths[13] = 82;   // BACKSPACE
+    int line02Keys[15] = { 
+        KEY_GRAVE, KEY_ONE, KEY_TWO, KEY_THREE, KEY_FOUR, 
+        KEY_FIVE, KEY_SIX, KEY_SEVEN, KEY_EIGHT, KEY_NINE, 
+        KEY_ZERO, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_DELETE };
+
+    // Keyboard line 03
+    int line03KeyWidths[15] = { 0 };
+    for (int i = 0; i < 15; i++) line03KeyWidths[i] = 45;
+    line03KeyWidths[0] = 50;    // TAB
+    line03KeyWidths[13] = 57;   // BACKSLASH
+    int line03Keys[15] = {
+        KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y,
+        KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFT_BRACKET,
+        KEY_RIGHT_BRACKET, KEY_BACKSLASH, KEY_INSERT
+    };
+
+    // Keyboard line 04
+    int line04KeyWidths[14] = { 0 };
+    for (int i = 0; i < 14; i++) line04KeyWidths[i] = 45;
+    line04KeyWidths[0] = 68;    // CAPS
+    line04KeyWidths[12] = 88;   // ENTER
+    int line04Keys[14] = {
+        KEY_CAPS_LOCK, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G,
+        KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
+        KEY_APOSTROPHE, KEY_ENTER, KEY_PAGE_UP
+    };
+
+    // Keyboard line 05
+    int line05KeyWidths[14] = { 0 };
+    for (int i = 0; i < 14; i++) line05KeyWidths[i] = 45;
+    line05KeyWidths[0] = 80;    // LSHIFT
+    line05KeyWidths[11] = 76;   // RSHIFT
+    int line05Keys[14] = {
+        KEY_LEFT_SHIFT, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B,
+        KEY_N, KEY_M, KEY_COMMA, KEY_PERIOD, /*KEY_MINUS*/
+        KEY_SLASH, KEY_RIGHT_SHIFT, KEY_UP, KEY_PAGE_DOWN
+    };
+
+    // Keyboard line 06
+    int line06KeyWidths[11] = { 0 };
+    for (int i = 0; i < 11; i++) line06KeyWidths[i] = 45;
+    line06KeyWidths[0] = 80;    // LCTRL
+    line06KeyWidths[3] = 208;   // SPACE
+    line06KeyWidths[7] = 60;    // RCTRL
+    int line06Keys[11] = {
+        KEY_LEFT_CONTROL, KEY_LEFT_SUPER, KEY_LEFT_ALT,
+        KEY_SPACE, KEY_RIGHT_ALT, 162, KEY_NULL,
+        KEY_RIGHT_CONTROL, KEY_LEFT, KEY_DOWN, KEY_RIGHT
+    };
+    
+    Vector2 keyboardOffset = { 26, 80 };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        int key = GetKeyPressed(); // Get pressed keycode
+        if (key > 0) TraceLog(LOG_INFO, "KEYBOARD TESTBED: KEY PRESSED:    %d", key);
+
+        int ch = GetCharPressed(); // Get pressed char for text input, using OS mapping
+        if (ch > 0) TraceLog(LOG_INFO,  "KEYBOARD TESTBED: CHAR PRESSED:   %c (%d)", ch, ch);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("KEYBOARD LAYOUT: ENG-US", 26, 38, 20, LIGHTGRAY);
+            
+            // Keyboard line 01 - 15 keys
+            // ESC, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, IMP, CLOSE
+            for (int i = 0, recOffsetX = 0; i < 15; i++) 
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y, line01KeyWidths[i], 30 }, line01Keys[i]);
+                recOffsetX += line01KeyWidths[i] + KEY_REC_SPACING;
+            }
+ 
+            // Keyboard line 02 - 15 keys
+            // `, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -, =, BACKSPACE, DEL
+            for (int i = 0, recOffsetX = 0; i < 15; i++) 
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + KEY_REC_SPACING, line02KeyWidths[i], 38 }, line02Keys[i]);
+                recOffsetX += line02KeyWidths[i] + KEY_REC_SPACING;
+            }
+            
+            // Keyboard line 03 - 15 keys
+            // TAB, Q, W, E, R, T, Y, U, I, O, P, [, ], \, INS
+            for (int i = 0, recOffsetX = 0; i < 15; i++)
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38 + KEY_REC_SPACING*2, line03KeyWidths[i], 38 }, line03Keys[i]);
+                recOffsetX += line03KeyWidths[i] + KEY_REC_SPACING;
+            }
+
+            // Keyboard line 04 - 14 keys
+            // MAYUS, A, S, D, F, G, H, J, K, L, ;, ', ENTER, REPAG
+            for (int i = 0, recOffsetX = 0; i < 14; i++)
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*2 + KEY_REC_SPACING*3, line04KeyWidths[i], 38 }, line04Keys[i]);
+                recOffsetX += line04KeyWidths[i] + KEY_REC_SPACING;
+            }
+
+            // Keyboard line 05 - 14 keys
+            // LSHIFT, Z, X, C, V, B, N, M, ,, ., /, RSHIFT, UP, AVPAG
+            for (int i = 0, recOffsetX = 0; i < 14; i++)
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*3 + KEY_REC_SPACING*4, line05KeyWidths[i], 38 }, line05Keys[i]);
+                recOffsetX += line05KeyWidths[i] + KEY_REC_SPACING;
+            }
+
+            // Keyboard line 06 - 11 keys
+            // LCTRL, WIN, LALT, SPACE, ALTGR, \, FN, RCTRL, LEFT, DOWN, RIGHT
+            for (int i = 0, recOffsetX = 0; i < 11; i++)
+            {
+                GuiKeyboardKey((Rectangle){ keyboardOffset.x + recOffsetX, keyboardOffset.y + 30 + 38*4 + KEY_REC_SPACING*5, line06KeyWidths[i], 38 }, line06Keys[i]);
+                recOffsetX += line06KeyWidths[i] + KEY_REC_SPACING;
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Get keyboard keycode as text (US keyboard)
+// NOTE: Mapping for other keyboard layouts can be done here
+static const char *GetKeyText(int key)
+{
+    switch (key)
+    {
+        case KEY_APOSTROPHE      : return "'";          // Key: '
+        case KEY_COMMA           : return ",";          // Key: ,
+        case KEY_MINUS           : return "-";          // Key: -
+        case KEY_PERIOD          : return ".";          // Key: .
+        case KEY_SLASH           : return "/";          // Key: /
+        case KEY_ZERO            : return "0";          // Key: 0
+        case KEY_ONE             : return "1";          // Key: 1
+        case KEY_TWO             : return "2";          // Key: 2
+        case KEY_THREE           : return "3";          // Key: 3
+        case KEY_FOUR            : return "4";          // Key: 4
+        case KEY_FIVE            : return "5";          // Key: 5
+        case KEY_SIX             : return "6";          // Key: 6
+        case KEY_SEVEN           : return "7";          // Key: 7
+        case KEY_EIGHT           : return "8";          // Key: 8
+        case KEY_NINE            : return "9";          // Key: 9
+        case KEY_SEMICOLON       : return ";";          // Key: ;
+        case KEY_EQUAL           : return "=";          // Key: =
+        case KEY_A               : return "A";          // Key: A | a
+        case KEY_B               : return "B";          // Key: B | b
+        case KEY_C               : return "C";          // Key: C | c
+        case KEY_D               : return "D";          // Key: D | d
+        case KEY_E               : return "E";          // Key: E | e
+        case KEY_F               : return "F";          // Key: F | f
+        case KEY_G               : return "G";          // Key: G | g
+        case KEY_H               : return "H";          // Key: H | h
+        case KEY_I               : return "I";          // Key: I | i
+        case KEY_J               : return "J";          // Key: J | j
+        case KEY_K               : return "K";          // Key: K | k
+        case KEY_L               : return "L";          // Key: L | l
+        case KEY_M               : return "M";          // Key: M | m
+        case KEY_N               : return "N";          // Key: N | n
+        case KEY_O               : return "O";          // Key: O | o
+        case KEY_P               : return "P";          // Key: P | p
+        case KEY_Q               : return "Q";          // Key: Q | q
+        case KEY_R               : return "R";          // Key: R | r
+        case KEY_S               : return "S";          // Key: S | s
+        case KEY_T               : return "T";          // Key: T | t
+        case KEY_U               : return "U";          // Key: U | u
+        case KEY_V               : return "V";          // Key: V | v
+        case KEY_W               : return "W";          // Key: W | w
+        case KEY_X               : return "X";          // Key: X | x
+        case KEY_Y               : return "Y";          // Key: Y | y
+        case KEY_Z               : return "Z";          // Key: Z | z
+        case KEY_LEFT_BRACKET    : return "[";          // Key: [
+        case KEY_BACKSLASH       : return "\\";         // Key: '\'
+        case KEY_RIGHT_BRACKET   : return "]";          // Key: ]
+        case KEY_GRAVE           : return "`";          // Key: `
+        case KEY_SPACE           : return "SPACE";      // Key: Space
+        case KEY_ESCAPE          : return "ESC";        // Key: Esc
+        case KEY_ENTER           : return "ENTER";      // Key: Enter
+        case KEY_TAB             : return "TAB";        // Key: Tab
+        case KEY_BACKSPACE       : return "BACK";       // Key: Backspace
+        case KEY_INSERT          : return "INS";        // Key: Ins
+        case KEY_DELETE          : return "DEL";        // Key: Del
+        case KEY_RIGHT           : return "RIGHT";      // Key: Cursor right
+        case KEY_LEFT            : return "LEFT";       // Key: Cursor left
+        case KEY_DOWN            : return "DOWN";       // Key: Cursor down
+        case KEY_UP              : return "UP";         // Key: Cursor up
+        case KEY_PAGE_UP         : return "PGUP";       // Key: Page up
+        case KEY_PAGE_DOWN       : return "PGDOWN";     // Key: Page down
+        case KEY_HOME            : return "HOME";       // Key: Home
+        case KEY_END             : return "END";        // Key: End
+        case KEY_CAPS_LOCK       : return "CAPS";       // Key: Caps lock
+        case KEY_SCROLL_LOCK     : return "LOCK";       // Key: Scroll down
+        case KEY_NUM_LOCK        : return "NUMLOCK";    // Key: Num lock
+        case KEY_PRINT_SCREEN    : return "PRINTSCR";   // Key: Print screen
+        case KEY_PAUSE           : return "PAUSE";      // Key: Pause
+        case KEY_F1              : return "F1";         // Key: F1
+        case KEY_F2              : return "F2";         // Key: F2
+        case KEY_F3              : return "F3";         // Key: F3
+        case KEY_F4              : return "F4";         // Key: F4
+        case KEY_F5              : return "F5";         // Key: F5
+        case KEY_F6              : return "F6";         // Key: F6
+        case KEY_F7              : return "F7";         // Key: F7
+        case KEY_F8              : return "F8";         // Key: F8
+        case KEY_F9              : return "F9";         // Key: F9
+        case KEY_F10             : return "F10";        // Key: F10
+        case KEY_F11             : return "F11";        // Key: F11
+        case KEY_F12             : return "F12";        // Key: F12
+        case KEY_LEFT_SHIFT      : return "LSHIFT";     // Key: Shift left
+        case KEY_LEFT_CONTROL    : return "LCTRL";      // Key: Control left
+        case KEY_LEFT_ALT        : return "LALT";       // Key: Alt left
+        case KEY_LEFT_SUPER      : return "WIN";        // Key: Super left
+        case KEY_RIGHT_SHIFT     : return "RSHIFT";     // Key: Shift right
+        case KEY_RIGHT_CONTROL   : return "RCTRL";      // Key: Control right
+        case KEY_RIGHT_ALT       : return "ALTGR";      // Key: Alt right
+        case KEY_RIGHT_SUPER     : return "RSUPER";     // Key: Super right
+        case KEY_KB_MENU         : return "KBMENU";     // Key: KB menu
+        case KEY_KP_0            : return "KP0";        // Key: Keypad 0
+        case KEY_KP_1            : return "KP1";        // Key: Keypad 1
+        case KEY_KP_2            : return "KP2";        // Key: Keypad 2
+        case KEY_KP_3            : return "KP3";        // Key: Keypad 3
+        case KEY_KP_4            : return "KP4";        // Key: Keypad 4
+        case KEY_KP_5            : return "KP5";        // Key: Keypad 5
+        case KEY_KP_6            : return "KP6";        // Key: Keypad 6
+        case KEY_KP_7            : return "KP7";        // Key: Keypad 7
+        case KEY_KP_8            : return "KP8";        // Key: Keypad 8
+        case KEY_KP_9            : return "KP9";        // Key: Keypad 9
+        case KEY_KP_DECIMAL      : return "KPDEC";      // Key: Keypad .
+        case KEY_KP_DIVIDE       : return "KPDIV";      // Key: Keypad /
+        case KEY_KP_MULTIPLY     : return "KPMUL";      // Key: Keypad *
+        case KEY_KP_SUBTRACT     : return "KPSUB";      // Key: Keypad -
+        case KEY_KP_ADD          : return "KPADD";      // Key: Keypad +
+        case KEY_KP_ENTER        : return "KPENTER";    // Key: Keypad Enter
+        case KEY_KP_EQUAL        : return "KPEQU";      // Key: Keypad =
+        default: return "";
+    }
+}
+
+// Draw keyboard key
+static void GuiKeyboardKey(Rectangle bounds, int key)
+{
+    if (key == KEY_NULL) DrawRectangleLinesEx(bounds, 2.0f, LIGHTGRAY);
+    else
+    {
+        if (IsKeyDown(key))
+        {
+            DrawRectangleLinesEx(bounds, 2.0f, MAROON);
+            DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, MAROON);
+        }
+        else
+        {
+            DrawRectangleLinesEx(bounds, 2.0f, DARKGRAY);
+            DrawText(GetKeyText(key), bounds.x + 4, bounds.y + 4, 10, DARKGRAY);
+        }
+    }
+    
+    if (CheckCollisionPointRec(GetMousePosition(), bounds)) 
+    {
+        DrawRectangleRec(bounds, Fade(RED, 0.2f));
+        DrawRectangleLinesEx(bounds, 3.0f, RED);
+    }
+}
diff --git a/raylib/examples/core/core_loading_thread.c b/raylib/examples/core/core_loading_thread.c
deleted file mode 100644
--- a/raylib/examples/core/core_loading_thread.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [core] example - loading thread
-*
-*   NOTE: This example requires linking with pthreads library on MinGW, 
-*   it can be accomplished passing -static parameter to compiler
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 3.0
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-// WARNING: This example does not build on Windows with MSVC compiler
-#include "pthread.h"                        // POSIX style threads management
-
-#include <stdatomic.h>                      // C11 atomic data types
-
-#include <time.h>                           // Required for: clock()
-
-// Using C11 atomics for synchronization
-// NOTE: A plain bool (or any plain data type for that matter) can't be used for inter-thread synchronization
-static atomic_bool dataLoaded = false; // Data Loaded completion indicator
-static void *LoadDataThread(void *arg);     // Loading data thread function declaration
-
-static atomic_int dataProgress = 0;                // Data progress accumulator
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread");
-
-    pthread_t threadId = { 0 };     // Loading data thread id
-
-    enum { STATE_WAITING, STATE_LOADING, STATE_FINISHED } state = STATE_WAITING;
-    int framesCounter = 0;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        switch (state)
-        {
-            case STATE_WAITING:
-            {
-                if (IsKeyPressed(KEY_ENTER))
-                {
-                    int error = pthread_create(&threadId, NULL, &LoadDataThread, NULL);
-                    if (error != 0) TraceLog(LOG_ERROR, "Error creating loading thread");
-                    else TraceLog(LOG_INFO, "Loading thread initialized successfully");
-
-                    state = STATE_LOADING;
-                }
-            } break;
-            case STATE_LOADING:
-            {
-                framesCounter++;
-                if (atomic_load_explicit(&dataLoaded, memory_order_relaxed))
-                {
-                    framesCounter = 0;
-                    int error = pthread_join(threadId, NULL);
-                    if (error != 0) TraceLog(LOG_ERROR, "Error joining loading thread");
-                    else TraceLog(LOG_INFO, "Loading thread terminated successfully");
-
-                    state = STATE_FINISHED;
-                }
-            } break;
-            case STATE_FINISHED:
-            {
-                if (IsKeyPressed(KEY_ENTER))
-                {
-                    // Reset everything to launch again
-                    atomic_store_explicit(&dataLoaded, false, memory_order_relaxed);
-                    atomic_store_explicit(&dataProgress, 0, memory_order_relaxed);
-                    state = STATE_WAITING;
-                }
-            } break;
-            default: break;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            switch (state)
-            {
-                case STATE_WAITING: DrawText("PRESS ENTER to START LOADING DATA", 150, 170, 20, DARKGRAY); break;
-                case STATE_LOADING:
-                {
-                    DrawRectangle(150, 200, atomic_load_explicit(&dataProgress, memory_order_relaxed), 60, SKYBLUE);
-                    if ((framesCounter/15)%2) DrawText("LOADING DATA...", 240, 210, 40, DARKBLUE);
-
-                } break;
-                case STATE_FINISHED:
-                {
-                    DrawRectangle(150, 200, 500, 60, LIME);
-                    DrawText("DATA LOADED!", 250, 210, 40, GREEN);
-
-                } break;
-                default: break;
-            }
-
-            DrawRectangleLines(150, 200, 500, 60, DARKGRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-// Loading data thread function definition
-static void *LoadDataThread(void *arg)
-{
-    int timeCounter = 0;            // Time counted in ms
-    clock_t prevTime = clock();     // Previous time
-
-    // We simulate data loading with a time counter for 5 seconds
-    while (timeCounter < 5000)
-    {
-        clock_t currentTime = clock() - prevTime;
-        timeCounter = currentTime*1000/CLOCKS_PER_SEC;
-
-        // We accumulate time over a global variable to be used in
-        // main thread as a progress bar
-        atomic_store_explicit(&dataProgress, timeCounter/10, memory_order_relaxed);
-    }
-
-    // When data has finished loading, we set global variable
-    atomic_store_explicit(&dataLoaded, true, memory_order_relaxed);
-
-    return NULL;
-}
diff --git a/raylib/examples/core/core_monitor_detector.c b/raylib/examples/core/core_monitor_detector.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_monitor_detector.c
@@ -0,0 +1,160 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - monitor detector
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Maicon Santana (@maiconpintoabreu)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define MAX_MONITORS 10
+
+// Monitor info
+typedef struct MonitorInfo {
+    Vector2 position;
+    const char *name;
+    int width;
+    int height;
+    int physicalWidth;
+    int physicalHeight;
+    int refreshRate;
+} MonitorInfo;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - monitor detector");
+
+    MonitorInfo monitors[MAX_MONITORS] = { 0 };
+    int currentMonitorIndex = GetCurrentMonitor();
+    int monitorCount = 0;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Variables to find the max x and Y to calculate the scale
+        int maxWidth = 1;
+        int maxHeight = 1;
+
+        // Monitor offset is to fix when monitor position x is negative
+        int monitorOffsetX = 0;
+
+        // Rebuild monitors array every frame
+        monitorCount = GetMonitorCount();
+        for (int i = 0; i < monitorCount; i++)
+        {
+            monitors[i] = (MonitorInfo){
+                GetMonitorPosition(i),
+                GetMonitorName(i),
+                GetMonitorWidth(i),
+                GetMonitorHeight(i),
+                GetMonitorPhysicalWidth(i),
+                GetMonitorPhysicalHeight(i),
+                GetMonitorRefreshRate(i)
+            };
+
+            if (monitors[i].position.x < monitorOffsetX) monitorOffsetX = -(int)monitors[i].position.x;
+
+            const int width = (int)monitors[i].position.x + monitors[i].width;
+            const int height = (int)monitors[i].position.y + monitors[i].height;
+
+            if (maxWidth < width) maxWidth = width;
+            if (maxHeight < height) maxHeight = height;
+        }
+
+        if (IsKeyPressed(KEY_ENTER) && (monitorCount > 1))
+        {
+            currentMonitorIndex += 1;
+
+            // Set index to 0 if the last one
+            if (currentMonitorIndex == monitorCount) currentMonitorIndex = 0;
+
+            SetWindowMonitor(currentMonitorIndex); // Move window to currentMonitorIndex
+        }
+        else currentMonitorIndex = GetCurrentMonitor(); // Get currentMonitorIndex if manually moved
+
+        float monitorScale = 0.6f;
+
+        if (maxHeight > (maxWidth + monitorOffsetX)) monitorScale *= ((float)screenHeight/(float)maxHeight);
+        else monitorScale *= ((float)screenWidth/(float)(maxWidth + monitorOffsetX));
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("Press [Enter] to move window to next monitor available", 20, 20, 20, DARKGRAY);
+
+            DrawRectangleLines(20, 60, screenWidth - 40, screenHeight - 100, DARKGRAY);
+
+            // Draw Monitor Rectangles with information inside
+            for (int i = 0; i < monitorCount; i++)
+            {
+                // Calculate retangle position and size using monitorScale
+                const Rectangle rec = (Rectangle){
+                    (monitors[i].position.x + monitorOffsetX)*monitorScale + 140,
+                    monitors[i].position.y*monitorScale + 80,
+                    monitors[i].width*monitorScale,
+                    monitors[i].height*monitorScale
+                };
+
+                // Draw monitor name and information inside the rectangle
+                DrawText(TextFormat("[%i] %s", i, monitors[i].name), (int)rec.x + 10, (int)rec.y + (int)(100*monitorScale), (int)(120*monitorScale), BLUE);
+                DrawText(
+                    TextFormat("Resolution: [%ipx x %ipx]\nRefreshRate: [%ihz]\nPhysical Size: [%imm x %imm]\nPosition: %3.0f x %3.0f",
+                        monitors[i].width,
+                        monitors[i].height,
+                        monitors[i].refreshRate,
+                        monitors[i].physicalWidth,
+                        monitors[i].physicalHeight,
+                        monitors[i].position.x,
+                        monitors[i].position.y
+                    ), (int)rec.x + 10, (int)rec.y + (int)(200*monitorScale), (int)(120*monitorScale), DARKGRAY);
+
+                // Highlight current monitor
+                if (i == currentMonitorIndex)
+                {
+                    DrawRectangleLinesEx(rec, 5, RED);
+                    Vector2 windowPosition = (Vector2){ (GetWindowPosition().x + monitorOffsetX)*monitorScale  + 140, GetWindowPosition().y*monitorScale + 80 };
+
+                    // Draw window position based on monitors
+                    DrawRectangleV(windowPosition, (Vector2){screenWidth*monitorScale, screenHeight*monitorScale}, Fade(GREEN, 0.5));
+                }
+                else DrawRectangleLinesEx(rec, 5, GRAY);
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_random_sequence.c b/raylib/examples/core/core_random_sequence.c
--- a/raylib/examples/core/core_random_sequence.c
+++ b/raylib/examples/core/core_random_sequence.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Generates a random sequence
+*   raylib [core] example - random sequence
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 5.0
 *
 *   Example contributed by Dalton Overmyer (@REDl3east) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,168 +11,169 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Dalton Overmyer (@REDl3east)
+*   Copyright (c) 2023-2025 Dalton Overmyer (@REDl3east)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
+
 #include "raymath.h"
 
-#include <stdlib.h> // Required for: malloc() and free()
+#include <stdlib.h>     // Required for: malloc(), free()
 
-typedef struct ColorRect{
-  Color c;
-  Rectangle r;
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct ColorRect {
+    Color color;
+    Rectangle rect;
 } ColorRect;
 
-static Color GenerateRandomColor();
-static ColorRect* GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight);
-static void ShuffleColorRectSequence(ColorRect* rectangles, int rectCount);
-static void DrawTextCenterKeyHelp(const char* key, const char* text, int posX, int posY, int fontSize, Color color);
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static Color GenerateRandomColor(void);
+static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight);
+static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount);
 
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main(void) {
-  // Initialization
-  //--------------------------------------------------------------------------------------
-  const int screenWidth = 800;
-  const int screenHeight = 450;
-
-  InitWindow(screenWidth, screenHeight, "raylib [core] example - Generates a random sequence");
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
 
-  int rectCount = 20;
-  float rectSize = (float)screenWidth/rectCount;
-  ColorRect* rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f * screenHeight);
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - random sequence");
 
-  SetTargetFPS(60);
-  //--------------------------------------------------------------------------------------
+    int rectCount = 20;
+    float rectSize = (float)screenWidth/rectCount;
+    ColorRect *rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f*screenHeight);
 
-  // Main game loop
-  while (!WindowShouldClose()) // Detect window close button or ESC key
-  {
-    // Update
-    //----------------------------------------------------------------------------------
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
 
-    if(IsKeyPressed(KEY_SPACE))
+    // Main game loop
+    while (!WindowShouldClose()) // Detect window close button or ESC key
     {
-      ShuffleColorRectSequence(rectangles, rectCount);
-    }
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_SPACE)) ShuffleColorRectSequence(rectangles, rectCount);
 
-    if(IsKeyPressed(KEY_UP))
-    {
-      rectCount++;
-      rectSize = (float)screenWidth/rectCount;
-      free(rectangles);
-      rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f * screenHeight);
-    }
+        if (IsKeyPressed(KEY_UP))
+        {
+            rectCount++;
+            rectSize = (float)screenWidth/rectCount;
+            RL_FREE(rectangles);
 
-    if(IsKeyPressed(KEY_DOWN))
-    {
-      if(rectCount >= 4){
-        rectCount--;
-        rectSize = (float)screenWidth/rectCount;
-        free(rectangles);
-        rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f * screenHeight);
-      }
-    }
+            // Re-generate random sequence with new count
+            rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f*screenHeight);
+        }
 
-    // Draw
-    //----------------------------------------------------------------------------------
-    BeginDrawing();
+        if (IsKeyPressed(KEY_DOWN))
+        {
+            if (rectCount >= 4)
+            {
+                rectCount--;
+                rectSize = (float)screenWidth/rectCount;
+                RL_FREE(rectangles);
 
-    ClearBackground(RAYWHITE);
-    
-    int fontSize = 20;
-    for(int x=0;x<rectCount;x++)
-    {
-      DrawRectangleRec(rectangles[x].r, rectangles[x].c);
-      DrawTextCenterKeyHelp("SPACE", "to shuffle the sequence.", 10, screenHeight - 96, fontSize, BLACK);
-      DrawTextCenterKeyHelp("UP", "to add a rectangle and generate a new sequence.", 10, screenHeight - 64, fontSize, BLACK);
-      DrawTextCenterKeyHelp("DOWN", "to remove a rectangle and generate a new sequence.", 10, screenHeight - 32, fontSize, BLACK);
-    }
+                // Re-generate random sequence with new count
+                rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f*screenHeight);
+            }
+        }
+        //----------------------------------------------------------------------------------
 
-    const char* rectCountText = TextFormat("%d rectangles", rectCount);
-    int rectCountTextSize = MeasureText(rectCountText, fontSize);
-    DrawText(rectCountText, screenWidth - rectCountTextSize - 10, 10, fontSize, BLACK);
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
 
-    DrawFPS(10, 10);
+            ClearBackground(RAYWHITE);
 
-    EndDrawing();
-    //----------------------------------------------------------------------------------
-  }
+            for (int i = 0; i < rectCount; i++)
+            {
+                DrawRectangleRec(rectangles[i].rect, rectangles[i].color);
 
-  // De-Initialization
-  //--------------------------------------------------------------------------------------
+                DrawText("Press SPACE to shuffle the sequence", 10, screenHeight - 96, 20, BLACK);
 
-  free(rectangles);
-  CloseWindow(); // Close window and OpenGL context
-  //--------------------------------------------------------------------------------------
+                DrawText("Press SPACE to shuffle the current sequence", 10, screenHeight - 96, 20, BLACK);
+                DrawText("Press UP to add a rectangle and generate a new sequence", 10, screenHeight - 64, 20, BLACK);
+                DrawText("Press DOWN to remove a rectangle and generate a new sequence", 10, screenHeight - 32, 20, BLACK);
+            }
 
-  return 0;
-}
+            DrawText(TextFormat("Count: %d rectangles", rectCount), 10, 10, 20, MAROON);
 
-static Color GenerateRandomColor()
-{
-  return CLITERAL(Color){
-    GetRandomValue(0, 255),
-    GetRandomValue(0, 255),
-    GetRandomValue(0, 255),
-    255,
-  };
-}
+            DrawFPS(screenWidth - 80, 10);
 
-static ColorRect* GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight){
-  int *seq = LoadRandomSequence((unsigned int)rectCount, 0, (unsigned int)rectCount-1);
-  ColorRect* rectangles = (ColorRect *)malloc((int)rectCount*sizeof(ColorRect));
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
 
-  float rectSeqWidth = rectCount * rectWidth;
-  float startX = (screenWidth - rectSeqWidth) * 0.5f;
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    free(rectangles);
+    CloseWindow(); // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
 
-  for(int x=0;x<rectCount;x++){
-    int rectHeight = (int)Remap((float)seq[x], 0, rectCount-1, 0, screenHeight);
-    rectangles[x].c = GenerateRandomColor();
-    rectangles[x].r = CLITERAL(Rectangle){
-      startX + x * rectWidth, screenHeight - rectHeight, rectWidth, (float)rectHeight
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+static Color GenerateRandomColor(void)
+{
+    Color color = {
+        GetRandomValue(0, 255),
+        GetRandomValue(0, 255),
+        GetRandomValue(0, 255),
+        255
     };
-  }
-  UnloadRandomSequence(seq);
-  return rectangles;
+
+    return color;
 }
 
-static void ShuffleColorRectSequence(ColorRect* rectangles, int rectCount)
+static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight)
 {
-  int *seq = LoadRandomSequence(rectCount, 0, rectCount-1);
-  for(int i1=0;i1<rectCount;i1++){
-    ColorRect* r1 = &rectangles[i1];
-    ColorRect* r2 = &rectangles[seq[i1]];
+    ColorRect *rectangles = (ColorRect *)RL_CALLOC((int)rectCount, sizeof(ColorRect));
 
-    // swap only the color and height
-    ColorRect tmp = *r1;
-    r1->c = r2->c;
-    r1->r.height = r2->r.height;
-    r1->r.y = r2->r.y;
-    r2->c = tmp.c;
-    r2->r.height = tmp.r.height;
-    r2->r.y = tmp.r.y;
+    int *seq = LoadRandomSequence((unsigned int)rectCount, 0, (unsigned int)rectCount - 1);
+    float rectSeqWidth = rectCount*rectWidth;
+    float startX = (screenWidth - rectSeqWidth)*0.5f;
 
-  }
-  UnloadRandomSequence(seq);
+    for (int i = 0; i < rectCount; i++)
+    {
+        int rectHeight = (int)Remap((float)seq[i], 0, rectCount - 1, 0, screenHeight);
+
+        rectangles[i].color = GenerateRandomColor();
+        rectangles[i].rect = CLITERAL(Rectangle){ startX + i*rectWidth, screenHeight - rectHeight, rectWidth, (float)rectHeight };
+    }
+
+    UnloadRandomSequence(seq);
+
+    return rectangles;
 }
 
-static void DrawTextCenterKeyHelp(const char* key, const char* text, int posX, int posY, int fontSize, Color color)
+static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount)
 {
-  int spaceSize = MeasureText(" ", fontSize); 
-  int pressSize = MeasureText("Press", fontSize); 
-  int keySize = MeasureText(key, fontSize); 
-  int textSize = MeasureText(text, fontSize); 
-  int totalSize = pressSize + 2 * spaceSize + keySize + 2 * spaceSize + textSize;
-  int textSizeCurrent = 0;
+    int *seq = LoadRandomSequence(rectCount, 0, rectCount -  1);
 
-  DrawText("Press", posX, posY, fontSize, color);
-  textSizeCurrent += pressSize + 2 * spaceSize;
-  DrawText(key, posX + textSizeCurrent, posY, fontSize, RED);
-  DrawRectangle(posX + textSizeCurrent, posY + fontSize, keySize, 3, RED);
-  textSizeCurrent += keySize + 2 * spaceSize;
-  DrawText(text, posX + textSizeCurrent, posY, fontSize, color);
+    for (int i1 = 0; i1 < rectCount; i1++)
+    {
+        ColorRect *r1 = &rectangles[i1];
+        ColorRect *r2 = &rectangles[seq[i1]];
+
+        // Swap only the color and height
+        ColorRect tmp = *r1;
+        r1->color = r2->color;
+        r1->rect.height = r2->rect.height;
+        r1->rect.y = r2->rect.y;
+        r2->color = tmp.color;
+        r2->rect.height = tmp.rect.height;
+        r2->rect.y = tmp.rect.y;
+    }
+
+    UnloadRandomSequence(seq);
 }
diff --git a/raylib/examples/core/core_random_values.c b/raylib/examples/core/core_random_values.c
--- a/raylib/examples/core/core_random_values.c
+++ b/raylib/examples/core/core_random_values.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Generate random values
+*   raylib [core] example - random values
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.1, last time updated with raylib 1.1
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,14 +25,14 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - generate random values");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - random values");
 
     // SetRandomSeed(0xaabbccff);   // Set a custom random seed if desired, by default: "time(NULL)"
 
     int randValue = GetRandomValue(-8, 5);   // Get a random integer number between -8 and 5 (both included)
-    
+
     unsigned int framesCounter = 0; // Variable used to count frames
-    
+
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
diff --git a/raylib/examples/core/core_render_texture.c b/raylib/examples/core/core_render_texture.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_render_texture.c
@@ -0,0 +1,101 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - render texture
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //---------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - render texture");
+
+    // Define a render texture to render
+    int renderTextureWidth = 300;
+    int renderTextureHeight = 300;
+    RenderTexture2D target = LoadRenderTexture(renderTextureWidth, renderTextureHeight);
+
+    Vector2 ballPosition = { renderTextureWidth/2.0f, renderTextureHeight/2.0f };
+    Vector2 ballSpeed = { 5.0f, 4.0f };
+    int ballRadius = 20;
+
+    float rotation = 0.0f;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //----------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //-----------------------------------------------------
+        // Ball movement logic
+        ballPosition.x += ballSpeed.x;
+        ballPosition.y += ballSpeed.y;
+
+        // Check walls collision for bouncing
+        if ((ballPosition.x >= (renderTextureWidth - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f;
+        if ((ballPosition.y >= (renderTextureHeight - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -1.0f;
+
+        // Render texture rotation
+        rotation += 0.5f;
+        //-----------------------------------------------------
+
+        // Draw
+        //-----------------------------------------------------
+        // Draw our scene to the render texture
+        BeginTextureMode(target);
+
+            ClearBackground(SKYBLUE);
+
+            DrawRectangle(0, 0, 20, 20, RED);
+            DrawCircleV(ballPosition, (float)ballRadius, MAROON);
+
+        EndTextureMode();
+
+        // Draw render texture to main framebuffer
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw our render texture with rotation applied
+            // NOTE 1: We set the origin of the texture to the center of the render texture
+            // NOTE 2: We flip vertically the texture setting negative source rectangle height
+            DrawTexturePro(target.texture,
+                (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height },
+                (Rectangle){ screenWidth/2.0f, screenHeight/2.0f, (float)target.texture.width, (float)target.texture.height },
+                (Vector2){ target.texture.width/2.0f, target.texture.height/2.0f }, rotation, WHITE);
+
+            DrawText("DRAWING BOUNCING BALL INSIDE RENDER TEXTURE!", 10, screenHeight - 40, 20, BLACK);
+
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //-----------------------------------------------------
+    }
+
+    // De-Initialization
+    //---------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //----------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_scissor_test.c b/raylib/examples/core/core_scissor_test.c
--- a/raylib/examples/core/core_scissor_test.c
+++ b/raylib/examples/core/core_scissor_test.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Scissor test
+*   raylib [core] example - scissor test
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.0
 *
 *   Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Chris Dill (@MysteriousSpace)
+*   Copyright (c) 2019-2025 Chris Dill (@MysteriousSpace)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/core/core_screen_recording.c b/raylib/examples/core/core_screen_recording.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_screen_recording.c
@@ -0,0 +1,166 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - screen recording
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+// Using msf_gif library to record frames into GIF
+#define MSF_GIF_IMPL
+#include "msf_gif.h"            // GIF recording functionality
+
+#include <math.h>               // Required for: sinf()
+
+#define GIF_RECORD_FRAMERATE     5     // Record framerate, we get a frame every N frames
+
+#define MAX_SINEWAVE_POINTS    256
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - screen recording");
+
+    bool gifRecording = false;           // GIF recording state
+    unsigned int gifFrameCounter = 0;    // GIF frames counter
+    MsfGifState gifState = { 0 };        // MSGIF context state
+
+    Vector2 circlePosition = { 0.0f, screenHeight/2.0f };
+    float timeCounter = 0.0f;
+
+    // Get sine wave points for line drawing
+    Vector2 sinePoints[MAX_SINEWAVE_POINTS] = { 0 };
+    for (int i = 0; i < MAX_SINEWAVE_POINTS; i++)
+    {
+        sinePoints[i].x = i*GetScreenWidth()/180.0f;
+        sinePoints[i].y = screenHeight/2.0f + 150*sinf((2*PI/1.5f)*(1.0f/60.0f)*(float)i); // Calculate for 60 fps
+    }
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Update circle sinusoidal movement
+        timeCounter += GetFrameTime();
+        circlePosition.x += GetScreenWidth()/180.0f;
+        circlePosition.y = screenHeight/2.0f + 150*sinf((2*PI/1.5f)*timeCounter);
+        if (circlePosition.x > screenWidth)
+        {
+            circlePosition.x = 0.0f;
+            circlePosition.y = screenHeight/2.0f;
+            timeCounter = 0.0f;
+        }
+
+        // Start-Stop GIF recording on CTRL+R
+        if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_R))
+        {
+            if (gifRecording)
+            {
+                // Stop current recording and save file
+                gifRecording = false;
+                MsfGifResult result = msf_gif_end(&gifState);
+                SaveFileData(TextFormat("%s/screenrecording.gif", GetApplicationDirectory()), result.data, (unsigned int)result.dataSize);
+                msf_gif_free(result);
+
+                TraceLog(LOG_INFO, "Finish animated GIF recording");
+            }
+            else
+            {
+                // Start a new recording
+                gifRecording = true;
+                gifFrameCounter = 0;
+                msf_gif_begin(&gifState, GetRenderWidth(), GetRenderHeight());
+
+                TraceLog(LOG_INFO, "Start animated GIF recording");
+            }
+        }
+
+        if (gifRecording)
+        {
+            gifFrameCounter++;
+
+            // NOTE: We record one gif frame depending on the desired gif framerate
+            if (gifFrameCounter > GIF_RECORD_FRAMERATE)
+            {
+                // Get image data for the current frame (from backbuffer)
+                // WARNING: This process is quite slow, it can generate stuttering
+                Image imScreen = LoadImageFromScreen();
+
+                // Add the frame to the gif recording, providing and "estimated" time for display in centiseconds
+                msf_gif_frame(&gifState, imScreen.data, (int)((1.0f/60.0f)*GIF_RECORD_FRAMERATE)/10, 16, imScreen.width*4);
+                gifFrameCounter = 0;
+
+                UnloadImage(imScreen);    // Free image data
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < (MAX_SINEWAVE_POINTS - 1); i++)
+            {
+                DrawLineV(sinePoints[i], sinePoints[i + 1], MAROON);
+                DrawCircleV(sinePoints[i], 3, MAROON);
+            }
+
+            DrawCircleV(circlePosition, 30, RED);
+
+            DrawFPS(10, 10);
+
+            /*
+            // Draw record indicator
+            // WARNING: If drawn here, it will appear in the recorded image,
+            // use a render texture instead for the recording and LoadImageFromTexture(rt.texture)
+            if (gifRecording)
+            {
+                // Display the recording indicator every half-second
+                if ((int)(GetTime()/0.5)%2 == 1)
+                {
+                    DrawCircle(30, GetScreenHeight() - 20, 10, MAROON);
+                    DrawText("GIF RECORDING", 50, GetScreenHeight() - 25, 10, RED);
+                }
+            }
+            */
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    // If still recording a GIF on close window, just finish
+    if (gifRecording)
+    {
+        MsfGifResult result = msf_gif_end(&gifState);
+        msf_gif_free(result);
+        gifRecording = false;
+    }
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_smooth_pixelperfect.c b/raylib/examples/core/core_smooth_pixelperfect.c
--- a/raylib/examples/core/core_smooth_pixelperfect.c
+++ b/raylib/examples/core/core_smooth_pixelperfect.c
@@ -1,16 +1,18 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Smooth Pixel-perfect camera
+*   raylib [core] example - smooth pixelperfect
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 3.7, last time updated with raylib 4.0
-*   
+*
 *   Example contributed by Giancamillo Alessandroni (@NotManyIdeasDev) and
 *   reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Giancamillo Alessandroni (@NotManyIdeasDev) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -33,7 +35,7 @@
 
     const float virtualRatio = (float)screenWidth/(float)virtualScreenWidth;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixel-perfect camera");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - smooth pixelperfect");
 
     Camera2D worldSpaceCamera = { 0 };  // Game world camera
     worldSpaceCamera.zoom = 1.0f;
@@ -41,7 +43,8 @@
     Camera2D screenSpaceCamera = { 0 }; // Smoothing camera
     screenSpaceCamera.zoom = 1.0f;
 
-    RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight); // This is where we'll draw all our objects.
+    // Load render texture to draw all our objects
+    RenderTexture2D target = LoadRenderTexture(virtualScreenWidth, virtualScreenHeight);
 
     Rectangle rec01 = { 70.0f, 35.0f, 20.0f, 20.0f };
     Rectangle rec02 = { 90.0f, 55.0f, 30.0f, 10.0f };
diff --git a/raylib/examples/core/core_storage_values.c b/raylib/examples/core/core_storage_values.c
--- a/raylib/examples/core/core_storage_values.c
+++ b/raylib/examples/core/core_storage_values.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Storage save/load values
+*   raylib [core] example - storage values
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.4, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,9 @@
     STORAGE_POSITION_HISCORE    = 1
 } StorageData;
 
-// Persistent storage functions
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 static bool SaveStorageValue(unsigned int position, int value);
 static int LoadStorageValue(unsigned int position);
 
@@ -37,7 +41,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - storage save/load values");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - storage values");
 
     int score = 0;
     int hiscore = 0;
@@ -99,6 +103,9 @@
     return 0;
 }
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 // Save integer value to storage file (to defined position)
 // NOTE: Storage positions is directly related to file memory layout (4 bytes each integer)
 bool SaveStorageValue(unsigned int position, int value)
diff --git a/raylib/examples/core/core_text_file_loading.c b/raylib/examples/core/core_text_file_loading.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_text_file_loading.c
@@ -0,0 +1,173 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - text file loading
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Aanjishnu Bhattacharyya (@NimComPoo-04) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 0 Aanjishnu Bhattacharyya (@NimComPoo-04)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"        // Required for: Lerp()
+
+#include <string.h>
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - text file loading");
+
+    // Setting up the camera
+    Camera2D cam = {
+        .offset = {0, 0},
+        .target = {0, 0},
+        .rotation = 0,
+        .zoom = 1
+    };
+
+    // Loading text file from resources/text_file.txt
+    const char *fileName = "resources/text_file.txt";
+    char *text = LoadFileText(fileName);
+
+    // Loading all the text lines
+    int lineCount = 0;
+    char **lines = LoadTextLines(text, &lineCount);
+
+    // Stylistic choises
+    int fontSize = 20;
+    int textTop = 25 + fontSize; // Top of the screen from where the text is rendered
+    int wrapWidth = screenWidth - 20;
+
+    // Wrap the lines as needed
+    for (int i = 0; i < lineCount; i++)
+    {
+        int j = 0;
+        int lastSpace = 0;          // Keeping track of last valid space to insert '\n'
+        int lastWrapStart = 0;      // Keeping track of the start of this wrapped line.
+
+        while (j <= strlen(lines[i]))
+        {
+            if (lines[i][j] == ' ' || lines[i][j] == '\0')
+            {
+                char before = lines[i][j];
+                // Making a C Style string by adding a '\0' at the required location so that we can use the MeasureText function
+                lines[i][j] = '\0';
+
+                // Checking if the text has crossed the wrapWidth, then going back and inserting a newline
+                if (MeasureText(lines[i] + lastWrapStart, fontSize) > wrapWidth)
+                {
+                    lines[i][lastSpace] = '\n';
+
+                    // Since we added a newline the place of wrap changed so we update our lastWrapStart
+                    lastWrapStart = lastSpace + 1;
+                }
+
+                if(before != '\0') lines[i][j] = ' ';  // Resetting the space back
+                lastSpace = j; // Since we encountered a new space we update our last encountered space location
+            }
+
+            j++;
+        }
+    }
+
+    // Calculating the total height so that we can show a scrollbar
+    int textHeight = 0;
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        Vector2 size = MeasureTextEx(GetFontDefault(), lines[i], (float)fontSize, 2);
+        textHeight += (int)size.y + 10;
+    }
+
+    // A simple scrollbar on the side to show how far we have read into the file
+    Rectangle scrollBar = {
+        .x = (float)screenWidth - 5,
+        .y = 0,
+        .width = 5,
+        .height = screenHeight*100.0f/(textHeight - screenHeight) // Scrollbar height is just a percentage
+    };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float scroll = GetMouseWheelMove();
+        cam.target.y -= scroll*fontSize*1.5f;   // Choosing an arbitrary speed for scroll
+
+        if (cam.target.y < 0) cam.target.y = 0;  // Snapping to 0 if we go too far back
+
+        // Ensuring that the camera does not scroll past all text
+        if (cam.target.y > textHeight - screenHeight + textTop)
+            cam.target.y = (float)textHeight - screenHeight + textTop;
+
+        // Computing the position of the scrollBar depending on the percentage of text covered
+        scrollBar.y = Lerp((float)textTop, (float)screenHeight - scrollBar.height, (float)(cam.target.y - textTop)/(textHeight - screenHeight));
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode2D(cam);
+                // Going through all the read lines
+                for (int i = 0, t = textTop; i < lineCount; i++)
+                {
+                    // Each time we go through and calculate the height of the text to move the cursor appropriately
+                    Vector2 size;
+                    if(strcmp(lines[i], "")){
+                        // Fix for empty line in the text file
+                        size = MeasureTextEx( GetFontDefault(), lines[i], (float)fontSize, 2);
+                    }else{
+                        size = MeasureTextEx( GetFontDefault(), " ", (float)fontSize, 2);
+                    }
+
+                    DrawText(lines[i], 10, t, fontSize, RED);
+
+                    // Inserting extra space for real newlines,
+                    // wrapped lines are rendered closer together
+                    t += (int)size.y + 10;
+                }
+            EndMode2D();
+
+            // Header displaying which file is being read currently
+            DrawRectangle(0, 0, screenWidth, textTop - 10, BEIGE);
+            DrawText(TextFormat("File: %s", fileName), 10, 10, fontSize, MAROON);
+
+            DrawRectangleRec(scrollBar, MAROON);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTextLines(lines, lineCount);
+    UnloadFileText(text);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/core/core_undo_redo.c b/raylib/examples/core/core_undo_redo.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_undo_redo.c
@@ -0,0 +1,312 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - undo redo
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>     // Required for: calloc(), free()
+#include <string.h>     // Required for: memcpy(), strcmp()
+
+#define MAX_UNDO_STATES             26      // Maximum undo states supported for the ring buffer
+
+#define GRID_CELL_SIZE              24
+#define MAX_GRID_CELLS_X            30
+#define MAX_GRID_CELLS_Y            13
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Point struct, like Vector2 but using int
+typedef struct {
+    int x;
+    int y;
+} Point;
+
+// Player state struct
+// NOTE: Contains all player data that needs to be affected by undo/redo
+typedef struct {
+    Point cell;
+    Color color;
+} PlayerState;
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+// Draw undo system visualization logic
+static void DrawUndoBuffer(Vector2 position, int firstUndoIndex, int lastUndoIndex, int currentUndoIndex, int slotSize);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    // We have multiple options to implement an Undo/Redo system
+    // Probably the most professional one is using the Command pattern to
+    // define Actions and store those actions into an array as the events happen,
+    // raylib internal Automation System actually uses a similar approach,
+    // but in this example we are using another more simple solution,
+    // just record PlayerState changes when detected, checking for changes every certain frames
+    // This approach requires more memory and is more performance costly but it is quite simple to implement
+
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - undo redo");
+
+    // Undo/redo system variables
+    int currentUndoIndex = 0;
+    int firstUndoIndex = 0;
+    int lastUndoIndex = 0;
+    int undoFrameCounter = 0;
+    Vector2 undoInfoPos = { 110, 400 };
+
+    // Init current player state and undo/redo recorded states array
+    PlayerState player = { 0 };
+    player.cell = (Point){ 10, 10 };
+    player.color = RED;
+
+    // Init undo buffer to store MAX_UNDO_STATES states
+    PlayerState *states = (PlayerState *)RL_CALLOC(MAX_UNDO_STATES, sizeof(PlayerState));
+    // Init all undo states to current state
+    for (int i = 0; i < MAX_UNDO_STATES; i++) memcpy(&states[i], &player, sizeof(PlayerState));
+
+    // Grid variables
+    Vector2 gridPosition = { 40, 60 };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Player movement logic
+        if (IsKeyPressed(KEY_RIGHT)) player.cell.x++;
+        else if (IsKeyPressed(KEY_LEFT)) player.cell.x--;
+        else if (IsKeyPressed(KEY_UP)) player.cell.y--;
+        else if (IsKeyPressed(KEY_DOWN)) player.cell.y++;
+
+        // Make sure player does not go out of bounds
+        if (player.cell.x < 0) player.cell.x = 0;
+        else if (player.cell.x >= MAX_GRID_CELLS_X) player.cell.x = MAX_GRID_CELLS_X - 1;
+        if (player.cell.y < 0) player.cell.y = 0;
+        else if (player.cell.y >= MAX_GRID_CELLS_Y) player.cell.y = MAX_GRID_CELLS_Y - 1;
+
+        // Player color change logic
+        if (IsKeyPressed(KEY_SPACE))
+        {
+            player.color.r = (unsigned char)GetRandomValue(20, 255);
+            player.color.g = (unsigned char)GetRandomValue(20, 220);
+            player.color.b = (unsigned char)GetRandomValue(20, 240);
+        }
+
+        // Undo state change logic
+        undoFrameCounter++;
+
+        // Waiting a number of frames before checking if we should store a new state snapshot
+        if (undoFrameCounter >= 2) // Checking every 2 frames
+        {
+            if (memcmp(&states[currentUndoIndex], &player, sizeof(PlayerState)) != 0)
+            {
+                // Move cursor to next available position of the undo ring buffer to record state
+                currentUndoIndex++;
+                if (currentUndoIndex >= MAX_UNDO_STATES) currentUndoIndex = 0;
+                if (currentUndoIndex == firstUndoIndex) firstUndoIndex++;
+                if (firstUndoIndex >= MAX_UNDO_STATES) firstUndoIndex = 0;
+
+                memcpy(&states[currentUndoIndex], &player, sizeof(PlayerState));
+                lastUndoIndex = currentUndoIndex;
+            }
+
+            undoFrameCounter = 0;
+        }
+
+        // Recover previous state from buffer: CTRL+Z
+        if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_Z))
+        {
+            if (currentUndoIndex != firstUndoIndex)
+            {
+                currentUndoIndex--;
+                if (currentUndoIndex < 0) currentUndoIndex = MAX_UNDO_STATES - 1;
+
+                if (memcmp(&states[currentUndoIndex], &player, sizeof(PlayerState)) != 0)
+                {
+                    memcpy(&player, &states[currentUndoIndex], sizeof(PlayerState));
+                }
+            }
+        }
+
+        // Recover next state from buffer: CTRL+Y
+        if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_Y))
+        {
+            if (currentUndoIndex != lastUndoIndex)
+            {
+                int nextUndoIndex = currentUndoIndex + 1;
+                if (nextUndoIndex >= MAX_UNDO_STATES) nextUndoIndex = 0;
+
+                if (nextUndoIndex != firstUndoIndex)
+                {
+                    currentUndoIndex = nextUndoIndex;
+
+                    if (memcmp(&states[currentUndoIndex], &player, sizeof(PlayerState)) != 0)
+                    {
+                        memcpy(&player, &states[currentUndoIndex], sizeof(PlayerState));
+                    }
+                }
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw controls info
+            DrawText("[ARROWS] MOVE PLAYER - [SPACE] CHANGE PLAYER COLOR", 40, 20, 20, DARKGRAY);
+
+            // Draw player visited cells recorded by undo
+            // NOTE: Remember we are using a ring buffer approach so,
+            // some cells info could start at the end of the array and end at the beginning
+            if (lastUndoIndex > firstUndoIndex)
+            {
+                for (int i = firstUndoIndex; i < currentUndoIndex; i++)
+                    DrawRectangleRec((Rectangle){gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
+                        GRID_CELL_SIZE, GRID_CELL_SIZE }, LIGHTGRAY);
+            }
+            else if (firstUndoIndex > lastUndoIndex)
+            {
+                if ((currentUndoIndex < MAX_UNDO_STATES) && (currentUndoIndex > lastUndoIndex))
+                {
+                    for (int i = firstUndoIndex; i < currentUndoIndex; i++)
+                        DrawRectangleRec((Rectangle) { gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
+                            GRID_CELL_SIZE, GRID_CELL_SIZE }, LIGHTGRAY);
+                }
+                else
+                {
+                    for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
+                        DrawRectangle((int)gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, (int)gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
+                            GRID_CELL_SIZE, GRID_CELL_SIZE, LIGHTGRAY);
+                    for (int i = 0; i < currentUndoIndex; i++)
+                        DrawRectangle((int)gridPosition.x + states[i].cell.x*GRID_CELL_SIZE, (int)gridPosition.y + states[i].cell.y*GRID_CELL_SIZE,
+                            GRID_CELL_SIZE, GRID_CELL_SIZE, LIGHTGRAY);
+                }
+            }
+
+            // Draw game grid
+            for (int y = 0; y <= MAX_GRID_CELLS_Y; y++)
+                DrawLine((int)gridPosition.x, (int)gridPosition.y + y*GRID_CELL_SIZE,
+                    (int)gridPosition.x + MAX_GRID_CELLS_X*GRID_CELL_SIZE, (int)gridPosition.y + y*GRID_CELL_SIZE, GRAY);
+            for (int x = 0; x <= MAX_GRID_CELLS_X; x++)
+                DrawLine((int)gridPosition.x + x*GRID_CELL_SIZE, (int)gridPosition.y,
+                    (int)gridPosition.x + x*GRID_CELL_SIZE, (int)gridPosition.y + MAX_GRID_CELLS_Y*GRID_CELL_SIZE, GRAY);
+
+            // Draw player
+            DrawRectangle((int)gridPosition.x + player.cell.x*GRID_CELL_SIZE, (int)gridPosition.y + player.cell.y*GRID_CELL_SIZE,
+                GRID_CELL_SIZE + 1, GRID_CELL_SIZE + 1, player.color);
+
+            // Draw undo system buffer info
+            DrawText("UNDO STATES:", (int)undoInfoPos.x - 85, (int)undoInfoPos.y + 9, 10, DARKGRAY);
+            DrawUndoBuffer(undoInfoPos, firstUndoIndex, lastUndoIndex, currentUndoIndex, 24);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    RL_FREE(states);        // Free undo states array
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Draw undo system visualization logic
+// NOTE: Visualizing the ring buffer array, every square can store a player state
+static void DrawUndoBuffer(Vector2 position, int firstUndoIndex, int lastUndoIndex, int currentUndoIndex, int slotSize)
+{
+    // Draw index marks
+    DrawRectangle((int)position.x + 8 + slotSize*currentUndoIndex, (int)position.y - 10, 8, 8, RED);
+    DrawRectangleLines((int)position.x + 2 + slotSize*firstUndoIndex, (int)position.y + 27, 8, 8, BLACK);
+    DrawRectangle((int)position.x + 14 + slotSize*lastUndoIndex, (int)position.y + 27, 8, 8, BLACK);
+
+    // Draw background gray slots
+    for (int i = 0; i < MAX_UNDO_STATES; i++)
+    {
+        DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, LIGHTGRAY);
+        DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, GRAY);
+    }
+
+    // Draw occupied slots: firstUndoIndex --> lastUndoIndex
+    if (firstUndoIndex <= lastUndoIndex)
+    {
+        for (int i = firstUndoIndex; i < lastUndoIndex + 1; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, SKYBLUE);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, BLUE);
+        }
+    }
+    else if (lastUndoIndex < firstUndoIndex)
+    {
+        for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, SKYBLUE);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, BLUE);
+        }
+
+        for (int i = 0; i < lastUndoIndex + 1; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, SKYBLUE);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, BLUE);
+        }
+    }
+
+    // Draw occupied slots: firstUndoIndex --> currentUndoIndex
+    if (firstUndoIndex < currentUndoIndex)
+    {
+        for (int i = firstUndoIndex; i < currentUndoIndex; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, GREEN);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, LIME);
+        }
+    }
+    else if (currentUndoIndex < firstUndoIndex)
+    {
+        for (int i = firstUndoIndex; i < MAX_UNDO_STATES; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, GREEN);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, LIME);
+        }
+
+        for (int i = 0; i < currentUndoIndex; i++)
+        {
+            DrawRectangle((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, GREEN);
+            DrawRectangleLines((int)position.x + slotSize*i, (int)position.y, slotSize, slotSize, LIME);
+        }
+    }
+
+    // Draw current selected UNDO slot
+    DrawRectangle((int)position.x + slotSize*currentUndoIndex, (int)position.y, slotSize, slotSize, GOLD);
+    DrawRectangleLines((int)position.x + slotSize*currentUndoIndex, (int)position.y, slotSize, slotSize, ORANGE);
+}
diff --git a/raylib/examples/core/core_viewport_scaling.c b/raylib/examples/core/core_viewport_scaling.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/core_viewport_scaling.c
@@ -0,0 +1,321 @@
+/*******************************************************************************************
+*
+*   raylib [core] example - viewport scaling
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Agnis Aldiņš (@nezvers)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RESOLUTION_COUNT    4   // For iteration purposes and teaching example
+
+typedef enum {
+    // Only upscale, useful for pixel art
+    KEEP_ASPECT_INTEGER,
+    KEEP_HEIGHT_INTEGER,
+    KEEP_WIDTH_INTEGER,
+    // Can also downscale
+    KEEP_ASPECT,
+    KEEP_HEIGHT,
+    KEEP_WIDTH,
+    // For itteration purposes and as a teaching example
+    VIEWPORT_TYPE_COUNT,
+} ViewportType;
+
+// For displaying on GUI
+const char *ViewportTypeNames[VIEWPORT_TYPE_COUNT] = {
+    "KEEP_ASPECT_INTEGER",
+    "KEEP_HEIGHT_INTEGER",
+    "KEEP_WIDTH_INTEGER",
+    "KEEP_ASPECT",
+    "KEEP_HEIGHT",
+    "KEEP_WIDTH",
+};
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
+static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target);
+
+// Example how to calculate position on RenderTexture
+static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //---------------------------------------------------------
+    int screenWidth = 800;
+    int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - viewport scaling");
+
+    // Preset resolutions that could be created by subdividing screen resolution
+    Vector2 resolutionList[RESOLUTION_COUNT] = {
+        (Vector2){ 64, 64 },
+        (Vector2){ 256, 240 },
+        (Vector2){ 320, 180 },
+        // 4K doesn't work with integer scaling but included for example purposes with non-integer scaling
+        (Vector2){ 3840, 2160 },
+    };
+
+    int resolutionIndex = 0;
+    int gameWidth = 64;
+    int gameHeight = 64;
+
+    RenderTexture2D target = (RenderTexture2D){ 0 };
+    Rectangle sourceRect = (Rectangle){ 0 };
+    Rectangle destRect = (Rectangle){ 0 };
+
+    ViewportType viewportType = KEEP_ASPECT_INTEGER;
+    ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+
+    // Button rectangles
+    Rectangle decreaseResolutionButton = (Rectangle){ 200, 30, 10, 10 };
+    Rectangle increaseResolutionButton = (Rectangle){ 215, 30, 10, 10 };
+    Rectangle decreaseTypeButton = (Rectangle){ 200, 45, 10, 10 };
+    Rectangle increaseTypeButton = (Rectangle){ 215, 45, 10, 10 };
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //----------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsWindowResized()) ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+
+        Vector2 mousePosition = GetMousePosition();
+        bool mousePressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
+
+        // Check buttons and rescale
+        if (CheckCollisionPointRec(mousePosition, decreaseResolutionButton) && mousePressed)
+        {
+            resolutionIndex = (resolutionIndex + RESOLUTION_COUNT - 1)%RESOLUTION_COUNT;
+            gameWidth = (int)resolutionList[resolutionIndex].x;
+            gameHeight = (int)resolutionList[resolutionIndex].y;
+            ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+        }
+
+        if (CheckCollisionPointRec(mousePosition, increaseResolutionButton) && mousePressed)
+        {
+            resolutionIndex = (resolutionIndex + 1)%RESOLUTION_COUNT;
+            gameWidth = (int)resolutionList[resolutionIndex].x;
+            gameHeight = (int)resolutionList[resolutionIndex].y;
+            ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+        }
+
+        if (CheckCollisionPointRec(mousePosition, decreaseTypeButton) && mousePressed)
+        {
+            viewportType = (viewportType + VIEWPORT_TYPE_COUNT - 1)%VIEWPORT_TYPE_COUNT;
+            ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+        }
+
+        if (CheckCollisionPointRec(mousePosition, increaseTypeButton) && mousePressed)
+        {
+            viewportType = (viewportType + 1)%VIEWPORT_TYPE_COUNT;
+            ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
+        }
+
+        Vector2 textureMousePosition = Screen2RenderTexturePosition(mousePosition, &sourceRect, &destRect);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        // Draw our scene to the render texture
+        BeginTextureMode(target);
+            ClearBackground(WHITE);
+            DrawCircleV(textureMousePosition, 20.0f, LIME);
+        EndTextureMode();
+
+        // Draw render texture to main framebuffer
+        BeginDrawing();
+            ClearBackground(BLACK);
+
+            // Draw our render texture with rotation applied
+            DrawTexturePro(target.texture, sourceRect, destRect, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
+
+            // Draw Native resolution (GUI or anything)
+            // Draw info box
+            Rectangle infoRect = (Rectangle){5, 5, 330, 105};
+            DrawRectangleRec(infoRect, Fade(LIGHTGRAY, 0.7f));
+            DrawRectangleLinesEx(infoRect, 1, BLUE);
+
+            DrawText(TextFormat("Window Resolution: %d x %d", screenWidth, screenHeight), 15, 15, 10, BLACK);
+            DrawText(TextFormat("Game Resolution: %d x %d", gameWidth, gameHeight), 15, 30, 10, BLACK);
+
+            DrawText(TextFormat("Type: %s", ViewportTypeNames[viewportType]), 15, 45, 10, BLACK);
+            Vector2 scaleRatio = (Vector2){destRect.width/sourceRect.width, -destRect.height/sourceRect.height};
+            if (scaleRatio.x < 0.001f || scaleRatio.y < 0.001f) DrawText(TextFormat("Scale ratio: INVALID"), 15, 60, 10, BLACK);
+            else DrawText(TextFormat("Scale ratio: %.2f x %.2f", scaleRatio.x, scaleRatio.y), 15, 60, 10, BLACK);
+
+            DrawText(TextFormat("Source size: %.2f x %.2f", sourceRect.width, -sourceRect.height), 15, 75, 10, BLACK);
+            DrawText(TextFormat("Destination size: %.2f x %.2f", destRect.width, destRect.height), 15, 90, 10, BLACK);
+
+            // Draw buttons
+            DrawRectangleRec(decreaseTypeButton, SKYBLUE);
+            DrawRectangleRec(increaseTypeButton, SKYBLUE);
+            DrawRectangleRec(decreaseResolutionButton, SKYBLUE);
+            DrawRectangleRec(increaseResolutionButton, SKYBLUE);
+            DrawText("<", decreaseTypeButton.x + 3, decreaseTypeButton.y + 1, 10, BLACK);
+            DrawText(">", increaseTypeButton.x + 3, increaseTypeButton.y + 1, 10, BLACK);
+            DrawText("<", decreaseResolutionButton.x + 3, decreaseResolutionButton.y + 1, 10, BLACK);
+            DrawText(">", increaseResolutionButton.x + 3, increaseResolutionButton.y + 1, 10, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //----------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //----------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    sourceRect->x = 0.0f;
+    sourceRect->y = (float)gameHeight;
+    sourceRect->width = (float)gameWidth;
+    sourceRect->height = (float)-gameHeight;
+
+    const int ratio_x = (screenWidth/gameWidth);
+    const int ratio_y = (screenHeight/gameHeight);
+    const float resizeRatio = (float)((ratio_x < ratio_y)? ratio_x : ratio_y);
+
+    destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(gameWidth*resizeRatio);
+    destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    const float resizeRatio = (float)(screenHeight/gameHeight);
+    sourceRect->x = 0.0f;
+    sourceRect->y = 0.0f;
+    sourceRect->width = (float)(int)(screenWidth/resizeRatio);
+    sourceRect->height = (float)-gameHeight;
+
+    destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(sourceRect->width*resizeRatio);
+    destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    const float resizeRatio = (float)(screenWidth/gameWidth);
+    sourceRect->x = 0.0f;
+    sourceRect->y = 0.0f;
+    sourceRect->width = (float)gameWidth;
+    sourceRect->height = (float)(int)(screenHeight/resizeRatio);
+
+    destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(gameWidth*resizeRatio);
+    destRect->height = (float)(int)(sourceRect->height*resizeRatio);
+
+    sourceRect->height *= -1.0f;
+}
+
+static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    sourceRect->x = 0.0f;
+    sourceRect->y = (float)gameHeight;
+    sourceRect->width = (float)gameWidth;
+    sourceRect->height = (float)-gameHeight;
+
+    const float ratio_x = ((float)screenWidth/(float)gameWidth);
+    const float ratio_y = ((float)screenHeight/(float)gameHeight);
+    const float resizeRatio = (ratio_x < ratio_y ? ratio_x : ratio_y);
+
+    destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(gameWidth*resizeRatio);
+    destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    const float resizeRatio = ((float)screenHeight/(float)gameHeight);
+    sourceRect->x = 0.0f;
+    sourceRect->y = 0.0f;
+    sourceRect->width = (float)(int)((float)screenWidth/resizeRatio);
+    sourceRect->height = (float)-gameHeight;
+
+    destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(sourceRect->width*resizeRatio);
+    destRect->height = (float)(int)(gameHeight*resizeRatio);
+}
+
+static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
+{
+    const float resizeRatio = ((float)screenWidth/(float)gameWidth);
+    sourceRect->x = 0.0f;
+    sourceRect->y = 0.0f;
+    sourceRect->width = (float)gameWidth;
+    sourceRect->height = (float)(int)((float)screenHeight/resizeRatio);
+
+    destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
+    destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
+    destRect->width = (float)(int)(gameWidth*resizeRatio);
+    destRect->height = (float)(int)(sourceRect->height*resizeRatio);
+
+    sourceRect->height *= -1.f;
+}
+
+static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target)
+{
+    *screenWidth = GetScreenWidth();
+    *screenHeight = GetScreenHeight();
+
+    switch(viewportType)
+    {
+        case KEEP_ASPECT_INTEGER: KeepAspectCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        case KEEP_HEIGHT_INTEGER: KeepHeightCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        case KEEP_WIDTH_INTEGER: KeepWidthCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        case KEEP_ASPECT: KeepAspectCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        case KEEP_HEIGHT: KeepHeightCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        case KEEP_WIDTH: KeepWidthCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
+        default: break;
+    }
+
+    UnloadRenderTexture(*target);
+    *target = LoadRenderTexture(sourceRect->width, -sourceRect->height);
+}
+
+// Example how to calculate position on RenderTexture
+static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect)
+{
+    Vector2 relativePosition = {point.x - scaledRect->x, point.y - scaledRect->y};
+    Vector2 ratio = {textureRect->width/scaledRect->width, -textureRect->height/scaledRect->height};
+
+    return (Vector2){relativePosition.x*ratio.x, relativePosition.y*ratio.x};
+}
diff --git a/raylib/examples/core/core_vr_simulator.c b/raylib/examples/core/core_vr_simulator.c
--- a/raylib/examples/core/core_vr_simulator.c
+++ b/raylib/examples/core/core_vr_simulator.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - VR Simulator (Oculus Rift CV1 parameters)
+*   raylib [core] example - vr simulator
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -59,7 +61,7 @@
     VrStereoConfig config = LoadVrStereoConfig(device);
 
     // Distortion shader (uses device lens distortion and chroma)
-    Shader distortion = LoadShader(0, TextFormat("resources/distortion%i.fs", GLSL_VERSION));
+    Shader distortion = LoadShader(0, TextFormat("resources/shaders/glsl%i/distortion.fs", GLSL_VERSION));
 
     // Update distortion shader with lens and distortion-scale parameters
     SetShaderValue(distortion, GetShaderLocation(distortion, "leftLensCenter"),
@@ -125,7 +127,7 @@
                 EndMode3D();
             EndVrStereoMode();
         EndTextureMode();
-        
+
         BeginDrawing();
             ClearBackground(RAYWHITE);
             BeginShaderMode(distortion);
diff --git a/raylib/examples/core/core_window_flags.c b/raylib/examples/core/core_window_flags.c
--- a/raylib/examples/core/core_window_flags.c
+++ b/raylib/examples/core/core_window_flags.c
@@ -2,12 +2,14 @@
 *
 *   raylib [core] example - window flags
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 3.5, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -41,16 +43,16 @@
     */
 
     // Set configuration flags for window creation
-    //SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);
+    //SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);// | FLAG_WINDOW_TRANSPARENT);
     InitWindow(screenWidth, screenHeight, "raylib [core] example - window flags");
 
-    Vector2 ballPosition = { GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f };
+    Vector2 ballPosition = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
     Vector2 ballSpeed = { 5.0f, 4.0f };
     float ballRadius = 20;
 
     int framesCounter = 0;
 
-    //SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //----------------------------------------------------------
 
     // Main game loop
@@ -95,7 +97,11 @@
         if (IsWindowState(FLAG_WINDOW_MINIMIZED))
         {
             framesCounter++;
-            if (framesCounter >= 240) RestoreWindow(); // Restore window after 3 seconds
+            if (framesCounter >= 240)
+            {
+                RestoreWindow(); // Restore window after 3 seconds
+                framesCounter = 0;
+            }
         }
 
         if (IsKeyPressed(KEY_M))
@@ -129,6 +135,9 @@
             else SetWindowState(FLAG_VSYNC_HINT);
         }
 
+        if (IsKeyPressed(KEY_B)) ToggleBorderlessWindowed();
+
+
         // Bouncing ball logic
         ballPosition.x += ballSpeed.x;
         ballPosition.y += ballSpeed.y;
@@ -161,9 +170,9 @@
         if (IsWindowState(FLAG_WINDOW_UNDECORATED)) DrawText("[D] FLAG_WINDOW_UNDECORATED: on", 10, 120, 10, LIME);
         else DrawText("[D] FLAG_WINDOW_UNDECORATED: off", 10, 120, 10, MAROON);
         if (IsWindowState(FLAG_WINDOW_HIDDEN)) DrawText("[H] FLAG_WINDOW_HIDDEN: on", 10, 140, 10, LIME);
-        else DrawText("[H] FLAG_WINDOW_HIDDEN: off", 10, 140, 10, MAROON);
+        else DrawText("[H] FLAG_WINDOW_HIDDEN: off (hides for 3 seconds)", 10, 140, 10, MAROON);
         if (IsWindowState(FLAG_WINDOW_MINIMIZED)) DrawText("[N] FLAG_WINDOW_MINIMIZED: on", 10, 160, 10, LIME);
-        else DrawText("[N] FLAG_WINDOW_MINIMIZED: off", 10, 160, 10, MAROON);
+        else DrawText("[N] FLAG_WINDOW_MINIMIZED: off (restores after 3 seconds)", 10, 160, 10, MAROON);
         if (IsWindowState(FLAG_WINDOW_MAXIMIZED)) DrawText("[M] FLAG_WINDOW_MAXIMIZED: on", 10, 180, 10, LIME);
         else DrawText("[M] FLAG_WINDOW_MAXIMIZED: off", 10, 180, 10, MAROON);
         if (IsWindowState(FLAG_WINDOW_UNFOCUSED)) DrawText("[G] FLAG_WINDOW_UNFOCUSED: on", 10, 200, 10, LIME);
@@ -174,14 +183,16 @@
         else DrawText("[A] FLAG_WINDOW_ALWAYS_RUN: off", 10, 240, 10, MAROON);
         if (IsWindowState(FLAG_VSYNC_HINT)) DrawText("[V] FLAG_VSYNC_HINT: on", 10, 260, 10, LIME);
         else DrawText("[V] FLAG_VSYNC_HINT: off", 10, 260, 10, MAROON);
+        if (IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) DrawText("[B] FLAG_BORDERLESS_WINDOWED_MODE: on", 10, 280, 10, LIME);
+        else DrawText("[B] FLAG_BORDERLESS_WINDOWED_MODE: off", 10, 280, 10, MAROON);
 
-        DrawText("Following flags can only be set before window creation:", 10, 300, 10, GRAY);
-        if (IsWindowState(FLAG_WINDOW_HIGHDPI)) DrawText("FLAG_WINDOW_HIGHDPI: on", 10, 320, 10, LIME);
-        else DrawText("FLAG_WINDOW_HIGHDPI: off", 10, 320, 10, MAROON);
-        if (IsWindowState(FLAG_WINDOW_TRANSPARENT)) DrawText("FLAG_WINDOW_TRANSPARENT: on", 10, 340, 10, LIME);
-        else DrawText("FLAG_WINDOW_TRANSPARENT: off", 10, 340, 10, MAROON);
-        if (IsWindowState(FLAG_MSAA_4X_HINT)) DrawText("FLAG_MSAA_4X_HINT: on", 10, 360, 10, LIME);
-        else DrawText("FLAG_MSAA_4X_HINT: off", 10, 360, 10, MAROON);
+        DrawText("Following flags can only be set before window creation:", 10, 320, 10, GRAY);
+        if (IsWindowState(FLAG_WINDOW_HIGHDPI)) DrawText("FLAG_WINDOW_HIGHDPI: on", 10, 340, 10, LIME);
+        else DrawText("FLAG_WINDOW_HIGHDPI: off", 10, 340, 10, MAROON);
+        if (IsWindowState(FLAG_WINDOW_TRANSPARENT)) DrawText("FLAG_WINDOW_TRANSPARENT: on", 10, 360, 10, LIME);
+        else DrawText("FLAG_WINDOW_TRANSPARENT: off", 10, 360, 10, MAROON);
+        if (IsWindowState(FLAG_MSAA_4X_HINT)) DrawText("FLAG_MSAA_4X_HINT: on", 10, 380, 10, LIME);
+        else DrawText("FLAG_MSAA_4X_HINT: off", 10, 380, 10, MAROON);
 
         EndDrawing();
         //-----------------------------------------------------
diff --git a/raylib/examples/core/core_window_letterbox.c b/raylib/examples/core/core_window_letterbox.c
--- a/raylib/examples/core/core_window_letterbox.c
+++ b/raylib/examples/core/core_window_letterbox.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - window scale letterbox (and virtual mouse)
+*   raylib [core] example - window letterbox
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
 *   Example contributed by Anata (@anatagawa) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Anata (@anatagawa) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Anata (@anatagawa) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -25,12 +27,12 @@
 //------------------------------------------------------------------------------------
 int main(void)
 {
-    const int windowWidth = 800;
-    const int windowHeight = 450;
+    const int screenWidth = 800;
+    const int screenHeight = 450;
 
     // Enable config flags for resizable window and vertical synchro
     SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
-    InitWindow(windowWidth, windowHeight, "raylib [core] example - window scale letterbox");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - window letterbox");
     SetWindowMinSize(320, 240);
 
     int gameScreenWidth = 640;
@@ -84,7 +86,7 @@
             DrawText(TextFormat("Default Mouse: [%i , %i]", (int)mouse.x, (int)mouse.y), 350, 25, 20, GREEN);
             DrawText(TextFormat("Virtual Mouse: [%i , %i]", (int)virtualMouse.x, (int)virtualMouse.y), 350, 55, 20, YELLOW);
         EndTextureMode();
-        
+
         BeginDrawing();
             ClearBackground(BLACK);     // Clear screen background
 
diff --git a/raylib/examples/core/core_window_should_close.c b/raylib/examples/core/core_window_should_close.c
--- a/raylib/examples/core/core_window_should_close.c
+++ b/raylib/examples/core/core_window_should_close.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Window should close
+*   raylib [core] example - window should close
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -16,7 +18,7 @@
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
@@ -24,9 +26,9 @@
     const int screenHeight = 450;
 
     InitWindow(screenWidth, screenHeight, "raylib [core] example - window should close");
-    
+
     SetExitKey(KEY_NULL);       // Disable KEY_ESCAPE to close window, X-button still works
-    
+
     bool exitWindowRequested = false;   // Flag to request window to exit
     bool exitWindow = false;    // Flag to set window to exit
 
@@ -40,12 +42,12 @@
         //----------------------------------------------------------------------------------
         // Detect if X-button or KEY_ESCAPE have been pressed to close window
         if (WindowShouldClose() || IsKeyPressed(KEY_ESCAPE)) exitWindowRequested = true;
-        
+
         if (exitWindowRequested)
         {
             // A request for close window has been issued, we can save data before closing
             // or just show a message asking for confirmation
-            
+
             if (IsKeyPressed(KEY_Y)) exitWindow = true;
             else if (IsKeyPressed(KEY_N)) exitWindowRequested = false;
         }
diff --git a/raylib/examples/core/core_world_screen.c b/raylib/examples/core/core_world_screen.c
--- a/raylib/examples/core/core_world_screen.c
+++ b/raylib/examples/core/core_world_screen.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - World to screen
+*   raylib [core] example - world screen
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 1.4
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - core world screen");
+    InitWindow(screenWidth, screenHeight, "raylib [core] example - world screen");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -67,8 +69,8 @@
 
             EndMode3D();
 
-            DrawText("Enemy: 100 / 100", (int)cubeScreenPosition.x - MeasureText("Enemy: 100/100", 20)/2, (int)cubeScreenPosition.y, 20, BLACK);
-            
+            DrawText("Enemy: 100/100", (int)cubeScreenPosition.x - MeasureText("Enemy: 100/100", 20)/2, (int)cubeScreenPosition.y, 20, BLACK);
+
             DrawText(TextFormat("Cube position in screen space coordinates: [%i, %i]", (int)cubeScreenPosition.x, (int)cubeScreenPosition.y), 10, 10, 20, LIME);
             DrawText("Text 2d should be always on top of the cube", 10, 40, 20, GRAY);
 
diff --git a/raylib/examples/core/msf_gif.h b/raylib/examples/core/msf_gif.h
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/msf_gif.h
@@ -0,0 +1,717 @@
+/*
+HOW TO USE:
+
+    In exactly one translation unit (.c or .cpp file), #define MSF_GIF_IMPL before including the header, like so:
+
+    #define MSF_GIF_IMPL
+    #include "msf_gif.h"
+
+    Everywhere else, just include the header like normal.
+
+
+USAGE EXAMPLE:
+
+    int width = 480, height = 320, centisecondsPerFrame = 5, bitDepth = 16;
+    MsfGifState gifState = {};
+    // msf_gif_bgra_flag = true; //optionally, set this flag if your pixels are in BGRA format instead of RGBA
+    // msf_gif_alpha_threshold = 128; //optionally, enable transparency (see function documentation below for details)
+    msf_gif_begin(&gifState, width, height);
+    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 1
+    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 2
+    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 3, etc...
+    MsfGifResult result = msf_gif_end(&gifState);
+    if (result.data) {
+        FILE * fp = fopen("MyGif.gif", "wb");
+        fwrite(result.data, result.dataSize, 1, fp);
+        fclose(fp);
+    }
+    msf_gif_free(result);
+
+Detailed function documentation can be found in the header section below.
+
+
+ERROR HANDLING:
+
+    If memory allocation fails, the functions will signal the error via their return values.
+    If one function call fails, the library will free all of its allocations,
+    and all subsequent calls will safely no-op and return 0 until the next call to `msf_gif_begin()`.
+    Therefore, it's safe to check only the return value of `msf_gif_end()`.
+
+
+REPLACING MALLOC:
+
+    This library uses malloc+realloc+free internally for memory allocation.
+    To facilitate integration with custom memory allocators, these calls go through macros, which can be redefined.
+    The expected function signature equivalents of the macros are as follows:
+
+    void * MSF_GIF_MALLOC(void * context, size_t newSize)
+    void * MSF_GIF_REALLOC(void * context, void * oldMemory, size_t oldSize, size_t newSize)
+    void MSF_GIF_FREE(void * context, void * oldMemory, size_t oldSize)
+
+    If your allocator needs a context pointer, you can set the `customAllocatorContext` field of the MsfGifState struct
+    before calling msf_gif_begin(), and it will be passed to all subsequent allocator macro calls.
+
+    The maximum number of bytes the library will allocate to encode a single gif is bounded by the following formula:
+    `(2 * 1024 * 1024) + (width * height * 8) + ((1024 + width * height * 1.5) * 3 * frameCount)`
+    The peak heap memory usage in bytes, if using a general-purpose heap allocator, is bounded by the following formula:
+    `(2 * 1024 * 1024) + (width * height * 9.5) + 1024 + (16 * frameCount) + (2 * sizeOfResultingGif)
+
+
+See end of file for license information.
+*/
+
+//version 2.2
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// HEADER                                                                                                           ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifndef MSF_GIF_H
+#define MSF_GIF_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+typedef struct {
+    void * data;
+    size_t dataSize;
+
+    size_t allocSize; //internal use
+    void * contextPointer; //internal use
+} MsfGifResult;
+
+typedef struct { //internal use
+    uint32_t * pixels;
+    int depth, count, rbits, gbits, bbits;
+} MsfCookedFrame;
+
+typedef struct MsfGifBuffer {
+    struct MsfGifBuffer * next;
+    size_t size;
+    uint8_t data[1];
+} MsfGifBuffer;
+
+typedef size_t (* MsfGifFileWriteFunc) (const void * buffer, size_t size, size_t count, void * stream);
+typedef struct {
+    MsfGifFileWriteFunc fileWriteFunc;
+    void * fileWriteData;
+    MsfCookedFrame previousFrame;
+    MsfCookedFrame currentFrame;
+    int16_t * lzwMem;
+    MsfGifBuffer * listHead;
+    MsfGifBuffer * listTail;
+    int width, height;
+    void * customAllocatorContext;
+    int framesSubmitted; //needed for transparency to work correctly (because we reach into the previous frame)
+} MsfGifState;
+
+#ifdef __cplusplus
+extern "C" {
+#endif //__cplusplus
+
+/**
+ * @param width                Image width in pixels.
+ * @param height               Image height in pixels.
+ * @return                     Non-zero on success, 0 on error.
+ */
+int msf_gif_begin(MsfGifState * handle, int width, int height);
+
+/**
+ * @param pixelData            Pointer to raw framebuffer data. Rows must be contiguous in memory, in RGBA8 format
+ *                             (or BGRA8 if you have set `msf_gif_bgra_flag = true`).
+ *                             Note: This function does NOT free `pixelData`. You must free it yourself afterwards.
+ * @param centiSecondsPerFrame How many hundredths of a second this frame should be displayed for.
+ *                             Note: This being specified in centiseconds is a limitation of the GIF format.
+ * @param maxBitDepth          Limits how many bits per pixel can be used when quantizing the gif.
+ *                             The actual bit depth chosen for a given frame will be less than or equal to
+ *                             the supplied maximum, depending on the variety of colors used in the frame.
+ *                             `maxBitDepth` will be clamped between 1 and 16. The recommended default is 16.
+ *                             Lowering this value can result in faster exports and smaller gifs,
+ *                             but the quality may suffer.
+ *                             Please experiment with this value to find what works best for your application.
+ * @param pitchInBytes         The number of bytes from the beginning of one row of pixels to the beginning of the next.
+ *                             If you want to flip the image, just pass in a negative pitch.
+ * @return                     Non-zero on success, 0 on error.
+ */
+int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
+
+/**
+ * @return                     A block of memory containing the gif file data, or NULL on error.
+ *                             You are responsible for freeing this via `msf_gif_free()`.
+ */
+MsfGifResult msf_gif_end(MsfGifState * handle);
+
+/**
+ * @param result                The MsfGifResult struct, verbatim as it was returned from `msf_gif_end()`.
+ */
+void msf_gif_free(MsfGifResult result);
+
+//The gif format only supports 1-bit transparency, meaning a pixel will either be fully transparent or fully opaque.
+//Pixels with an alpha value less than the alpha threshold will be treated as transparent.
+//To enable exporting transparent gifs, set it to a value between 1 and 255 (inclusive) before calling msf_gif_frame().
+//Setting it to 0 causes the alpha channel to be ignored. Its initial value is 0.
+extern int msf_gif_alpha_threshold;
+
+//Set `msf_gif_bgra_flag = true` before calling `msf_gif_frame()` if your pixels are in BGRA byte order instead of RBGA.
+extern int msf_gif_bgra_flag;
+
+
+
+//TO-FILE FUNCTIONS
+//These functions are equivalent to the ones above, but they write results to a file incrementally,
+//instead of building a buffer in memory. This can result in lower memory usage when saving large gifs,
+//because memory usage is bounded by only the size of a single frame, and is not dependent on the number of frames.
+//There is currently no reason to use these unless you are on a memory-constrained platform.
+//If in doubt about which API to use, for now you should use the normal (non-file) functions above.
+//The signature of MsfGifFileWriteFunc matches fwrite for convenience, so that you can use the C file API like so:
+//  FILE * fp = fopen("MyGif.gif", "wb");
+//  msf_gif_begin_to_file(&handle, width, height, (MsfGifFileWriteFunc) fwrite, (void *) fp);
+//  msf_gif_frame_to_file(...)
+//  msf_gif_end_to_file(&handle);
+//  fclose(fp);
+//If you use a custom file write function, you must take care to return the same values that fwrite() would return.
+//Note that all three functions will potentially write to the file.
+int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer);
+int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
+int msf_gif_end_to_file(MsfGifState * handle); //returns 0 on error and non-zero on success
+
+#ifdef __cplusplus
+}
+#endif //__cplusplus
+
+#endif //MSF_GIF_H
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// IMPLEMENTATION                                                                                                   ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef MSF_GIF_IMPL
+#ifndef MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
+#define MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
+
+//ensure the library user has either defined all of malloc/realloc/free, or none
+#if defined(MSF_GIF_MALLOC) && defined(MSF_GIF_REALLOC) && defined(MSF_GIF_FREE) //ok
+#elif !defined(MSF_GIF_MALLOC) && !defined(MSF_GIF_REALLOC) && !defined(MSF_GIF_FREE) //ok
+#else
+#error "You must either define all of MSF_GIF_MALLOC, MSF_GIF_REALLOC, and MSF_GIF_FREE, or define none of them"
+#endif
+
+//provide default allocator definitions that redirect to the standard global allocator
+#if !defined(MSF_GIF_MALLOC)
+#include <stdlib.h> //malloc, etc.
+#define MSF_GIF_MALLOC(contextPointer, newSize) malloc(newSize)
+#define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) realloc(oldMemory, newSize)
+#define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) free(oldMemory)
+#endif
+
+//instrumentation for capturing profiling traces (useless for the library user, but useful for the library author)
+#ifdef MSF_GIF_ENABLE_TRACING
+#define MsfTimeFunc TimeFunc
+#define MsfTimeLoop TimeLoop
+#define msf_init_profiling_thread init_profiling_thread
+#else
+#define MsfTimeFunc
+#define MsfTimeLoop(name)
+#define msf_init_profiling_thread()
+#endif //MSF_GIF_ENABLE_TRACING
+
+#include <string.h> //memcpy
+
+//TODO: use compiler-specific notation to force-inline functions currently marked inline
+#if defined(__GNUC__) //gcc, clang
+static inline int msf_bit_log(int i) { return 32 - __builtin_clz(i); }
+#elif defined(_MSC_VER) //msvc
+#include <intrin.h>
+static inline int msf_bit_log(int i) { unsigned long idx; _BitScanReverse(&idx, i); return idx + 1; }
+#else //fallback implementation for other compilers
+//from https://stackoverflow.com/a/31718095/3064745 - thanks!
+static inline int msf_bit_log(int i) {
+    static const int MultiplyDeBruijnBitPosition[32] = {
+        0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
+        8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31,
+    };
+    i |= i >> 1;
+    i |= i >> 2;
+    i |= i >> 4;
+    i |= i >> 8;
+    i |= i >> 16;
+    return MultiplyDeBruijnBitPosition[(uint32_t)(i * 0x07C4ACDDU) >> 27] + 1;
+}
+#endif
+static inline int msf_imin(int a, int b) { return a < b? a : b; }
+static inline int msf_imax(int a, int b) { return b < a? a : b; }
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Frame Cooking                                                                                                    ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
+#include <emmintrin.h>
+#endif
+
+int msf_gif_alpha_threshold = 0;
+int msf_gif_bgra_flag = 0;
+
+static void msf_cook_frame(MsfCookedFrame * frame, uint8_t * raw, uint8_t * used,
+                           int width, int height, int pitch, int depth)
+{ MsfTimeFunc
+    //bit depth for each channel
+    static const int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
+    static const int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 };
+    static const int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
+    //this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers
+    //in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays.
+    const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray;
+    const int * gdepths =                                   gdepthsArray;
+    const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray;
+
+    static const int ditherKernel[16] = {
+         0 << 12,  8 << 12,  2 << 12, 10 << 12,
+        12 << 12,  4 << 12, 14 << 12,  6 << 12,
+         3 << 12, 11 << 12,  1 << 12,  9 << 12,
+        15 << 12,  7 << 12, 13 << 12,  5 << 12,
+    };
+
+    uint32_t * cooked = frame->pixels;
+    int count = 0;
+    MsfTimeLoop("do") do {
+        int rbits = rdepths[depth], gbits = gdepths[depth], bbits = bdepths[depth];
+        int paletteSize = (1 << (rbits + gbits + bbits)) + 1;
+        memset(used, 0, paletteSize * sizeof(uint8_t));
+
+        //TODO: document what this math does and why it's correct
+        int rdiff = (1 << (8 - rbits)) - 1;
+        int gdiff = (1 << (8 - gbits)) - 1;
+        int bdiff = (1 << (8 - bbits)) - 1;
+        short rmul = (short) ((255.0f - rdiff) / 255.0f * 257);
+        short gmul = (short) ((255.0f - gdiff) / 255.0f * 257);
+        short bmul = (short) ((255.0f - bdiff) / 255.0f * 257);
+
+        int gmask = ((1 << gbits) - 1) << rbits;
+        int bmask = ((1 << bbits) - 1) << rbits << gbits;
+
+        MsfTimeLoop("cook") for (int y = 0; y < height; ++y) {
+            int x = 0;
+
+            #if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
+                __m128i k = _mm_loadu_si128((__m128i *) &ditherKernel[(y & 3) * 4]);
+                __m128i k2 = _mm_or_si128(_mm_srli_epi32(k, rbits), _mm_slli_epi32(_mm_srli_epi32(k, bbits), 16));
+                for (; x < width - 3; x += 4) {
+                    uint8_t * pixels = &raw[y * pitch + x * 4];
+                    __m128i p = _mm_loadu_si128((__m128i *) pixels);
+
+                    __m128i rb = _mm_and_si128(p, _mm_set1_epi32(0x00FF00FF));
+                    __m128i rb1 = _mm_mullo_epi16(rb, _mm_set_epi16(bmul, rmul, bmul, rmul, bmul, rmul, bmul, rmul));
+                    __m128i rb2 = _mm_adds_epu16(rb1, k2);
+                    __m128i r3 = _mm_srli_epi32(_mm_and_si128(rb2, _mm_set1_epi32(0x0000FFFF)), 16 - rbits);
+                    __m128i b3 = _mm_and_si128(_mm_srli_epi32(rb2, 32 - rbits - gbits - bbits), _mm_set1_epi32(bmask));
+
+                    __m128i g = _mm_and_si128(_mm_srli_epi32(p, 8), _mm_set1_epi32(0x000000FF));
+                    __m128i g1 = _mm_mullo_epi16(g, _mm_set1_epi32(gmul));
+                    __m128i g2 = _mm_adds_epu16(g1, _mm_srli_epi32(k, gbits));
+                    __m128i g3 = _mm_and_si128(_mm_srli_epi32(g2, 16 - rbits - gbits), _mm_set1_epi32(gmask));
+
+                    __m128i out = _mm_or_si128(_mm_or_si128(r3, g3), b3);
+
+                    //mask in transparency based on threshold
+                    //NOTE: we can theoretically do a sub instead of srli by doing an unsigned compare via bias
+                    //      to maybe save a TINY amount of throughput? but lol who cares maybe I'll do it later -m
+                    __m128i invAlphaMask = _mm_cmplt_epi32(_mm_srli_epi32(p, 24), _mm_set1_epi32(msf_gif_alpha_threshold));
+                    out = _mm_or_si128(_mm_and_si128(invAlphaMask, _mm_set1_epi32(paletteSize - 1)), _mm_andnot_si128(invAlphaMask, out));
+
+                    //TODO: does storing this as a __m128i then reading it back as a uint32_t violate strict aliasing?
+                    uint32_t * c = &cooked[y * width + x];
+                    _mm_storeu_si128((__m128i *) c, out);
+                }
+            #endif
+
+            //scalar cleanup loop
+            for (; x < width; ++x) {
+                uint8_t * p = &raw[y * pitch + x * 4];
+
+                //transparent pixel if alpha is low
+                if (p[3] < msf_gif_alpha_threshold) {
+                    cooked[y * width + x] = paletteSize - 1;
+                    continue;
+                }
+
+                int dx = x & 3, dy = y & 3;
+                int k = ditherKernel[dy * 4 + dx];
+                cooked[y * width + x] =
+                    (msf_imin(65535, p[2] * bmul + (k >> bbits)) >> (16 - rbits - gbits - bbits) & bmask) |
+                    (msf_imin(65535, p[1] * gmul + (k >> gbits)) >> (16 - rbits - gbits        ) & gmask) |
+                     msf_imin(65535, p[0] * rmul + (k >> rbits)) >> (16 - rbits                );
+            }
+        }
+
+        count = 0;
+        MsfTimeLoop("mark") for (int i = 0; i < width * height; ++i) {
+            used[cooked[i]] = 1;
+        }
+
+        //count used colors, transparent is ignored
+        MsfTimeLoop("count") for (int j = 0; j < paletteSize - 1; ++j) {
+            count += used[j];
+        }
+    } while (count >= 256 && --depth);
+
+    MsfCookedFrame ret = { cooked, depth, count, rdepths[depth], gdepths[depth], bdepths[depth] };
+    *frame = ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// Frame Compression                                                                                                ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline void msf_put_code(uint8_t * * writeHead, uint32_t * blockBits, int len, uint32_t code) {
+    //insert new code into block buffer
+    int idx = *blockBits / 8;
+    int bit = *blockBits % 8;
+    (*writeHead)[idx + 0] |= code <<       bit ;
+    (*writeHead)[idx + 1] |= code >> ( 8 - bit);
+    (*writeHead)[idx + 2] |= code >> (16 - bit);
+    *blockBits += len;
+
+    //prep the next block buffer if the current one is full
+    if (*blockBits >= 256 * 8) {
+        *blockBits -= 255 * 8;
+        (*writeHead) += 256;
+        (*writeHead)[2] = (*writeHead)[1];
+        (*writeHead)[1] = (*writeHead)[0];
+        (*writeHead)[0] = 255;
+        memset((*writeHead) + 4, 0, 256);
+    }
+}
+
+typedef struct {
+    int16_t * data;
+    int len;
+    int stride;
+} MsfStridedList;
+
+static inline void msf_lzw_reset(MsfStridedList * lzw, int tableSize, int stride) { MsfTimeFunc
+    memset(lzw->data, 0xFF, 4096 * stride * sizeof(int16_t));
+    lzw->len = tableSize + 2;
+    lzw->stride = stride;
+}
+
+static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int height, int centiSeconds,
+                                         MsfCookedFrame frame, MsfGifState * handle, uint8_t * used, int16_t * lzwMem)
+{ MsfTimeFunc
+    //NOTE: we reserve enough memory for theoretical the worst case upfront because it's a reasonable amount,
+    //      and prevents us from ever having to check size or realloc during compression
+    int maxBufSize = offsetof(MsfGifBuffer, data) + 32 + 256 * 3 + width * height * 3 / 2; //headers + color table + data
+    MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize);
+    if (!buffer) { return NULL; }
+    uint8_t * writeHead = buffer->data;
+    MsfStridedList lzw = { lzwMem, 0, 0 };
+
+    //allocate tlb
+    int totalBits = frame.rbits + frame.gbits + frame.bbits;
+    int tlbSize = (1 << totalBits) + 1;
+    uint8_t tlb[(1 << 16) + 1]; //only 64k, so stack allocating is fine
+
+    //generate palette
+    typedef struct { uint8_t r, g, b; } Color3;
+    Color3 table[256] = { {0} };
+    int tableIdx = 1; //we start counting at 1 because 0 is the transparent color
+    //transparent is always last in the table
+    tlb[tlbSize-1] = 0;
+    MsfTimeLoop("table") for (int i = 0; i < tlbSize-1; ++i) {
+        if (used[i]) {
+            tlb[i] = tableIdx;
+            int rmask = (1 << frame.rbits) - 1;
+            int gmask = (1 << frame.gbits) - 1;
+            //isolate components
+            int r = i & rmask;
+            int g = i >> frame.rbits & gmask;
+            int b = i >> (frame.rbits + frame.gbits);
+            //shift into highest bits
+            r <<= 8 - frame.rbits;
+            g <<= 8 - frame.gbits;
+            b <<= 8 - frame.bbits;
+            table[tableIdx].r = r | r >> frame.rbits | r >> (frame.rbits * 2) | r >> (frame.rbits * 3);
+            table[tableIdx].g = g | g >> frame.gbits | g >> (frame.gbits * 2) | g >> (frame.gbits * 3);
+            table[tableIdx].b = b | b >> frame.bbits | b >> (frame.bbits * 2) | b >> (frame.bbits * 3);
+            if (msf_gif_bgra_flag) {
+                uint8_t temp = table[tableIdx].r;
+                table[tableIdx].r = table[tableIdx].b;
+                table[tableIdx].b = temp;
+            }
+            ++tableIdx;
+        }
+    }
+    int hasTransparentPixels = used[tlbSize-1];
+
+    //SPEC: "Because of some algorithmic constraints however, black & white images which have one color bit
+    //       must be indicated as having a code size of 2."
+    int tableBits = msf_imax(2, msf_bit_log(tableIdx - 1));
+    int tableSize = 1 << tableBits;
+    //NOTE: we don't just compare `depth` field here because it will be wrong for the first frame and we will segfault
+    MsfCookedFrame previous = handle->previousFrame;
+    int hasSamePal = frame.rbits == previous.rbits && frame.gbits == previous.gbits && frame.bbits == previous.bbits;
+    int framesCompatible = hasSamePal && !hasTransparentPixels;
+
+    //NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
+    char headerBytes[19] = "\x21\xF9\x04\x05\0\0\0\0" "\x2C\0\0\0\0\0\0\0\0\x80";
+    //NOTE: we need to check the frame number because if we reach into the buffer prior to the first frame,
+    //      we'll just clobber the file header instead, which is a bug
+    if (hasTransparentPixels && handle->framesSubmitted > 0) {
+        handle->listTail->data[3] = 0x09; //set the previous frame's disposal to background, so transparency is possible
+    }
+    memcpy(&headerBytes[4], &centiSeconds, 2);
+    memcpy(&headerBytes[13], &width, 2);
+    memcpy(&headerBytes[15], &height, 2);
+    headerBytes[17] |= tableBits - 1;
+    memcpy(writeHead, headerBytes, 18);
+    writeHead += 18;
+
+    //local color table
+    memcpy(writeHead, table, tableSize * sizeof(Color3));
+    writeHead += tableSize * sizeof(Color3);
+    *writeHead++ = tableBits;
+
+    //prep block
+    memset(writeHead, 0, 260);
+    writeHead[0] = 255;
+    uint32_t blockBits = 8; //relative to block.head
+
+    //SPEC: "Encoders should output a Clear code as the first code of each image data stream."
+    msf_lzw_reset(&lzw, tableSize, tableIdx);
+    msf_put_code(&writeHead, &blockBits, msf_bit_log(lzw.len - 1), tableSize);
+
+    int lastCode = framesCompatible && frame.pixels[0] == previous.pixels[0]? 0 : tlb[frame.pixels[0]];
+    MsfTimeLoop("compress") for (int i = 1; i < width * height; ++i) {
+        //PERF: branching vs. branchless version of this line is observed to have no discernable impact on speed
+        int color = framesCompatible && frame.pixels[i] == previous.pixels[i]? 0 : tlb[frame.pixels[i]];
+        int code = (&lzw.data[lastCode * lzw.stride])[color];
+        if (code < 0) {
+            //write to code stream
+            int codeBits = msf_bit_log(lzw.len - 1);
+            msf_put_code(&writeHead, &blockBits, codeBits, lastCode);
+
+            if (lzw.len > 4095) {
+                //reset buffer code table
+                msf_put_code(&writeHead, &blockBits, codeBits, tableSize);
+                msf_lzw_reset(&lzw, tableSize, tableIdx);
+            } else {
+                (&lzw.data[lastCode * lzw.stride])[color] = lzw.len;
+                ++lzw.len;
+            }
+
+            lastCode = color;
+        } else {
+            lastCode = code;
+        }
+    }
+
+    //write code for leftover index buffer contents, then the end code
+    msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len - 1)), lastCode);
+    msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len)), tableSize + 1);
+
+    //flush remaining data
+    if (blockBits > 8) {
+        int bytes = (blockBits + 7) / 8; //round up
+        writeHead[0] = bytes - 1;
+        writeHead += bytes;
+    }
+    *writeHead++ = 0; //terminating block
+
+    //fill in buffer header and shrink buffer to fit data
+    buffer->next = NULL;
+    buffer->size = writeHead - buffer->data;
+    MsfGifBuffer * moved =
+        (MsfGifBuffer *) MSF_GIF_REALLOC(allocContext, buffer, maxBufSize, offsetof(MsfGifBuffer, data) + buffer->size);
+    if (!moved) { MSF_GIF_FREE(allocContext, buffer, maxBufSize); return NULL; }
+    return moved;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// To-memory API                                                                                                    ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static const int lzwAllocSize = 4096 * 256 * sizeof(int16_t);
+
+//NOTE: by C standard library conventions, freeing NULL should be a no-op,
+//      but just in case the user's custom free doesn't follow that rule, we do null checks on our end as well.
+static void msf_free_gif_state(MsfGifState * handle) {
+    if (handle->previousFrame.pixels) MSF_GIF_FREE(handle->customAllocatorContext, handle->previousFrame.pixels,
+                                                   handle->width * handle->height * sizeof(uint32_t));
+    if (handle->currentFrame.pixels)  MSF_GIF_FREE(handle->customAllocatorContext, handle->currentFrame.pixels,
+                                                   handle->width * handle->height * sizeof(uint32_t));
+    if (handle->lzwMem) MSF_GIF_FREE(handle->customAllocatorContext, handle->lzwMem, lzwAllocSize);
+    for (MsfGifBuffer * node = handle->listHead; node;) {
+        MsfGifBuffer * next = node->next; //NOTE: we have to copy the `next` pointer BEFORE freeing the node holding it
+        MSF_GIF_FREE(handle->customAllocatorContext, node, offsetof(MsfGifBuffer, data) + node->size);
+        node = next;
+    }
+    handle->listHead = NULL; //this implicitly marks the handle as invalid until the next msf_gif_begin() call
+}
+
+int msf_gif_begin(MsfGifState * handle, int width, int height) { MsfTimeFunc
+    //NOTE: we cannot stomp the entire struct to zero because we must preserve `customAllocatorContext`.
+    MsfCookedFrame empty = {0}; //god I hate MSVC...
+    handle->previousFrame = empty;
+    handle->currentFrame = empty;
+    handle->width = width;
+    handle->height = height;
+    handle->framesSubmitted = 0;
+
+    //allocate memory for LZW buffer
+    //NOTE: Unfortunately we can't just use stack memory for the LZW table because it's 2MB,
+    //      which is more stack space than most operating systems give by default,
+    //      and we can't realistically expect users to be willing to override that just to use our library,
+    //      so we have to allocate this on the heap.
+    handle->lzwMem = (int16_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, lzwAllocSize);
+    handle->previousFrame.pixels =
+        (uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
+    handle->currentFrame.pixels =
+        (uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
+
+    //setup header buffer header (lol)
+    handle->listHead = (MsfGifBuffer *) MSF_GIF_MALLOC(handle->customAllocatorContext, offsetof(MsfGifBuffer, data) + 32);
+    if (!handle->listHead || !handle->lzwMem || !handle->previousFrame.pixels || !handle->currentFrame.pixels) {
+        msf_free_gif_state(handle);
+        return 0;
+    }
+    handle->listTail = handle->listHead;
+    handle->listHead->next = NULL;
+    handle->listHead->size = 32;
+
+    //NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
+    char headerBytes[33] = "GIF89a\0\0\0\0\x70\0\0" "\x21\xFF\x0BNETSCAPE2.0\x03\x01\0\0\0";
+    memcpy(&headerBytes[6], &width, 2);
+    memcpy(&headerBytes[8], &height, 2);
+    memcpy(handle->listHead->data, headerBytes, 32);
+    return 1;
+}
+
+int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
+{ MsfTimeFunc
+    if (!handle->listHead) { return 0; }
+
+    maxBitDepth = msf_imax(1, msf_imin(16, maxBitDepth));
+    if (pitchInBytes == 0) pitchInBytes = handle->width * 4;
+    if (pitchInBytes < 0) pixelData -= pitchInBytes * (handle->height - 1);
+
+    uint8_t used[(1 << 16) + 1]; //only 64k, so stack allocating is fine
+    msf_cook_frame(&handle->currentFrame, pixelData, used, handle->width, handle->height, pitchInBytes,
+        msf_imin(maxBitDepth, handle->previousFrame.depth + 160 / msf_imax(1, handle->previousFrame.count)));
+
+    MsfGifBuffer * buffer = msf_compress_frame(handle->customAllocatorContext, handle->width, handle->height,
+        centiSecondsPerFame, handle->currentFrame, handle, used, handle->lzwMem);
+    if (!buffer) { msf_free_gif_state(handle); return 0; }
+    handle->listTail->next = buffer;
+    handle->listTail = buffer;
+
+    //swap current and previous frames
+    MsfCookedFrame tmp = handle->previousFrame;
+    handle->previousFrame = handle->currentFrame;
+    handle->currentFrame = tmp;
+
+    handle->framesSubmitted += 1;
+    return 1;
+}
+
+MsfGifResult msf_gif_end(MsfGifState * handle) { MsfTimeFunc
+    if (!handle->listHead) { MsfGifResult empty = {0}; return empty; }
+
+    //first pass: determine total size
+    size_t total = 1; //1 byte for trailing marker
+    for (MsfGifBuffer * node = handle->listHead; node; node = node->next) { total += node->size; }
+
+    //second pass: write data
+    uint8_t * buffer = (uint8_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, total);
+    if (buffer) {
+        uint8_t * writeHead = buffer;
+        for (MsfGifBuffer * node = handle->listHead; node; node = node->next) {
+            memcpy(writeHead, node->data, node->size);
+            writeHead += node->size;
+        }
+        *writeHead++ = 0x3B;
+    }
+
+    //third pass: free buffers
+    msf_free_gif_state(handle);
+
+    MsfGifResult ret = { buffer, total, total, handle->customAllocatorContext };
+    return ret;
+}
+
+void msf_gif_free(MsfGifResult result) { MsfTimeFunc
+    if (result.data) { MSF_GIF_FREE(result.contextPointer, result.data, result.allocSize); }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/// To-file API                                                                                                      ///
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer) {
+    handle->fileWriteFunc = func;
+    handle->fileWriteData = filePointer;
+    return msf_gif_begin(handle, width, height);
+}
+
+int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes) {
+    if (!msf_gif_frame(handle, pixelData, centiSecondsPerFame, maxBitDepth, pitchInBytes)) { return 0; }
+
+    //NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
+    MsfGifBuffer * head = handle->listHead;
+    if (!handle->fileWriteFunc(head->data, head->size, 1, handle->fileWriteData)) { msf_free_gif_state(handle); return 0; }
+    handle->listHead = head->next;
+    MSF_GIF_FREE(handle->customAllocatorContext, head, offsetof(MsfGifBuffer, data) + head->size);
+    return 1;
+}
+
+int msf_gif_end_to_file(MsfGifState * handle) {
+    //NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
+    MsfGifResult result = msf_gif_end(handle);
+    int ret = (int) handle->fileWriteFunc(result.data, result.dataSize, 1, handle->fileWriteData);
+    msf_gif_free(result);
+    return ret;
+}
+
+#endif //MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
+#endif //MSF_GIF_IMPL
+
+/*
+------------------------------------------------------------------------------
+This software is available under 2 licenses -- choose whichever you prefer.
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+Copyright (c) 2021 Miles Fogle
+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 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.
+------------------------------------------------------------------------------
+ALTERNATIVE B - Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+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 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.
+------------------------------------------------------------------------------
+*/
diff --git a/raylib/examples/core/raygui.h b/raylib/examples/core/raygui.h
new file mode 100644
--- /dev/null
+++ b/raylib/examples/core/raygui.h
@@ -0,0 +1,5987 @@
+/*******************************************************************************************
+*
+*   raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library
+*
+*   DESCRIPTION:
+*       raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
+*       available as a standalone library, as long as input and drawing functions are provided
+*
+*   FEATURES:
+*       - Immediate-mode gui, minimal retained data
+*       - +25 controls provided (basic and advanced)
+*       - Styling system for colors, font and metrics
+*       - Icons supported, embedded as a 1-bit icons pack
+*       - Standalone mode option (custom input/graphics backend)
+*       - Multiple support tools provided for raygui development
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Better standalone mode API for easy plug of custom backends
+*       - Externalize required inputs, allow user easier customization
+*
+*   LIMITATIONS:
+*       - No editable multi-line word-wraped text box supported
+*       - No auto-layout mechanism, up to the user to define controls position and size
+*       - Standalone mode requires library modification and some user work to plug another backend
+*
+*   NOTES:
+*       - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
+*         font atlas recs and glyphs, freeing that memory is (usually) up to the user,
+*         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
+*         by default any previously loaded font (texture, recs, glyphs)
+*       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
+*
+*   CONTROLS PROVIDED:
+*     # Container/separators Controls
+*       - WindowBox     --> StatusBar, Panel
+*       - GroupBox      --> Line
+*       - Line
+*       - Panel         --> StatusBar
+*       - ScrollPanel   --> StatusBar
+*       - TabBar        --> Button
+*
+*     # Basic Controls
+*       - Label
+*       - LabelButton   --> Label
+*       - Button
+*       - Toggle
+*       - ToggleGroup   --> Toggle
+*       - ToggleSlider
+*       - CheckBox
+*       - ComboBox
+*       - DropdownBox
+*       - TextBox
+*       - ValueBox      --> TextBox
+*       - Spinner       --> Button, ValueBox
+*       - Slider
+*       - SliderBar     --> Slider
+*       - ProgressBar
+*       - StatusBar
+*       - DummyRec
+*       - Grid
+*
+*     # Advance Controls
+*       - ListView
+*       - ColorPicker   --> ColorPanel, ColorBarHue
+*       - MessageBox    --> Window, Label, Button
+*       - TextInputBox  --> Window, Label, TextBox, Button
+*
+*     It also provides a set of functions for styling the controls based on its properties (size, color)
+*
+*
+*   RAYGUI STYLE (guiStyle):
+*       raygui uses a global data array for all gui style properties (allocated on data segment by default),
+*       when a new style is loaded, it is loaded over the global style... but a default gui style could always be
+*       recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one
+*
+*       The global style array size is fixed and depends on the number of controls and properties:
+*
+*           static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
+*
+*       guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
+*
+*       Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
+*       used for all controls, when any of those base values is set, it is automatically populated to all
+*       controls, so, specific control values overwriting generic style should be set after base values
+*
+*       After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
+*       properties are actually common to all controls and can not be overwritten individually (like BASE ones)
+*       Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR
+*
+*       Custom control properties can be defined using the EXTENDED properties for each independent control.
+*
+*       TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler
+*
+*
+*   RAYGUI ICONS (guiIcons):
+*       raygui could use a global array containing icons data (allocated on data segment by default),
+*       a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set
+*       must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded
+*
+*       Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
+*       requires 8 integers (16*16/32) to be stored in memory.
+*
+*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
+*
+*       The global icons array size is fixed and depends on the number of icons and size:
+*
+*           static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS];
+*
+*       guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+*
+*       TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons
+*
+*   RAYGUI LAYOUT:
+*       raygui currently does not provide an auto-layout mechanism like other libraries,
+*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
+*
+*       TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
+*
+*   CONFIGURATION:
+*       #define RAYGUI_IMPLEMENTATION
+*           Generates the implementation of the library into the included file
+*           If not defined, the library is in header only mode and can be included in other headers
+*           or source files without problems. But only ONE file should hold the implementation
+*
+*       #define RAYGUI_STANDALONE
+*           Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
+*           internally in the library and input management and drawing functions must be provided by
+*           the user (check library implementation for further details)
+*
+*       #define RAYGUI_NO_ICONS
+*           Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
+*
+*       #define RAYGUI_CUSTOM_ICONS
+*           Includes custom ricons.h header defining a set of custom icons,
+*           this file can be generated using rGuiIcons tool
+*
+*       #define RAYGUI_DEBUG_RECS_BOUNDS
+*           Draw control bounds rectangles for debug
+*
+*       #define RAYGUI_DEBUG_TEXT_BOUNDS
+*           Draw text bounds rectangles for debug
+*
+*   VERSIONS HISTORY:
+*       5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
+*                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+*                         ADDED: GuiValueBoxFloat()
+*                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+*                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: GuiLoadIconsFromMemory()
+*                         ADDED: Multiple new icons
+*                         REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+*                         REMOVED: GuiSliderPro(), functionality was redundant
+*                         REVIEWED: Controls using text labels to use LABEL properties
+*                         REVIEWED: Replaced sprintf() by snprintf() for more safety
+*                         REVIEWED: GuiTabBar(), close tab with mouse middle button
+*                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+*                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+*                         REVIEWED: GuiTextBox(), cursor position initialization
+*                         REVIEWED: GuiSliderPro(), control value change check
+*                         REVIEWED: GuiGrid(), simplified implementation
+*                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+*                         REVIEWED: GuiDrawText(), improved wrap mode drawing
+*                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: GuiProgressBar(), improved borders computing
+*                         REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
+*                         REVIEWED: Functions descriptions, removed wrong return value reference
+*                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
+*
+*       4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
+*                         ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
+*                         ADDED: Multiple new icons, mostly compiler related
+*                         ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE
+*                         ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode
+*                         ADDED: Support loading styles with custom font charset from external file
+*                         REDESIGNED: GuiTextBox(), support mouse cursor positioning
+*                         REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only)
+*                         REDESIGNED: GuiProgressBar() to be more visual, progress affects border color
+*                         REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText()
+*                         REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value
+*                         REDESIGNED: GuiComboBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiCheckBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSlider(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSliderBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiProgressBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiListView(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPicker(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), added extra parameter
+*                         REDESIGNED: GuiListViewEx(), change parameters order
+*                         REDESIGNED: All controls return result as int value
+*                         REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars
+*                         REVIEWED: All examples and specially controls_test_suite
+*                         RENAMED: gui_file_dialog module to gui_window_file_dialog
+*                         UPDATED: All styles to include ISO-8859-15 charset (as much as possible)
+*
+*       3.6 (10-May-2023) ADDED: New icon: SAND_TIMER
+*                         ADDED: GuiLoadStyleFromMemory() (binary only)
+*                         REVIEWED: GuiScrollBar() horizontal movement key
+*                         REVIEWED: GuiTextBox() crash on cursor movement
+*                         REVIEWED: GuiTextBox(), additional inputs support
+*                         REVIEWED: GuiLabelButton(), avoid text cut
+*                         REVIEWED: GuiTextInputBox(), password input
+*                         REVIEWED: Local GetCodepointNext(), aligned with raylib
+*                         REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds
+*
+*       3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle()
+*                         ADDED: Helper functions to split text in separate lines
+*                         ADDED: Multiple new icons, useful for code editing tools
+*                         REMOVED: Unneeded icon editing functions
+*                         REMOVED: GuiTextBoxMulti(), very limited and broken
+*                         REMOVED: MeasureTextEx() dependency, logic directly implemented
+*                         REMOVED: DrawTextEx() dependency, logic directly implemented
+*                         REVIEWED: GuiScrollBar(), improve mouse-click behaviour
+*                         REVIEWED: Library header info, more info, better organized
+*                         REDESIGNED: GuiTextBox() to support cursor movement
+*                         REDESIGNED: GuiDrawText() to divide drawing by lines
+*
+*       3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes
+*                         REMOVED: GuiScrollBar(), only internal
+*                         REDESIGNED: GuiPanel() to support text parameter
+*                         REDESIGNED: GuiScrollPanel() to support text parameter
+*                         REDESIGNED: GuiColorPicker() to support text parameter
+*                         REDESIGNED: GuiColorPanel() to support text parameter
+*                         REDESIGNED: GuiColorBarAlpha() to support text parameter
+*                         REDESIGNED: GuiColorBarHue() to support text parameter
+*                         REDESIGNED: GuiTextInputBox() to support password
+*
+*       3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool)
+*                         REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures
+*                         REVIEWED: External icons usage logic
+*                         REVIEWED: GuiLine() for centered alignment when including text
+*                         RENAMED: Multiple controls properties definitions to prepend RAYGUI_
+*                         RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency
+*                         Projects updated and multiple tweaks
+*
+*       3.0 (04-Nov-2021) Integrated ricons data to avoid external file
+*                         REDESIGNED: GuiTextBoxMulti()
+*                         REMOVED: GuiImageButton*()
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.9 (17-Mar-2021) REMOVED: Tooltip API
+*       2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle()
+*       2.7 (20-Feb-2020) ADDED: Possible tooltips API
+*       2.6 (09-Sep-2019) ADDED: GuiTextInputBox()
+*                         REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox()
+*                         REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle()
+*                         Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties
+*                         ADDED: 8 new custom styles ready to use
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
+*       2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed
+*                         Refactor all controls drawing mechanism to use control state
+*       2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls
+*       2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string
+*                         REDESIGNED: Style system (breaking change)
+*       2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts
+*                         REVIEWED: GuiComboBox(), GuiListView()...
+*       1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()...
+*       1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout
+*       1.5 (21-Jun-2017) Working in an improved styles system
+*       1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
+*       1.3 (12-Jun-2017) Complete redesign of style system
+*       1.1 (01-Jun-2017) Complete review of the library
+*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
+*
+*   DEPENDENCIES:
+*       raylib 5.6-dev  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+*
+*   STANDALONE MODE:
+*       By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
+*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
+*
+*       The following functions should be redefined for a custom backend:
+*
+*           - Vector2 GetMousePosition(void);
+*           - float GetMouseWheelMove(void);
+*           - bool IsMouseButtonDown(int button);
+*           - bool IsMouseButtonPressed(int button);
+*           - bool IsMouseButtonReleased(int button);
+*           - bool IsKeyDown(int key);
+*           - bool IsKeyPressed(int key);
+*           - int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+*
+*           - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle()
+*           - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+*
+*           - Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+*           - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle()
+*           - Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+*           - void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+*           - char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+*           - void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+*           - const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+*           - int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+*           - void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+*           - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+*
+*   CONTRIBUTORS:
+*       Ramon Santamaria:   Supervision, review, redesign, update and maintenance
+*       Vlad Adrian:        Complete rewrite of GuiTextBox() to support extended features (2019)
+*       Sergio Martinez:    Review, testing (2015) and redesign of multiple controls (2018)
+*       Adria Arranz:       Testing and implementation of additional controls (2018)
+*       Jordi Jorba:        Testing and implementation of additional controls (2018)
+*       Albert Martos:      Review and testing of the library (2015)
+*       Ian Eito:           Review and testing of the library (2015)
+*       Kevin Gato:         Initial implementation of basic components (2014)
+*       Daniel Nicolas:     Initial implementation of basic components (2014)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RAYGUI_H
+#define RAYGUI_H
+
+#define RAYGUI_VERSION_MAJOR 4
+#define RAYGUI_VERSION_MINOR 5
+#define RAYGUI_VERSION_PATCH 0
+#define RAYGUI_VERSION  "5.0-dev"
+
+#if !defined(RAYGUI_STANDALONE)
+    #include "raylib.h"
+#endif
+
+// Function specifiers in case library is build/used as a shared library (Windows)
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+#if defined(_WIN32)
+    #if defined(BUILD_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+    #elif defined(USE_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+    #endif
+#endif
+
+// Function specifiers definition
+#ifndef RAYGUIAPI
+    #define RAYGUIAPI       // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define RAYGUI_SUPPORT_LOG_INFO
+#if defined(RAYGUI_SUPPORT_LOG_INFO)
+  #define RAYGUI_LOG(...)           printf(__VA_ARGS__)
+#else
+  #define RAYGUI_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+// NOTE: Some types are required for RAYGUI_STANDALONE usage
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+    #ifndef __cplusplus
+    // Boolean type
+        #ifndef true
+            typedef enum { false, true } bool;
+        #endif
+    #endif
+
+    // Vector2 type
+    typedef struct Vector2 {
+        float x;
+        float y;
+    } Vector2;
+
+    // Vector3 type                 // -- ConvertHSVtoRGB(), ConvertRGBtoHSV()
+    typedef struct Vector3 {
+        float x;
+        float y;
+        float z;
+    } Vector3;
+
+    // Color type, RGBA (32bit)
+    typedef struct Color {
+        unsigned char r;
+        unsigned char g;
+        unsigned char b;
+        unsigned char a;
+    } Color;
+
+    // Rectangle type
+    typedef struct Rectangle {
+        float x;
+        float y;
+        float width;
+        float height;
+    } Rectangle;
+
+    // TODO: Texture2D type is very coupled to raylib, required by Font type
+    // It should be redesigned to be provided by user
+    typedef struct Texture {
+        unsigned int id;        // OpenGL texture id
+        int width;              // Texture base width
+        int height;             // Texture base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Texture;
+
+    // Texture2D, same as Texture
+    typedef Texture Texture2D;
+
+    // Image, pixel data stored in CPU memory (RAM)
+    typedef struct Image {
+        void *data;             // Image raw data
+        int width;              // Image base width
+        int height;             // Image base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Image;
+
+    // GlyphInfo, font characters glyphs info
+    typedef struct GlyphInfo {
+        int value;              // Character value (Unicode)
+        int offsetX;            // Character offset X when drawing
+        int offsetY;            // Character offset Y when drawing
+        int advanceX;           // Character advance position X
+        Image image;            // Character image data
+    } GlyphInfo;
+
+    // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle()
+    // It should be redesigned to be provided by user
+    typedef struct Font {
+        int baseSize;           // Base size (default chars height)
+        int glyphCount;         // Number of glyph characters
+        int glyphPadding;       // Padding around the glyph characters
+        Texture2D texture;      // Texture atlas containing the glyphs
+        Rectangle *recs;        // Rectangles in texture for the glyphs
+        GlyphInfo *glyphs;      // Glyphs info data
+    } Font;
+#endif
+
+// Style property
+// NOTE: Used when exporting style as code for convenience
+typedef struct GuiStyleProp {
+    unsigned short controlId;   // Control identifier
+    unsigned short propertyId;  // Property identifier
+    int propertyValue;          // Property value
+} GuiStyleProp;
+
+/*
+// Controls text style -NOT USED-
+// NOTE: Text style is defined by control
+typedef struct GuiTextStyle {
+    unsigned int size;
+    int charSpacing;
+    int lineSpacing;
+    int alignmentH;
+    int alignmentV;
+    int padding;
+} GuiTextStyle;
+*/
+
+// Gui control state
+typedef enum {
+    STATE_NORMAL = 0,
+    STATE_FOCUSED,
+    STATE_PRESSED,
+    STATE_DISABLED
+} GuiState;
+
+// Gui control text alignment
+typedef enum {
+    TEXT_ALIGN_LEFT = 0,
+    TEXT_ALIGN_CENTER,
+    TEXT_ALIGN_RIGHT
+} GuiTextAlignment;
+
+// Gui control text alignment vertical
+// NOTE: Text vertical position inside the text bounds
+typedef enum {
+    TEXT_ALIGN_TOP = 0,
+    TEXT_ALIGN_MIDDLE,
+    TEXT_ALIGN_BOTTOM
+} GuiTextAlignmentVertical;
+
+// Gui control text wrap mode
+// NOTE: Useful for multiline text
+typedef enum {
+    TEXT_WRAP_NONE = 0,
+    TEXT_WRAP_CHAR,
+    TEXT_WRAP_WORD
+} GuiTextWrapMode;
+
+// Gui controls
+typedef enum {
+    // Default -> populates to all controls when set
+    DEFAULT = 0,
+
+    // Basic controls
+    LABEL,          // Used also for: LABELBUTTON
+    BUTTON,
+    TOGGLE,         // Used also for: TOGGLEGROUP
+    SLIDER,         // Used also for: SLIDERBAR, TOGGLESLIDER
+    PROGRESSBAR,
+    CHECKBOX,
+    COMBOBOX,
+    DROPDOWNBOX,
+    TEXTBOX,        // Used also for: TEXTBOXMULTI
+    VALUEBOX,
+    CONTROL11,
+    LISTVIEW,
+    COLORPICKER,
+    SCROLLBAR,
+    STATUSBAR
+} GuiControl;
+
+// Gui base properties for every control
+// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
+typedef enum {
+    BORDER_COLOR_NORMAL = 0,    // Control border color in STATE_NORMAL
+    BASE_COLOR_NORMAL,          // Control base color in STATE_NORMAL
+    TEXT_COLOR_NORMAL,          // Control text color in STATE_NORMAL
+    BORDER_COLOR_FOCUSED,       // Control border color in STATE_FOCUSED
+    BASE_COLOR_FOCUSED,         // Control base color in STATE_FOCUSED
+    TEXT_COLOR_FOCUSED,         // Control text color in STATE_FOCUSED
+    BORDER_COLOR_PRESSED,       // Control border color in STATE_PRESSED
+    BASE_COLOR_PRESSED,         // Control base color in STATE_PRESSED
+    TEXT_COLOR_PRESSED,         // Control text color in STATE_PRESSED
+    BORDER_COLOR_DISABLED,      // Control border color in STATE_DISABLED
+    BASE_COLOR_DISABLED,        // Control base color in STATE_DISABLED
+    TEXT_COLOR_DISABLED,        // Control text color in STATE_DISABLED
+    BORDER_WIDTH = 12,          // Control border size, 0 for no border
+    //TEXT_SIZE,                  // Control text size (glyphs max height) -> GLOBAL for all controls
+    //TEXT_SPACING,               // Control text spacing between glyphs -> GLOBAL for all controls
+    //TEXT_LINE_SPACING,          // Control text spacing between lines -> GLOBAL for all controls
+    TEXT_PADDING = 13,          // Control text padding, not considering border
+    TEXT_ALIGNMENT = 14,        // Control text horizontal alignment inside control text bound (after border and padding)
+    //TEXT_WRAP_MODE              // Control text wrap-mode inside text bounds -> GLOBAL for all controls
+} GuiControlProperty;
+
+// TODO: Which text styling properties should be global or per-control?
+// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while
+// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and
+// should be configured by user as needed while defining the UI layout
+
+// Gui extended properties depend on control
+// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
+//----------------------------------------------------------------------------------
+// DEFAULT extended properties
+// NOTE: Those properties are common to all controls or global
+// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT?
+typedef enum {
+    TEXT_SIZE = 16,             // Text size (glyphs max height)
+    TEXT_SPACING,               // Text spacing between glyphs
+    LINE_COLOR,                 // Line control color
+    BACKGROUND_COLOR,           // Background color
+    TEXT_LINE_SPACING,          // Text spacing between lines
+    TEXT_ALIGNMENT_VERTICAL,    // Text vertical alignment inside text bounds (after border and padding)
+    TEXT_WRAP_MODE              // Text wrap-mode inside text bounds
+    //TEXT_DECORATION             // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline
+    //TEXT_DECORATION_THICK       // Text decoration line thickness
+} GuiDefaultProperty;
+
+// Other possible text properties:
+// TEXT_WEIGHT                  // Normal, Italic, Bold -> Requires specific font change
+// TEXT_INDENT                  // Text indentation -> Now using TEXT_PADDING...
+
+// Label
+//typedef enum { } GuiLabelProperty;
+
+// Button/Spinner
+//typedef enum { } GuiButtonProperty;
+
+// Toggle/ToggleGroup
+typedef enum {
+    GROUP_PADDING = 16,         // ToggleGroup separation between toggles
+} GuiToggleProperty;
+
+// Slider/SliderBar
+typedef enum {
+    SLIDER_WIDTH = 16,          // Slider size of internal bar
+    SLIDER_PADDING              // Slider/SliderBar internal bar padding
+} GuiSliderProperty;
+
+// ProgressBar
+typedef enum {
+    PROGRESS_PADDING = 16,      // ProgressBar internal padding
+} GuiProgressBarProperty;
+
+// ScrollBar
+typedef enum {
+    ARROWS_SIZE = 16,           // ScrollBar arrows size
+    ARROWS_VISIBLE,             // ScrollBar arrows visible
+    SCROLL_SLIDER_PADDING,      // ScrollBar slider internal padding
+    SCROLL_SLIDER_SIZE,         // ScrollBar slider size
+    SCROLL_PADDING,             // ScrollBar scroll padding from arrows
+    SCROLL_SPEED,               // ScrollBar scrolling speed
+} GuiScrollBarProperty;
+
+// CheckBox
+typedef enum {
+    CHECK_PADDING = 16          // CheckBox internal check padding
+} GuiCheckBoxProperty;
+
+// ComboBox
+typedef enum {
+    COMBO_BUTTON_WIDTH = 16,    // ComboBox right button width
+    COMBO_BUTTON_SPACING        // ComboBox button separation
+} GuiComboBoxProperty;
+
+// DropdownBox
+typedef enum {
+    ARROW_PADDING = 16,         // DropdownBox arrow separation from border and items
+    DROPDOWN_ITEMS_SPACING,     // DropdownBox items separation
+    DROPDOWN_ARROW_HIDDEN,      // DropdownBox arrow hidden
+    DROPDOWN_ROLL_UP            // DropdownBox roll up flag (default rolls down)
+} GuiDropdownBoxProperty;
+
+// TextBox/TextBoxMulti/ValueBox/Spinner
+typedef enum {
+    TEXT_READONLY = 16,         // TextBox in read-only mode: 0-text editable, 1-text no-editable
+} GuiTextBoxProperty;
+
+// ValueBox/Spinner
+typedef enum {
+    SPINNER_BUTTON_WIDTH = 16,  // Spinner left/right buttons width
+    SPINNER_BUTTON_SPACING,     // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
+
+// ListView
+typedef enum {
+    LIST_ITEMS_HEIGHT = 16,     // ListView items height
+    LIST_ITEMS_SPACING,         // ListView items separation
+    SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
+    SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_NORMAL,   // ListView items border enabled in normal state
+    LIST_ITEMS_BORDER_WIDTH     // ListView items border width
+} GuiListViewProperty;
+
+// ColorPicker
+typedef enum {
+    COLOR_SELECTOR_SIZE = 16,
+    HUEBAR_WIDTH,               // ColorPicker right hue bar width
+    HUEBAR_PADDING,             // ColorPicker right hue bar separation from panel
+    HUEBAR_SELECTOR_HEIGHT,     // ColorPicker right hue bar selector height
+    HUEBAR_SELECTOR_OVERFLOW    // ColorPicker right hue bar selector overflow
+} GuiColorPickerProperty;
+
+#define SCROLLBAR_LEFT_SIDE     0
+#define SCROLLBAR_RIGHT_SIDE    1
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+// Global gui state control functions
+RAYGUIAPI void GuiEnable(void);                                 // Enable gui controls (global state)
+RAYGUIAPI void GuiDisable(void);                                // Disable gui controls (global state)
+RAYGUIAPI void GuiLock(void);                                   // Lock gui controls (global state)
+RAYGUIAPI void GuiUnlock(void);                                 // Unlock gui controls (global state)
+RAYGUIAPI bool GuiIsLocked(void);                               // Check if gui is locked (global state)
+RAYGUIAPI void GuiSetAlpha(float alpha);                        // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f
+RAYGUIAPI void GuiSetState(int state);                          // Set gui state (global state)
+RAYGUIAPI int GuiGetState(void);                                // Get gui state (global state)
+
+// Font set/get functions
+RAYGUIAPI void GuiSetFont(Font font);                           // Set gui custom font (global state)
+RAYGUIAPI Font GuiGetFont(void);                                // Get gui custom font (global state)
+
+// Style set/get functions
+RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property
+RAYGUIAPI int GuiGetStyle(int control, int property);           // Get one style property
+
+// Styles loading functions
+RAYGUIAPI void GuiLoadStyle(const char *fileName);              // Load style file over global style variable (.rgs)
+RAYGUIAPI void GuiLoadStyleDefault(void);                       // Load style default over global style
+
+// Tooltips management functions
+RAYGUIAPI void GuiEnableTooltip(void);                          // Enable gui tooltips (global state)
+RAYGUIAPI void GuiDisableTooltip(void);                         // Disable gui tooltips (global state)
+RAYGUIAPI void GuiSetTooltip(const char *tooltip);              // Set tooltip string
+
+// Icons functionality
+RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported)
+#if !defined(RAYGUI_NO_ICONS)
+RAYGUIAPI void GuiSetIconScale(int scale);                      // Set default icon drawing size
+RAYGUIAPI unsigned int *GuiGetIcons(void);                      // Get raygui icons data pointer
+RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data
+RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
+#endif
+
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text);                // Get text width considering gui style and icon size (if required)
+
+// Controls
+//----------------------------------------------------------------------------------------------------------
+// Container/separator controls, useful for controls organization
+RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title);                                       // Window Box control, shows a window that can be closed
+RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text);                                         // Group Box control with text name
+RAYGUIAPI int GuiLine(Rectangle bounds, const char *text);                                             // Line separator control, could contain text
+RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text);                                            // Panel control, useful to group controls
+RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active);                  // Tab Bar control, returns TAB to be closed or -1
+RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control
+
+// Basic controls set
+RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text);                                            // Label control
+RAYGUIAPI int GuiButton(Rectangle bounds, const char *text);                                           // Button control, returns true when clicked
+RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text);                                      // Label button control, returns true when clicked
+RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active);                             // Toggle Button control
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active);                         // Toggle Group control
+RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active);                        // Toggle Slider control
+RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked);                          // Check Box control, returns true when active
+RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active);                            // Combo Box control
+
+RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode);          // Dropdown Box control
+RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control
+RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
+RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values
+RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode);                   // Text Box control, updates input text
+
+RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control
+RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control
+RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control
+RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text);                                        // Status Bar control, shows info text
+RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text);                                         // Dummy control for placeholders
+RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control
+
+// Advance controls set
+RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active);          // List View control
+RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters
+RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message
+RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret
+RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color);                        // Color Picker control (multiple color controls)
+RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color);                         // Color Panel control
+RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha);                      // Color Bar Alpha control
+RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value);                        // Color Bar Hue control
+RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                // Color Picker control that avoids conversion to RGB on each call (multiple color controls)
+RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                 // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV()
+//----------------------------------------------------------------------------------------------------------
+
+#if !defined(RAYGUI_NO_ICONS)
+
+#if !defined(RAYGUI_CUSTOM_ICONS)
+//----------------------------------------------------------------------------------
+// Icons enumeration
+//----------------------------------------------------------------------------------
+typedef enum {
+    ICON_NONE                     = 0,
+    ICON_FOLDER_FILE_OPEN         = 1,
+    ICON_FILE_SAVE_CLASSIC        = 2,
+    ICON_FOLDER_OPEN              = 3,
+    ICON_FOLDER_SAVE              = 4,
+    ICON_FILE_OPEN                = 5,
+    ICON_FILE_SAVE                = 6,
+    ICON_FILE_EXPORT              = 7,
+    ICON_FILE_ADD                 = 8,
+    ICON_FILE_DELETE              = 9,
+    ICON_FILETYPE_TEXT            = 10,
+    ICON_FILETYPE_AUDIO           = 11,
+    ICON_FILETYPE_IMAGE           = 12,
+    ICON_FILETYPE_PLAY            = 13,
+    ICON_FILETYPE_VIDEO           = 14,
+    ICON_FILETYPE_INFO            = 15,
+    ICON_FILE_COPY                = 16,
+    ICON_FILE_CUT                 = 17,
+    ICON_FILE_PASTE               = 18,
+    ICON_CURSOR_HAND              = 19,
+    ICON_CURSOR_POINTER           = 20,
+    ICON_CURSOR_CLASSIC           = 21,
+    ICON_PENCIL                   = 22,
+    ICON_PENCIL_BIG               = 23,
+    ICON_BRUSH_CLASSIC            = 24,
+    ICON_BRUSH_PAINTER            = 25,
+    ICON_WATER_DROP               = 26,
+    ICON_COLOR_PICKER             = 27,
+    ICON_RUBBER                   = 28,
+    ICON_COLOR_BUCKET             = 29,
+    ICON_TEXT_T                   = 30,
+    ICON_TEXT_A                   = 31,
+    ICON_SCALE                    = 32,
+    ICON_RESIZE                   = 33,
+    ICON_FILTER_POINT             = 34,
+    ICON_FILTER_BILINEAR          = 35,
+    ICON_CROP                     = 36,
+    ICON_CROP_ALPHA               = 37,
+    ICON_SQUARE_TOGGLE            = 38,
+    ICON_SYMMETRY                 = 39,
+    ICON_SYMMETRY_HORIZONTAL      = 40,
+    ICON_SYMMETRY_VERTICAL        = 41,
+    ICON_LENS                     = 42,
+    ICON_LENS_BIG                 = 43,
+    ICON_EYE_ON                   = 44,
+    ICON_EYE_OFF                  = 45,
+    ICON_FILTER_TOP               = 46,
+    ICON_FILTER                   = 47,
+    ICON_TARGET_POINT             = 48,
+    ICON_TARGET_SMALL             = 49,
+    ICON_TARGET_BIG               = 50,
+    ICON_TARGET_MOVE              = 51,
+    ICON_CURSOR_MOVE              = 52,
+    ICON_CURSOR_SCALE             = 53,
+    ICON_CURSOR_SCALE_RIGHT       = 54,
+    ICON_CURSOR_SCALE_LEFT        = 55,
+    ICON_UNDO                     = 56,
+    ICON_REDO                     = 57,
+    ICON_REREDO                   = 58,
+    ICON_MUTATE                   = 59,
+    ICON_ROTATE                   = 60,
+    ICON_REPEAT                   = 61,
+    ICON_SHUFFLE                  = 62,
+    ICON_EMPTYBOX                 = 63,
+    ICON_TARGET                   = 64,
+    ICON_TARGET_SMALL_FILL        = 65,
+    ICON_TARGET_BIG_FILL          = 66,
+    ICON_TARGET_MOVE_FILL         = 67,
+    ICON_CURSOR_MOVE_FILL         = 68,
+    ICON_CURSOR_SCALE_FILL        = 69,
+    ICON_CURSOR_SCALE_RIGHT_FILL  = 70,
+    ICON_CURSOR_SCALE_LEFT_FILL   = 71,
+    ICON_UNDO_FILL                = 72,
+    ICON_REDO_FILL                = 73,
+    ICON_REREDO_FILL              = 74,
+    ICON_MUTATE_FILL              = 75,
+    ICON_ROTATE_FILL              = 76,
+    ICON_REPEAT_FILL              = 77,
+    ICON_SHUFFLE_FILL             = 78,
+    ICON_EMPTYBOX_SMALL           = 79,
+    ICON_BOX                      = 80,
+    ICON_BOX_TOP                  = 81,
+    ICON_BOX_TOP_RIGHT            = 82,
+    ICON_BOX_RIGHT                = 83,
+    ICON_BOX_BOTTOM_RIGHT         = 84,
+    ICON_BOX_BOTTOM               = 85,
+    ICON_BOX_BOTTOM_LEFT          = 86,
+    ICON_BOX_LEFT                 = 87,
+    ICON_BOX_TOP_LEFT             = 88,
+    ICON_BOX_CENTER               = 89,
+    ICON_BOX_CIRCLE_MASK          = 90,
+    ICON_POT                      = 91,
+    ICON_ALPHA_MULTIPLY           = 92,
+    ICON_ALPHA_CLEAR              = 93,
+    ICON_DITHERING                = 94,
+    ICON_MIPMAPS                  = 95,
+    ICON_BOX_GRID                 = 96,
+    ICON_GRID                     = 97,
+    ICON_BOX_CORNERS_SMALL        = 98,
+    ICON_BOX_CORNERS_BIG          = 99,
+    ICON_FOUR_BOXES               = 100,
+    ICON_GRID_FILL                = 101,
+    ICON_BOX_MULTISIZE            = 102,
+    ICON_ZOOM_SMALL               = 103,
+    ICON_ZOOM_MEDIUM              = 104,
+    ICON_ZOOM_BIG                 = 105,
+    ICON_ZOOM_ALL                 = 106,
+    ICON_ZOOM_CENTER              = 107,
+    ICON_BOX_DOTS_SMALL           = 108,
+    ICON_BOX_DOTS_BIG             = 109,
+    ICON_BOX_CONCENTRIC           = 110,
+    ICON_BOX_GRID_BIG             = 111,
+    ICON_OK_TICK                  = 112,
+    ICON_CROSS                    = 113,
+    ICON_ARROW_LEFT               = 114,
+    ICON_ARROW_RIGHT              = 115,
+    ICON_ARROW_DOWN               = 116,
+    ICON_ARROW_UP                 = 117,
+    ICON_ARROW_LEFT_FILL          = 118,
+    ICON_ARROW_RIGHT_FILL         = 119,
+    ICON_ARROW_DOWN_FILL          = 120,
+    ICON_ARROW_UP_FILL            = 121,
+    ICON_AUDIO                    = 122,
+    ICON_FX                       = 123,
+    ICON_WAVE                     = 124,
+    ICON_WAVE_SINUS               = 125,
+    ICON_WAVE_SQUARE              = 126,
+    ICON_WAVE_TRIANGULAR          = 127,
+    ICON_CROSS_SMALL              = 128,
+    ICON_PLAYER_PREVIOUS          = 129,
+    ICON_PLAYER_PLAY_BACK         = 130,
+    ICON_PLAYER_PLAY              = 131,
+    ICON_PLAYER_PAUSE             = 132,
+    ICON_PLAYER_STOP              = 133,
+    ICON_PLAYER_NEXT              = 134,
+    ICON_PLAYER_RECORD            = 135,
+    ICON_MAGNET                   = 136,
+    ICON_LOCK_CLOSE               = 137,
+    ICON_LOCK_OPEN                = 138,
+    ICON_CLOCK                    = 139,
+    ICON_TOOLS                    = 140,
+    ICON_GEAR                     = 141,
+    ICON_GEAR_BIG                 = 142,
+    ICON_BIN                      = 143,
+    ICON_HAND_POINTER             = 144,
+    ICON_LASER                    = 145,
+    ICON_COIN                     = 146,
+    ICON_EXPLOSION                = 147,
+    ICON_1UP                      = 148,
+    ICON_PLAYER                   = 149,
+    ICON_PLAYER_JUMP              = 150,
+    ICON_KEY                      = 151,
+    ICON_DEMON                    = 152,
+    ICON_TEXT_POPUP               = 153,
+    ICON_GEAR_EX                  = 154,
+    ICON_CRACK                    = 155,
+    ICON_CRACK_POINTS             = 156,
+    ICON_STAR                     = 157,
+    ICON_DOOR                     = 158,
+    ICON_EXIT                     = 159,
+    ICON_MODE_2D                  = 160,
+    ICON_MODE_3D                  = 161,
+    ICON_CUBE                     = 162,
+    ICON_CUBE_FACE_TOP            = 163,
+    ICON_CUBE_FACE_LEFT           = 164,
+    ICON_CUBE_FACE_FRONT          = 165,
+    ICON_CUBE_FACE_BOTTOM         = 166,
+    ICON_CUBE_FACE_RIGHT          = 167,
+    ICON_CUBE_FACE_BACK           = 168,
+    ICON_CAMERA                   = 169,
+    ICON_SPECIAL                  = 170,
+    ICON_LINK_NET                 = 171,
+    ICON_LINK_BOXES               = 172,
+    ICON_LINK_MULTI               = 173,
+    ICON_LINK                     = 174,
+    ICON_LINK_BROKE               = 175,
+    ICON_TEXT_NOTES               = 176,
+    ICON_NOTEBOOK                 = 177,
+    ICON_SUITCASE                 = 178,
+    ICON_SUITCASE_ZIP             = 179,
+    ICON_MAILBOX                  = 180,
+    ICON_MONITOR                  = 181,
+    ICON_PRINTER                  = 182,
+    ICON_PHOTO_CAMERA             = 183,
+    ICON_PHOTO_CAMERA_FLASH       = 184,
+    ICON_HOUSE                    = 185,
+    ICON_HEART                    = 186,
+    ICON_CORNER                   = 187,
+    ICON_VERTICAL_BARS            = 188,
+    ICON_VERTICAL_BARS_FILL       = 189,
+    ICON_LIFE_BARS                = 190,
+    ICON_INFO                     = 191,
+    ICON_CROSSLINE                = 192,
+    ICON_HELP                     = 193,
+    ICON_FILETYPE_ALPHA           = 194,
+    ICON_FILETYPE_HOME            = 195,
+    ICON_LAYERS_VISIBLE           = 196,
+    ICON_LAYERS                   = 197,
+    ICON_WINDOW                   = 198,
+    ICON_HIDPI                    = 199,
+    ICON_FILETYPE_BINARY          = 200,
+    ICON_HEX                      = 201,
+    ICON_SHIELD                   = 202,
+    ICON_FILE_NEW                 = 203,
+    ICON_FOLDER_ADD               = 204,
+    ICON_ALARM                    = 205,
+    ICON_CPU                      = 206,
+    ICON_ROM                      = 207,
+    ICON_STEP_OVER                = 208,
+    ICON_STEP_INTO                = 209,
+    ICON_STEP_OUT                 = 210,
+    ICON_RESTART                  = 211,
+    ICON_BREAKPOINT_ON            = 212,
+    ICON_BREAKPOINT_OFF           = 213,
+    ICON_BURGER_MENU              = 214,
+    ICON_CASE_SENSITIVE           = 215,
+    ICON_REG_EXP                  = 216,
+    ICON_FOLDER                   = 217,
+    ICON_FILE                     = 218,
+    ICON_SAND_TIMER               = 219,
+    ICON_WARNING                  = 220,
+    ICON_HELP_BOX                 = 221,
+    ICON_INFO_BOX                 = 222,
+    ICON_PRIORITY                 = 223,
+    ICON_LAYERS_ISO               = 224,
+    ICON_LAYERS2                  = 225,
+    ICON_MLAYERS                  = 226,
+    ICON_MAPS                     = 227,
+    ICON_HOT                      = 228,
+    ICON_LABEL                    = 229,
+    ICON_NAME_ID                  = 230,
+    ICON_SLICING                  = 231,
+    ICON_MANUAL_CONTROL           = 232,
+    ICON_COLLISION                = 233,
+    ICON_CIRCLE_ADD               = 234,
+    ICON_CIRCLE_ADD_FILL          = 235,
+    ICON_CIRCLE_WARNING           = 236,
+    ICON_CIRCLE_WARNING_FILL      = 237,
+    ICON_BOX_MORE                 = 238,
+    ICON_BOX_MORE_FILL            = 239,
+    ICON_BOX_MINUS                = 240,
+    ICON_BOX_MINUS_FILL           = 241,
+    ICON_UNION                    = 242,
+    ICON_INTERSECTION             = 243,
+    ICON_DIFFERENCE               = 244,
+    ICON_SPHERE                   = 245,
+    ICON_CYLINDER                 = 246,
+    ICON_CONE                     = 247,
+    ICON_ELLIPSOID                = 248,
+    ICON_CAPSULE                  = 249,
+    ICON_250                      = 250,
+    ICON_251                      = 251,
+    ICON_252                      = 252,
+    ICON_253                      = 253,
+    ICON_254                      = 254,
+    ICON_255                      = 255
+} GuiIconName;
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+}            // Prevents name mangling of functions
+#endif
+
+#endif // RAYGUI_H
+
+/***********************************************************************************
+*
+*   RAYGUI IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RAYGUI_IMPLEMENTATION)
+
+#include <ctype.h>              // required for: isspace() [GuiTextBox()]
+#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
+#include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
+#include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
+#include <math.h>               // Required for: roundf() [GuiColorPicker()]
+
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+        #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+    #endif
+#else
+    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+    #define RAYGUI_MALLOC(sz)       malloc(sz)
+    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
+    #define RAYGUI_FREE(p)          free(p)
+#endif
+
+#ifdef __cplusplus
+    #define RAYGUI_CLITERAL(name) name
+#else
+    #define RAYGUI_CLITERAL(name) (name)
+#endif
+
+// Check if two rectangles are equal, used to validate a slider bounds as an id
+#ifndef CHECK_BOUNDS_ID
+    #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
+#endif
+
+#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
+
+// Embedded icons, no external file provided
+#define RAYGUI_ICON_SIZE               16          // Size of icons in pixels (squared)
+#define RAYGUI_ICON_MAX_ICONS         256          // Maximum number of icons
+#define RAYGUI_ICON_MAX_NAME_LENGTH    32          // Maximum length of icon name id
+
+// Icons data is defined by bit array (every bit represents one pixel)
+// Those arrays are stored as unsigned int data arrays, so,
+// every array element defines 32 pixels (bits) of information
+// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels)
+// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels)
+#define RAYGUI_ICON_DATA_ELEMENTS   (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32)
+
+//----------------------------------------------------------------------------------
+// Icons data for all gui possible icons (allocated on data segment by default)
+//
+// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so,
+// every 16x16 icon requires 8 integers (16*16/32) to be stored
+//
+// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(),
+// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS
+//
+// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = {
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_NONE
+    0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe,      // ICON_FOLDER_FILE_OPEN
+    0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe,      // ICON_FILE_SAVE_CLASSIC
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100,      // ICON_FOLDER_OPEN
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000,      // ICON_FOLDER_SAVE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc,      // ICON_FILE_OPEN
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc,      // ICON_FILE_SAVE
+    0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc,      // ICON_FILE_EXPORT
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc,      // ICON_FILE_ADD
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc,      // ICON_FILE_DELETE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_TEXT
+    0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_AUDIO
+    0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_IMAGE
+    0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_PLAY
+    0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4,      // ICON_FILETYPE_VIDEO
+    0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc,      // ICON_FILETYPE_INFO
+    0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0,      // ICON_FILE_COPY
+    0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000,      // ICON_FILE_CUT
+    0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0,      // ICON_FILE_PASTE
+    0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_CURSOR_HAND
+    0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000,      // ICON_CURSOR_POINTER
+    0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000,      // ICON_CURSOR_CLASSIC
+    0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000,      // ICON_PENCIL
+    0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000,      // ICON_PENCIL_BIG
+    0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8,      // ICON_BRUSH_CLASSIC
+    0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080,      // ICON_BRUSH_PAINTER
+    0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000,      // ICON_WATER_DROP
+    0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000,      // ICON_COLOR_PICKER
+    0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000,      // ICON_RUBBER
+    0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040,      // ICON_COLOR_BUCKET
+    0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000,      // ICON_TEXT_T
+    0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f,      // ICON_TEXT_A
+    0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e,      // ICON_SCALE
+    0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe,      // ICON_RESIZE
+    0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_POINT
+    0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_BILINEAR
+    0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002,      // ICON_CROP
+    0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000,      // ICON_CROP_ALPHA
+    0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002,      // ICON_SQUARE_TOGGLE
+    0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000,      // ICON_SYMMETRY
+    0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100,      // ICON_SYMMETRY_HORIZONTAL
+    0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180,      // ICON_SYMMETRY_VERTICAL
+    0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000,      // ICON_LENS
+    0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000,      // ICON_LENS_BIG
+    0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000,      // ICON_EYE_ON
+    0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000,      // ICON_EYE_OFF
+    0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100,      // ICON_FILTER_TOP
+    0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0,      // ICON_FILTER
+    0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_POINT
+    0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL
+    0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG
+    0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280,      // ICON_TARGET_MOVE
+    0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280,      // ICON_CURSOR_MOVE
+    0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e,      // ICON_CURSOR_SCALE
+    0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT
+    0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000,      // ICON_CURSOR_SCALE_LEFT
+    0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO
+    0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000,      // ICON_REREDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000,      // ICON_MUTATE
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020,      // ICON_ROTATE
+    0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000,      // ICON_REPEAT
+    0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000,      // ICON_SHUFFLE
+    0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe,      // ICON_EMPTYBOX
+    0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000,      // ICON_TARGET
+    0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL_FILL
+    0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380,      // ICON_TARGET_MOVE_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380,      // ICON_CURSOR_MOVE_FILL
+    0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e,      // ICON_CURSOR_SCALE_FILL
+    0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT_FILL
+    0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000,      // ICON_CURSOR_SCALE_LEFT_FILL
+    0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO_FILL
+    0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000,      // ICON_REREDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000,      // ICON_MUTATE_FILL
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020,      // ICON_ROTATE_FILL
+    0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000,      // ICON_REPEAT_FILL
+    0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000,      // ICON_SHUFFLE_FILL
+    0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000,      // ICON_EMPTYBOX_SMALL
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX
+    0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP
+    0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000,      // ICON_BOX_BOTTOM_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000,      // ICON_BOX_BOTTOM
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000,      // ICON_BOX_BOTTOM_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_LEFT
+    0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_CENTER
+    0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe,      // ICON_BOX_CIRCLE_MASK
+    0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff,      // ICON_POT
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe,      // ICON_ALPHA_MULTIPLY
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe,      // ICON_ALPHA_CLEAR
+    0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe,      // ICON_DITHERING
+    0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0,      // ICON_MIPMAPS
+    0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000,      // ICON_BOX_GRID
+    0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248,      // ICON_GRID
+    0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000,      // ICON_BOX_CORNERS_SMALL
+    0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e,      // ICON_BOX_CORNERS_BIG
+    0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000,      // ICON_FOUR_BOXES
+    0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000,      // ICON_GRID_FILL
+    0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e,      // ICON_BOX_MULTISIZE
+    0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_SMALL
+    0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_MEDIUM
+    0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e,      // ICON_ZOOM_BIG
+    0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e,      // ICON_ZOOM_ALL
+    0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000,      // ICON_ZOOM_CENTER
+    0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000,      // ICON_BOX_DOTS_SMALL
+    0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000,      // ICON_BOX_DOTS_BIG
+    0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe,      // ICON_BOX_CONCENTRIC
+    0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000,      // ICON_BOX_GRID_BIG
+    0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000,      // ICON_OK_TICK
+    0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000,      // ICON_CROSS
+    0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000,      // ICON_ARROW_LEFT
+    0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000,      // ICON_ARROW_RIGHT
+    0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN
+    0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP
+    0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000,      // ICON_ARROW_LEFT_FILL
+    0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000,      // ICON_ARROW_RIGHT_FILL
+    0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN_FILL
+    0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP_FILL
+    0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000,      // ICON_AUDIO
+    0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000,      // ICON_FX
+    0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000,      // ICON_WAVE
+    0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000,      // ICON_WAVE_SINUS
+    0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000,      // ICON_WAVE_SQUARE
+    0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000,      // ICON_WAVE_TRIANGULAR
+    0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000,      // ICON_CROSS_SMALL
+    0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000,      // ICON_PLAYER_PREVIOUS
+    0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000,      // ICON_PLAYER_PLAY_BACK
+    0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000,      // ICON_PLAYER_PLAY
+    0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000,      // ICON_PLAYER_PAUSE
+    0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000,      // ICON_PLAYER_STOP
+    0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000,      // ICON_PLAYER_NEXT
+    0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000,      // ICON_PLAYER_RECORD
+    0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000,      // ICON_MAGNET
+    0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_CLOSE
+    0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_OPEN
+    0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770,      // ICON_CLOCK
+    0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70,      // ICON_TOOLS
+    0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180,      // ICON_GEAR
+    0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180,      // ICON_GEAR_BIG
+    0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8,      // ICON_BIN
+    0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000,      // ICON_HAND_POINTER
+    0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000,      // ICON_LASER
+    0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000,      // ICON_COIN
+    0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000,      // ICON_EXPLOSION
+    0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000,      // ICON_1UP
+    0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240,      // ICON_PLAYER
+    0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000,      // ICON_PLAYER_JUMP
+    0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0,      // ICON_KEY
+    0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0,      // ICON_DEMON
+    0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000,      // ICON_TEXT_POPUP
+    0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000,      // ICON_GEAR_EX
+    0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK
+    0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK_POINTS
+    0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808,      // ICON_STAR
+    0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8,      // ICON_DOOR
+    0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0,      // ICON_EXIT
+    0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000,      // ICON_MODE_2D
+    0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000,      // ICON_MODE_3D
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE
+    0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_TOP
+    0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe,      // ICON_CUBE_FACE_LEFT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe,      // ICON_CUBE_FACE_FRONT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe,      // ICON_CUBE_FACE_BOTTOM
+    0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe,      // ICON_CUBE_FACE_RIGHT
+    0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_BACK
+    0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000,      // ICON_CAMERA
+    0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000,      // ICON_SPECIAL
+    0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800,      // ICON_LINK_NET
+    0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00,      // ICON_LINK_BOXES
+    0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000,      // ICON_LINK_MULTI
+    0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00,      // ICON_LINK
+    0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00,      // ICON_LINK_BROKE
+    0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_TEXT_NOTES
+    0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc,      // ICON_NOTEBOOK
+    0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000,      // ICON_SUITCASE
+    0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000,      // ICON_SUITCASE_ZIP
+    0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000,      // ICON_MAILBOX
+    0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000,      // ICON_MONITOR
+    0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000,      // ICON_PRINTER
+    0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA
+    0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA_FLASH
+    0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000,      // ICON_HOUSE
+    0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000,      // ICON_HEART
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000,      // ICON_CORNER
+    0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000,      // ICON_VERTICAL_BARS
+    0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000,      // ICON_VERTICAL_BARS_FILL
+    0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000,      // ICON_LIFE_BARS
+    0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc,      // ICON_INFO
+    0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002,      // ICON_CROSSLINE
+    0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000,      // ICON_HELP
+    0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc,      // ICON_FILETYPE_ALPHA
+    0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_HOME
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS_VISIBLE
+    0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS
+    0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_WINDOW
+    0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000,      // ICON_HIDPI
+    0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc,      // ICON_FILETYPE_BINARY
+    0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000,      // ICON_HEX
+    0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180,      // ICON_SHIELD
+    0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8,      // ICON_FILE_NEW
+    0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000,      // ICON_FOLDER_ADD
+    0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420,      // ICON_ALARM
+    0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0,      // ICON_CPU
+    0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0,      // ICON_ROM
+    0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000,      // ICON_STEP_OVER
+    0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_INTO
+    0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_OUT
+    0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000,      // ICON_RESTART
+    0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_ON
+    0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_OFF
+    0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000,      // ICON_BURGER_MENU
+    0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000,      // ICON_CASE_SENSITIVE
+    0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000,      // ICON_REG_EXP
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_FOLDER
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc,      // ICON_FILE
+    0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0,      // ICON_SAND_TIMER
+    0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc,      // ICON_WARNING
+    0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe,      // ICON_HELP_BOX
+    0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe,      // ICON_INFO_BOX
+    0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0,      // ICON_PRIORITY
+    0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000,      // ICON_LAYERS_ISO
+    0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
+    0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
+    0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060,      // ICON_LABEL
+    0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000,      // ICON_NAME_ID
+    0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000,      // ICON_SLICING
+    0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0,      // ICON_MANUAL_CONTROL
+    0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe,      // ICON_COLLISION
+    0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_ADD
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_ADD_FILL
+    0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_WARNING
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_WARNING_FILL
+    0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE
+    0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE_FILL
+    0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS
+    0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS_FILL
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0,      // ICON_UNION
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_INTERSECTION
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_DIFFERENCE
+    0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0,      // ICON_SPHERE
+    0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0,      // ICON_CYLINDER
+    0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0,      // ICON_CONE
+    0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000,      // ICON_ELLIPSOID
+    0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000,      // ICON_CAPSULE
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_250
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_251
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_252
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_253
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_254
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_255
+};
+
+// NOTE: A pointer to current icons array should be defined
+static unsigned int *guiIconsPtr = guiIcons;
+
+#endif      // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
+
+#ifndef RAYGUI_ICON_SIZE
+    #define RAYGUI_ICON_SIZE             0
+#endif
+
+// WARNING: Those values define the total size of the style data array,
+// if changed, previous saved styles could become incompatible
+#define RAYGUI_MAX_CONTROLS             16      // Maximum number of controls
+#define RAYGUI_MAX_PROPS_BASE           16      // Maximum number of base properties
+#define RAYGUI_MAX_PROPS_EXTENDED        8      // Maximum number of extended properties
+
+//----------------------------------------------------------------------------------
+// Module Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Gui control property style color element
+typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static GuiState guiState = STATE_NORMAL;        // Gui global state, if !STATE_NORMAL, forces defined state
+
+static Font guiFont = { 0 };                    // Gui current font (WARNING: highly coupled to raylib)
+static bool guiLocked = false;                  // Gui lock state (no inputs processed)
+static float guiAlpha = 1.0f;                   // Gui controls transparency
+
+static unsigned int guiIconScale = 1;           // Gui icon default scale (if icons enabled)
+
+static bool guiTooltip = false;                 // Tooltip enabled/disabled
+static const char *guiTooltipPtr = NULL;        // Tooltip string pointer (string provided by user)
+
+static bool guiControlExclusiveMode = false;    // Gui control exclusive mode (no inputs processed except current control)
+static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier
+
+static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
+//static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
+static int autoCursorCounter = 0;               // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
+
+//----------------------------------------------------------------------------------
+// Style data array for all gui style properties (allocated on data segment by default)
+//
+// NOTE 1: First set of BASE properties are generic to all controls but could be individually
+// overwritten per control, first set of EXTENDED properties are generic to all controls and
+// can not be overwritten individually but custom EXTENDED properties can be used by control
+//
+// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(),
+// but default gui style could always be recovered with GuiLoadStyleDefault()
+//
+// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
+
+static bool guiStyleLoaded = false;         // Style loaded flag for lazy style initialization
+
+//----------------------------------------------------------------------------------
+// Standalone Mode Functions Declaration
+//
+// NOTE: raygui depend on some raylib input and drawing functions
+// To use raygui as standalone library, below functions must be defined by the user
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+
+#define KEY_RIGHT           262
+#define KEY_LEFT            263
+#define KEY_DOWN            264
+#define KEY_UP              265
+#define KEY_BACKSPACE       259
+#define KEY_ENTER           257
+
+#define MOUSE_LEFT_BUTTON     0
+
+// Input required functions
+//-------------------------------------------------------------------------------
+static Vector2 GetMousePosition(void);
+static float GetMouseWheelMove(void);
+static bool IsMouseButtonDown(int button);
+static bool IsMouseButtonPressed(int button);
+static bool IsMouseButtonReleased(int button);
+
+static bool IsKeyDown(int key);
+static bool IsKeyPressed(int key);
+static int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+//-------------------------------------------------------------------------------
+
+// Drawing required functions
+//-------------------------------------------------------------------------------
+static void DrawRectangle(int x, int y, int width, int height, Color color);        // -- GuiDrawRectangle()
+static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+//-------------------------------------------------------------------------------
+
+// Text required functions
+//-------------------------------------------------------------------------------
+static Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font
+
+static Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+static void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+
+static char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+static void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+
+static const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+
+static int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+static void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+
+static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+//-------------------------------------------------------------------------------
+
+// raylib functions already implemented in raygui
+//-------------------------------------------------------------------------------
+static Color GetColor(int hexValue);                // Returns a Color struct from hexadecimal value
+static int ColorToInt(Color color);                 // Returns hexadecimal value for a Color
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec);   // Check if point is inside rectangle
+static const char *TextFormat(const char *text, ...);               // Formatting of text with variables to 'embed'
+static const char **TextSplit(const char *text, char delimiter, int *count);    // Split text into multiple strings
+static int TextToInteger(const char *text);         // Get integer value from text
+static float TextToFloat(const char *text);         // Get float value from text
+
+static int GetCodepointNext(const char *text, int *codepointSize);  // Get next codepoint in a UTF-8 encoded text
+static const char *CodepointToUTF8(int codepoint, int *byteSize);   // Encode codepoint into UTF-8 text (char array size returned as parameter)
+
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);  // Draw rectangle vertical gradient
+//-------------------------------------------------------------------------------
+
+#endif      // RAYGUI_STANDALONE
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize);    // Load style from memory (binary only)
+
+static Rectangle GetTextBounds(int control, Rectangle bounds);  // Get text bounds considering control bounds
+static const char *GetTextIcon(const char *text, int *iconId);  // Get text icon if provided and move text cursor
+
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint);     // Gui draw text using default font
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color);   // Gui draw rectangle using default raygui style
+
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow);   // Split controls text into multiple strings
+static Vector3 ConvertHSVtoRGB(Vector3 hsv);                    // Convert color data from HSV to RGB
+static Vector3 ConvertRGBtoHSV(Vector3 rgb);                    // Convert color data from RGB to HSV
+
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue);   // Scroll bar control, used by GuiScrollPanel()
+static void GuiTooltip(Rectangle controlRec);                   // Draw tooltip using control rec position
+
+static Color GuiFade(Color color, float alpha);         // Fade color by an alpha factor
+
+//----------------------------------------------------------------------------------
+// Gui Setup Functions Definition
+//----------------------------------------------------------------------------------
+// Enable gui global state
+// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups
+void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; }
+
+// Disable gui global state
+// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups
+void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; }
+
+// Lock gui global state
+void GuiLock(void) { guiLocked = true; }
+
+// Unlock gui global state
+void GuiUnlock(void) { guiLocked = false; }
+
+// Check if gui is locked (global state)
+bool GuiIsLocked(void) { return guiLocked; }
+
+// Set gui controls alpha global state
+void GuiSetAlpha(float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    guiAlpha = alpha;
+}
+
+// Set gui state (global state)
+void GuiSetState(int state) { guiState = (GuiState)state; }
+
+// Get gui state (global state)
+int GuiGetState(void) { return guiState; }
+
+// Set custom gui font
+// NOTE: Font loading/unloading is external to raygui
+void GuiSetFont(Font font)
+{
+    if (font.texture.id > 0)
+    {
+        // NOTE: If we try to setup a font but default style has not been
+        // lazily loaded before, it will be overwritten, so we need to force
+        // default style loading first
+        if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+        guiFont = font;
+    }
+}
+
+// Get custom gui font
+Font GuiGetFont(void)
+{
+    return guiFont;
+}
+
+// Set control style property value
+void GuiSetStyle(int control, int property, int value)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+
+    // Default properties are propagated to all controls
+    if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE))
+    {
+        for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+    }
+}
+
+// Get control style property value
+int GuiGetStyle(int control, int property)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property];
+}
+
+//----------------------------------------------------------------------------------
+// Gui Controls Functions Definition
+//----------------------------------------------------------------------------------
+
+// Window Box control
+int GuiWindowBox(Rectangle bounds, const char *title)
+{
+    // Window title bar height (including borders)
+    // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox()
+    #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT)
+        #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT        24
+    #endif
+
+    #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+        #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT      18
+    #endif
+
+    int result = 0;
+    //GuiState state = guiState;
+
+    int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT;
+
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
+    if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+
+    const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
+    Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
+    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+                                 statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
+
+    // Update control
+    //--------------------------------------------------------------------
+    // NOTE: Logic is directly managed by button
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiStatusBar(statusBar, title); // Draw window header as status bar
+    GuiPanel(windowPanel, NULL);    // Draw window base
+
+    // Draw window close button
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+    result = GuiButton(closeButtonRec, "x");
+#else
+    result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL));
+#endif
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Window close button clicked: result = 1
+}
+
+// Group Box control with text name
+int GuiGroupBox(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_GROUPBOX_LINE_THICK)
+        #define RAYGUI_GROUPBOX_LINE_THICK     1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+
+    GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Line control
+int GuiLine(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_LINE_MARGIN_TEXT)
+        #define RAYGUI_LINE_MARGIN_TEXT  12
+    #endif
+    #if !defined(RAYGUI_LINE_TEXT_PADDING)
+        #define RAYGUI_LINE_TEXT_PADDING  4
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR));
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color);
+    else
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = bounds.height;
+        textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
+        textBounds.y = bounds.y;
+
+        // Draw line with embedded text label: "--- text --------------"
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+        GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color);
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Panel control
+int GuiPanel(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_PANEL_BORDER_WIDTH)
+        #define RAYGUI_PANEL_BORDER_WIDTH   1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+    }
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Tab Bar control
+// NOTE: Using GuiToggle() for the TABS
+int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
+{
+    #define RAYGUI_TABBAR_ITEM_WIDTH    148
+
+    int result = -1;
+    //GuiState state = guiState;
+
+    Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height };
+
+    if (*active < 0) *active = 0;
+    else if (*active > count - 1) *active = count - 1;
+
+    int offsetX = 0;    // Required in case tabs go out of screen
+    offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth();
+    if (offsetX < 0) offsetX = 0;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Draw control
+    //--------------------------------------------------------------------
+    for (int i = 0; i < count; i++)
+    {
+        tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX;
+
+        if (tabBounds.x < GetScreenWidth())
+        {
+            // Draw tabs as toggle controls
+            int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT);
+            int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+            GuiSetStyle(TOGGLE, TEXT_PADDING, 8);
+
+            if (i == (*active))
+            {
+                toggle = true;
+                GuiToggle(tabBounds, text[i], &toggle);
+            }
+            else
+            {
+                toggle = false;
+                GuiToggle(tabBounds, text[i], &toggle);
+                if (toggle) *active = i;
+            }
+
+            // Close tab with middle mouse button pressed
+            if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i;
+
+            GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
+
+            // Draw tab close button
+            // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds))
+            int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+            int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+            GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i;
+#else
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i;
+#endif
+            GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+        }
+    }
+
+    // Draw tab-bar bottom line
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL)));
+    //--------------------------------------------------------------------
+
+    return result;     // Return as result the current TAB closing requested
+}
+
+// Scroll Panel control
+int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view)
+{
+    #define RAYGUI_MIN_SCROLLBAR_WIDTH     40
+    #define RAYGUI_MIN_SCROLLBAR_HEIGHT    40
+    #define RAYGUI_MIN_MOUSE_WHEEL_SPEED   20
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Rectangle temp = { 0 };
+    if (view == NULL) view = &temp;
+
+    Vector2 scrollPos = { 0.0f, 0.0f };
+    if (scroll != NULL) scrollPos = *scroll;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1;
+    }
+
+    bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+    bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+
+    // Recheck to account for the other scrollbar being visible
+    if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+    if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+
+    int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    int verticalScrollBarWidth =  hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    Rectangle horizontalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)horizontalScrollBarWidth
+    };
+    Rectangle verticalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)),
+        (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)verticalScrollBarWidth,
+        (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+    };
+
+    // Make sure scroll bars have a minimum width/height
+    if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
+    if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
+
+    // Calculate view area (area without the scrollbars)
+    *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } :
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth };
+
+    // Clip view area to the actual content size
+    if (view->width > content.width) view->width = content.width;
+    if (view->height > content.height) view->height = content.height;
+
+    float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f;
+    float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+#if defined(SUPPORT_SCROLLBAR_KEY_INPUT)
+            if (hasHorizontalScrollBar)
+            {
+                if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+
+            if (hasVerticalScrollBar)
+            {
+                if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+#endif
+            float wheelMove = GetMouseWheelMove();
+
+            // Set scrolling speed with mouse wheel based on ratio between bounds and content
+            Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height };
+            if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+            if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+
+            // Horizontal and vertical scrolling with mouse wheel
+            if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x;
+            else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll
+        }
+    }
+
+    // Normalize scroll values
+    if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin;
+    if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax;
+    if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin;
+    if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));        // Draw background
+
+    // Save size of the scrollbar slider
+    const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+
+    // Draw horizontal scrollbar if visible
+    if (hasHorizontalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content width and the widget width
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)));
+        scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax);
+    }
+    else scrollPos.x = 0.0f;
+
+    // Draw vertical scrollbar if visible
+    if (hasVerticalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content height and the widget height
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)));
+        scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax);
+    }
+    else scrollPos.y = 0.0f;
+
+    // Draw detail corner rectangle if both scroll bars are visible
+    if (hasHorizontalScrollBar && hasVerticalScrollBar)
+    {
+        Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 };
+        GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))));
+    }
+
+    // Draw scrollbar lines depending on current state
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
+
+    // Set scrollbar slider size back to the way it was before
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider);
+    //--------------------------------------------------------------------
+
+    if (scroll != NULL) *scroll = scrollPos;
+
+    return result;
+}
+
+// Label control
+int GuiLabel(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    //...
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Button control, returns true when clicked
+int GuiButton(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))));
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //------------------------------------------------------------------
+
+    return result;      // Button pressed: result = 1
+}
+
+// Label button control
+int GuiLabelButton(Rectangle bounds, const char *text)
+{
+    GuiState state = guiState;
+    bool pressed = false;
+
+    // NOTE: We force bounds.width to be all text
+    float textWidth = (float)GuiGetTextWidth(text);
+    if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return pressed;
+}
+
+// Toggle Button control
+int GuiToggle(Rectangle bounds, const char *text, bool *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (active == NULL) active = &temp;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check toggle button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_NORMAL;
+                *active = !(*active);
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_NORMAL)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))));
+    }
+    else
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3)));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)));
+    }
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Toggle Group control
+int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
+{
+    #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
+        #define RAYGUI_TOGGLEGROUP_MAX_ITEMS    32
+    #endif
+
+    int result = 0;
+    float initBoundsX = bounds.x;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 };
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, rows);
+
+    int prevRow = rows[0];
+
+    for (int i = 0; i < itemCount; i++)
+    {
+        if (prevRow != rows[i])
+        {
+            bounds.x = initBoundsX;
+            bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING));
+            prevRow = rows[i];
+        }
+
+        if (i == (*active))
+        {
+            toggle = true;
+            GuiToggle(bounds, items[i], &toggle);
+        }
+        else
+        {
+            toggle = false;
+            GuiToggle(bounds, items[i], &toggle);
+            if (toggle) *active = i;
+        }
+
+        bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING));
+    }
+
+    return result;
+}
+
+// Toggle Slider control extended
+int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    //bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle slider = {
+        0,      // Calculated later depending on the active toggle
+        bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+        (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount,
+        bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                (*active)++;
+                result = 1;
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED;
+    }
+
+    if (*active >= itemCount) *active = 0;
+    slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))),
+        GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL)));
+
+    // Draw internal slider
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+
+    // Draw text in slider
+    if (text != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Check Box control, returns 1 when state changed
+int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (checked == NULL) checked = &temp;
+
+    Rectangle textBounds = { 0 };
+
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        Rectangle totalBounds = {
+            (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x,
+            bounds.y,
+            bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING),
+            bounds.height,
+        };
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, totalBounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                *checked = !(*checked);
+                result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK);
+
+    if (*checked)
+    {
+        Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)),
+                            bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) };
+        GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)));
+    }
+
+    GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Combo Box control
+int GuiComboBox(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING));
+
+    Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING),
+                           (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height };
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    if (*active < 0) *active = 0;
+    else if (*active > (itemCount - 1)) *active = itemCount - 1;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds) ||
+            CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                *active += 1;
+                if (*active >= itemCount) *active = 0;      // Cyclic combobox
+            }
+
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw combo box main
+    GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))));
+    GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))));
+
+    // Draw selector using a custom button
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount));
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dropdown Box control
+// NOTE: Returns mouse click
+int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    int itemSelected = *active;
+    int itemFocused = -1;
+
+    int direction = 0; // Dropdown box open direction: down (default)
+    if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle boundsOpen = bounds;
+    boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+    if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
+
+    Rectangle itemBounds = bounds;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            // Check if mouse has been pressed or released outside limits
+            if (!CheckCollisionPointRec(mousePoint, boundsOpen))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+
+            // Check if already selected item has been pressed again
+            if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+
+            // Check focused and selected item
+            for (int i = 0; i < itemCount; i++)
+            {
+                // Update item rectangle y position for next item
+                if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = i;
+                    if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+                    {
+                        itemSelected = i;
+                        result = 1;         // Item selected
+                    }
+                    break;
+                }
+            }
+
+            itemBounds = bounds;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    result = 1;
+                    state = STATE_PRESSED;
+                }
+                else state = STATE_FOCUSED;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (editMode) GuiPanel(boundsOpen, NULL);
+
+    GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)));
+    GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)));
+
+    if (editMode)
+    {
+        // Draw visible items
+        for (int i = 0; i < itemCount; i++)
+        {
+            // Update item rectangle y position for next item
+            if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+            if (i == itemSelected)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)));
+            }
+            else if (i == itemFocused)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)));
+            }
+            else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)));
+        }
+    }
+
+    if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN))
+    {
+        // Draw arrows (using icon if available)
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    *active = itemSelected;
+
+    // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item...
+    return result;   // Mouse click: result = 1
+}
+
+// Text Box control
+// NOTE: Returns true on ENTER pressed (useful for data validation)
+int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  20        // Frames to wait for autocursor movement
+    #endif
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY      1        // Frames delay for autocursor movement
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    bool multiline = false;     // TODO: Consider multiline text input
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
+
+    Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
+    int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
+    int thisCursorIndex = textBoxCursorIndex;
+    if (thisCursorIndex > textLength) thisCursorIndex = textLength;
+    int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
+    int textIndexOffset = 0;    // Text index offset to start drawing in the box
+
+    // Cursor rectangle
+    // NOTE: Position X value should be updated
+    Rectangle cursor = {
+        textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
+        textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE),
+        2,
+        (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2
+    };
+
+    if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+    if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
+
+    // Mouse cursor rectangle
+    // NOTE: Initialized outside of screen
+    Rectangle mouseCursor = cursor;
+    mouseCursor.x = -1;
+    mouseCursor.width = 1;
+
+    // Blink-cursor frame counter
+    //if (!autoCursorMode) blinkCursorFrameCounter++;
+    //else blinkCursorFrameCounter = 0;
+
+    // Update control
+    //--------------------------------------------------------------------
+    // WARNING: Text editing is only supported under certain conditions:
+    if ((state != STATE_DISABLED) &&                // Control not disabled
+        !GuiGetStyle(TEXTBOX, TEXT_READONLY) &&     // TextBox not on read-only mode
+        !guiLocked &&                               // Gui not locked
+        !guiControlExclusiveMode &&                       // No gui slider on dragging
+        (wrapMode == TEXT_WRAP_NONE))               // No wrap mode
+    {
+        Vector2 mousePosition = GetMousePosition();
+
+        if (editMode)
+        {
+            // GLOBAL: Auto-cursor movement logic
+            // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+            if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+            else autoCursorCounter = 0;
+
+            bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
+            state = STATE_PRESSED;
+
+            if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+
+            // If text does not fit in the textbox and current cursor position is out of bounds,
+            // we add an index offset to text for drawing only what requires depending on cursor
+            while (textWidth >= textBounds.width)
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textIndexOffset, &nextCodepointSize);
+
+                textIndexOffset += nextCodepointSize;
+
+                textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
+            }
+
+            int codepoint = GetCharPressed();       // Get Unicode codepoint
+            if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
+
+            // Encode codepoint as UTF-8
+            int codepointSize = 0;
+            const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
+
+            // Handle text paste action
+            if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                const char *pasteText = GetClipboardText();
+                if (pasteText != NULL)
+                {
+                    int pasteLength = 0;
+                    int pasteCodepoint;
+                    int pasteCodepointSize;
+
+                    // Count how many codepoints to copy, stopping at the first unwanted control character
+                    while (true)
+                    {
+                        pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+                        if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+                        if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+                        pasteLength += pasteCodepointSize;
+                    }
+
+                    if (pasteLength > 0)
+                    {
+                        // Move forward data from cursor position
+                        for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+                        // Paste data in at cursor
+                        for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+                        textBoxCursorIndex += pasteLength;
+                        textLength += pasteLength;
+                        text[textLength] = '\0';
+                    }
+                }
+            }
+            else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            {
+                // Adding codepoint to text, at current cursor position
+
+                // Move forward data from cursor position
+                for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
+
+                // Add new codepoint in current cursor position
+                for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
+
+                textBoxCursorIndex += codepointSize;
+                textLength += codepointSize;
+
+                // Make sure text last character is EOL
+                text[textLength] = '\0';
+            }
+
+            // Move cursor to start
+            if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0;
+
+            // Move cursor to end
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
+
+            // Delete related codepoints from text, after current cursor position
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+                        break;
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to delete (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+            }
+
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, after current cursor position
+
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+                textLength -= nextCodepointSize;
+            }
+
+            // Delete related codepoints from text, before current cursor position
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
+
+                // Check whitespace to delete (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+                textBoxCursorIndex -= accCodepointSize;
+            }
+
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, before current cursor position
+
+                int prevCodepointSize = 0;
+
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
+
+                textLength -= prevCodepointSize;
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+
+            // Move cursor position with keys
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
+
+                // Check whitespace to skip (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
+            {
+                int prevCodepointSize = 0;
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+            else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to skip (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                textBoxCursorIndex += nextCodepointSize;
+            }
+
+            // Move cursor position with mouse
+            if (CheckCollisionPointRec(mousePosition, textBounds))     // Mouse hover text
+            {
+                float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize;
+                int codepointIndex = 0;
+                float glyphWidth = 0.0f;
+                float widthToMouseX = 0;
+                int mouseCursorIndex = 0;
+
+                for (int i = textIndexOffset; i < textLength; i += codepointSize)
+                {
+                    codepoint = GetCodepointNext(&text[i], &codepointSize);
+                    codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                    if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                    else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                    if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2)))
+                    {
+                        mouseCursor.x = textBounds.x + widthToMouseX;
+                        mouseCursorIndex = i;
+                        break;
+                    }
+
+                    widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                }
+
+                // Check if mouse cursor is at the last position
+                int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
+                if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
+                {
+                    mouseCursor.x = textBounds.x + textEndWidth;
+                    mouseCursorIndex = textLength;
+                }
+
+                // Place cursor at required index on mouse click
+                if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    cursor.x = mouseCursor.x;
+                    textBoxCursorIndex = mouseCursorIndex;
+                }
+            }
+            else mouseCursor.x = -1;
+
+            // Recalculate cursor position.y depending on textBoxCursorIndex
+            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+            //if (multiline) cursor.y = GetTextLines()
+
+            // Finish text editing on ENTER or mouse click outside bounds
+            if ((!multiline && IsKeyPressed(KEY_ENTER)) ||
+                (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                textBoxCursorIndex = 0;     // GLOBAL: Reset the shared cursor index
+                autoCursorCounter = 0;      // GLOBAL: Reset counter for repeated keystrokes
+                result = 1;
+            }
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePosition, bounds))
+            {
+                state = STATE_FOCUSED;
+
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
+                    autoCursorCounter = 0;             // GLOBAL: Reset counter for repeated keystrokes
+                    result = 1;
+                }
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_PRESSED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)));
+    }
+    else if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)));
+    }
+    else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK);
+
+    // Draw text considering index offset if required
+    // NOTE: Text index offset depends on cursor position
+    GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))));
+
+    // Draw cursor
+    if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY))
+    {
+        //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0))
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+
+        // Draw mouse position cursor (if required)
+        if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+    }
+    else if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;      // Mouse button pressed: result = 1
+}
+
+/*
+// Text Box control with multiple lines and word-wrap
+// NOTE: This text-box is readonly, no editing supported by default
+bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    bool pressed = false;
+
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD);   // WARNING: If wrap mode enabled, text editing is not supported
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
+
+    // TODO: Implement methods to calculate cursor position properly
+    pressed = GuiTextBox(bounds, text, textSize, editMode);
+
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE);
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+
+    return pressed;
+}
+*/
+
+// Spinner control, returns selected value
+int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    int result = 1;
+    GuiState state = guiState;
+
+    int tempValue = *value;
+
+    Rectangle valueBoxBounds = {
+        bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+        bounds.y,
+        bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+        (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check spinner state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+
+#if defined(RAYGUI_NO_ICONS)
+    if (GuiButton(leftButtonBound, "<")) tempValue--;
+    if (GuiButton(rightButtonBound, ">")) tempValue++;
+#else
+    if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--;
+    if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++;
+#endif
+
+    if (!editMode)
+    {
+        if (tempValue < minValue) tempValue = minValue;
+        if (tempValue > maxValue) tempValue = maxValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
+
+    // Draw value selector custom buttons
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    *value = tempValue;
+    return result;
+}
+
+// Value Box control, updates input text with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+    snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Add new digit to text value
+            if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+            {
+                int key = GetCharPressed();
+                
+                // Only allow keys in range [48..57]
+                if ((key >= 48) && (key <= 57))
+                {
+                    textValue[keyCount] = (char)key;
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Delete text
+            if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
+            {
+                keyCount--;
+                textValue[keyCount] = '\0';
+                valueHasChanged = true;
+            }
+
+            if (valueHasChanged) *value = TextToInteger(textValue);
+
+            // NOTE: We are not clamp values until user input finishes
+            //if (*value > maxValue) *value = maxValue;
+            //else if (*value < minValue) *value = minValue;
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                if (*value > maxValue) *value = maxValue;
+                else if (*value < minValue) *value = minValue;
+
+                result = 1;
+            }
+        }
+        else
+        {
+            if (*value > maxValue) *value = maxValue;
+            else if (*value < minValue) *value = minValue;
+
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor rectangle
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+            bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+            2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+        if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Floating point Value Box control, updates input val_str with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+    //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Only allow keys in range [48..57]
+            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            {
+                if (GuiGetTextWidth(textValue) < bounds.width)
+                {
+                    int key = GetCharPressed();
+                    if (((key >= 48) && (key <= 57)) ||
+                        (key == '.') ||
+                        ((keyCount == 0) && (key == '+')) ||  // NOTE: Sign can only be in first position
+                        ((keyCount == 0) && (key == '-')))
+                    {
+                        textValue[keyCount] = (char)key;
+                        keyCount++;
+
+                        valueHasChanged = true;
+                    }
+                }
+            }
+
+            // Pressed backspace
+            if (IsKeyPressed(KEY_BACKSPACE))
+            {
+                if (keyCount > 0)
+                {
+                    keyCount--;
+                    textValue[keyCount] = '\0';
+                    valueHasChanged = true;
+                }
+            }
+
+            if (valueHasChanged) *value = TextToFloat(textValue);
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+                            bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
+                            bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds,
+                (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT,
+                GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider control with pro parameters
+// NOTE: Other GuiSlider*() controls use this one
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+    float oldValue = *value;
+
+    int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
+    Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+                         0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if (*value > maxValue) *value = maxValue;
+        else if (*value < minValue) *value = minValue;
+    }
+
+    // Control value change check
+    if (oldValue == *value) result = 0;
+    else result = 1;
+
+    // Slider bar limits check
+    float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
+    if (sliderWidth > 0)        // Slider
+    {
+        slider.x += sliderValue;
+        slider.width = (float)sliderWidth;
+        if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
+        else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    else if (sliderWidth == 0)  // SliderBar
+    {
+        slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+        slider.width = sliderValue;
+        if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)?  BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+
+    // Draw slider internal bar (depends on state)
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+    else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider Bar control extended, returns selected value
+int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+    result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+    return result;
+}
+
+// Progress Bar control extended, shows current progress value
+int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+
+    // Progress bar
+    Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
+                           bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
+                           bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if (*value > maxValue) *value = maxValue;
+
+    // WARNING: Working with floats could lead to rounding issues
+    if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK);
+    }
+    else
+    {
+        if (*value > minValue)
+        {
+            // Draw progress bar with colored border, more visual
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        }
+        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+
+        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        else
+        {
+            // Draw borders not yet reached by value
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+        }
+
+        // Draw slider internal progress bar (depends on state)
+        GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+    }
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Status Bar control
+int GuiStatusBar(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dummy rectangle control, intended for placeholding
+int GuiDummyRec(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+    GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)));
+    //------------------------------------------------------------------
+
+    return result;
+}
+
+// List View control
+int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active)
+{
+    int result = 0;
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL);
+
+    return result;
+}
+
+// List View control with extended parameters
+int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int itemFocused = (focus == NULL)? -1 : *focus;
+    int itemSelected = (active == NULL)? -1 : *active;
+
+    // Check if we need a scroll bar
+    bool useScrollBar = false;
+    if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true;
+
+    // Define base item rectangle [0]
+    Rectangle itemBounds = { 0 };
+    itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING);
+    itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
+    if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
+
+    // Get items on the list
+    int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    if (visibleItems > count) visibleItems = count;
+
+    int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
+    if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
+    int endIndex = startIndex + visibleItems;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check mouse inside list view
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Check focused and selected item
+            for (int i = 0; i < visibleItems; i++)
+            {
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = startIndex + i;
+                    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                    {
+                        if (itemSelected == (startIndex + i)) itemSelected = -1;
+                        else itemSelected = startIndex + i;
+                    }
+                    break;
+                }
+
+                // Update item rectangle y position for next item
+                itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+            }
+
+            if (useScrollBar)
+            {
+                int wheelMove = (int)GetMouseWheelMove();
+                startIndex -= wheelMove;
+
+                if (startIndex < 0) startIndex = 0;
+                else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
+
+                endIndex = startIndex + visibleItems;
+                if (endIndex > count) endIndex = count;
+            }
+        }
+        else itemFocused = -1;
+
+        // Reset item rectangle y to [0]
+        itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+
+    // Draw visible items
+    for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
+    {
+        if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+
+        if (state == STATE_DISABLED)
+        {
+            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+
+            GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
+        }
+        else
+        {
+            if (((startIndex + i) == itemSelected) && (active != NULL))
+            {
+                // Draw item selected
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
+            }
+            else if (((startIndex + i) == itemFocused)) // && (focus != NULL))  // NOTE: We want items focused, despite not returned!
+            {
+                // Draw item focused
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
+            }
+            else
+            {
+                // Draw item normal (no rectangle)
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
+            }
+        }
+
+        // Update item rectangle y position for next item
+        itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    }
+
+    if (useScrollBar)
+    {
+        Rectangle scrollBarBounds = {
+            bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+        };
+
+        // Calculate percentage of visible items and apply same percentage to scrollbar
+        float percentVisible = (float)(endIndex - startIndex)/count;
+        float sliderSize = bounds.height*percentVisible;
+
+        int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);   // Save default slider size
+        int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize);            // Change slider size
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
+
+        startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
+
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default
+    }
+    //--------------------------------------------------------------------
+
+    if (active != NULL) *active = itemSelected;
+    if (focus != NULL) *focus = itemFocused;
+    if (scrollIndex != NULL) *scrollIndex = startIndex;
+
+    return result;
+}
+
+// Color Panel control - Color (RGBA) variant
+int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
+    Vector3 hsv = ConvertRGBtoHSV(vcolor);
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
+
+    GuiColorPanelHSV(bounds, text, &hsv);
+
+    // Check if the hsv was changed, only then change the color
+    // This is required, because the Color->HSV->Color conversion has precision errors
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+    // Otherwise GuiColorPanel would often modify it's color without user input
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
+    if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
+    {
+        Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+        // NOTE: Vector3ToColor() only available on raylib 1.8.1
+        *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+                            (unsigned char)(255.0f*rgb.y),
+                            (unsigned char)(255.0f*rgb.z),
+                            color->a };
+    }
+    return result;
+}
+
+// Color Bar Alpha control
+// NOTE: Returns alpha value normalized [0..1]
+int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+{
+    #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE)
+        #define RAYGUI_COLORBARALPHA_CHECKED_SIZE   10
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+        (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+        (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+        (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                    if (*alpha <= 0.0f) *alpha = 0.0f;
+                    if (*alpha >= 1.0f) *alpha = 1.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                if (*alpha <= 0.0f) *alpha = 0.0f;
+                if (*alpha >= 1.0f) *alpha = 1.0f;
+                //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw alpha bar: checked background
+    if (state != STATE_DISABLED)
+    {
+        int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+        int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+
+        for (int x = 0; x < checksX; x++)
+        {
+            for (int y = 0; y < checksY; y++)
+            {
+                Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE };
+                GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f));
+            }
+        }
+
+        DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw alpha bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Bar Hue control
+// Returns hue value normalized [0..1]
+// NOTE: Other similar bars (for reference):
+//      Color GuiColorBarSat() [WHITE->color]
+//      Color GuiColorBarValue() [BLACK->color], HSV/HSL
+//      float GuiColorBarLuminance() [BLACK->WHITE]
+int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                    if (*hue <= 0.0f) *hue = 0.0f;
+                    if (*hue >= 359.0f) *hue = 359.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                if (*hue <= 0.0f) *hue = 0.0f;
+                if (*hue >= 359.0f) *hue = 359.0f;
+
+            }
+            else state = STATE_FOCUSED;
+
+            /*if (IsKeyDown(KEY_UP))
+            {
+                hue -= 2.0f;
+                if (hue <= 0.0f) hue = 0.0f;
+            }
+            else if (IsKeyDown(KEY_DOWN))
+            {
+                hue += 2.0f;
+                if (hue >= 360.0f) hue = 360.0f;
+            }*/
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        // Draw hue bar:color bars
+        // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw hue bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Picker control
+// NOTE: It's divided in multiple controls:
+//      Color GuiColorPanel(Rectangle bounds, Color color)
+//      float GuiColorBarAlpha(Rectangle bounds, float alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanel() size
+// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead
+int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Color temp = { 200, 0, 0, 255 };
+    if (color == NULL) color = &temp;
+
+    GuiColorPanel(bounds, NULL, color);
+
+    Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+    //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
+
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
+    Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
+
+    GuiColorBarHue(boundsHue, NULL, &hsv.x);
+
+    //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
+    Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+    *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a };
+
+    return result;
+}
+
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
+// NOTE: It's divided in multiple controls:
+//      int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+//      int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanelHSV() size
+int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+
+    Vector3 tempHsv = { 0 };
+
+    if (colorHsv == NULL)
+    {
+        const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f };
+        tempHsv = ConvertRGBtoHSV(tempColor);
+        colorHsv = &tempHsv;
+    }
+
+    GuiColorPanelHSV(bounds, NULL, colorHsv);
+
+    const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+
+    GuiColorBarHue(boundsHue, NULL, &colorHsv->x);
+
+    return result;
+}
+
+// Color Panel control - HSV variant
+int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Vector2 pickerSelector = { 0 };
+
+    const Color colWhite = { 255, 255, 255, 255 };
+    const Color colBlack = { 0, 0, 0, 255 };
+
+    pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width;            // HSV: Saturation
+    pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height;  // HSV: Value
+
+    Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f };
+    Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
+    Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
+                      (unsigned char)(255.0f*rgbHue.y),
+                      (unsigned char)(255.0f*rgbHue.z), 255 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    pickerSelector = mousePoint;
+
+                    if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x;
+                    if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width;
+                    if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y;
+                    if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height;
+
+                    // Calculate color from picker
+                    Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                    colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                    colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                    colorHsv->y = colorPick.x;
+                    colorHsv->z = 1.0f - colorPick.y;
+
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds;
+                pickerSelector = mousePoint;
+
+                // Calculate color from picker
+                Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                colorHsv->y = colorPick.x;
+                colorHsv->z = 1.0f - colorPick.y;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
+        DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
+
+        // Draw color picker: selector
+        Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) };
+        GuiDrawRectangle(selector, 0, BLANK, colWhite);
+    }
+    else
+    {
+        DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
+    }
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Message Box control
+int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons)
+{
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT)
+        #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT    24
+    #endif
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING)
+        #define RAYGUI_MESSAGEBOX_BUTTON_PADDING   12
+    #endif
+
+    int result = -1;    // Returns clicked button from buttons list, 0 refers to closed window button
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    //int textWidth = GuiGetTextWidth(message) + 2;
+
+    Rectangle textBounds = { 0 };
+    textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2;
+    textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiLabel(textBounds, message);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+
+    prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Text Input Box control, ask for text
+int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive)
+{
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT      24
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING     12
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_HEIGHT             26
+    #endif
+
+    // Used to enable text edit mode
+    // WARNING: No more than one GuiTextInputBox() should be open at the same time
+    static bool textEditMode = false;
+
+    int result = -1;
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT;
+
+    int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+
+    Rectangle textBounds = { 0 };
+    if (message != NULL)
+    {
+        int textSize = GuiGetTextWidth(message) + 2;
+
+        textBounds.x = bounds.x + bounds.width/2 - textSize/2;
+        textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        textBounds.width = (float)textSize;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+    }
+
+    Rectangle textBoxBounds = { 0 };
+    textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2;
+    if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4);
+    textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2;
+    textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    // Draw message if available
+    if (message != NULL)
+    {
+        int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(textBounds, message);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+    }
+
+    if (secretViewActive != NULL)
+    {
+        static char stars[] = "****************";
+        if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height },
+            ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+
+        GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive);
+    }
+    else
+    {
+        if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+    }
+
+    int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    if (result >= 0) textEditMode = false;
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Result is the pressed button index
+}
+
+// Grid control
+// NOTE: Returns grid mouse-hover selected cell
+// About drawing lines at subpixel spacing, simple put, not easy solution:
+// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
+int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell)
+{
+    // Grid lines alpha amount
+    #if !defined(RAYGUI_GRID_ALPHA)
+        #define RAYGUI_GRID_ALPHA    0.15f
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Vector2 mousePoint = GetMousePosition();
+    Vector2 currentMouseCell = { -1, -1 };
+
+    float spaceWidth = spacing/(float)subdivs;
+    int linesV = (int)(bounds.width/spaceWidth) + 1;
+    int linesH = (int)(bounds.height/spaceWidth) + 1;
+
+    int color = GuiGetStyle(DEFAULT, LINE_COLOR);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            // NOTE: Cell values must be the upper left of the cell the mouse is in
+            currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing);
+            currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing);
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED);
+
+    if (subdivs > 0)
+    {
+        // Draw vertical grid lines
+        for (int i = 0; i < linesV; i++)
+        {
+            Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 };
+            GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+
+        // Draw horizontal grid lines
+        for (int i = 0; i < linesH; i++)
+        {
+            Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 };
+            GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+    }
+
+    if (mouseCell != NULL) *mouseCell = currentMouseCell;
+    return result;
+}
+
+//----------------------------------------------------------------------------------
+// Tooltip management functions
+// NOTE: Tooltips requires some global variables: tooltipPtr
+//----------------------------------------------------------------------------------
+// Enable gui tooltips (global state)
+void GuiEnableTooltip(void) { guiTooltip = true; }
+
+// Disable gui tooltips (global state)
+void GuiDisableTooltip(void) { guiTooltip = false; }
+
+// Set tooltip string
+void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
+
+//----------------------------------------------------------------------------------
+// Styles loading functions
+//----------------------------------------------------------------------------------
+
+// Load raygui style file (.rgs)
+// NOTE: By default a binary file is expected, that file could contain a custom font,
+// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE)
+void GuiLoadStyle(const char *fileName)
+{
+    #define MAX_LINE_BUFFER_SIZE    256
+
+    bool tryBinary = false;
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+    // Try reading the files as text file first
+    FILE *rgsFile = fopen(fileName, "rt");
+
+    if (rgsFile != NULL)
+    {
+        char buffer[MAX_LINE_BUFFER_SIZE] = { 0 };
+        fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+
+        if (buffer[0] == '#')
+        {
+            int controlId = 0;
+            int propertyId = 0;
+            unsigned int propertyValue = 0;
+
+            while (!feof(rgsFile))
+            {
+                switch (buffer[0])
+                {
+                    case 'p':
+                    {
+                        // Style property: p <control_id> <property_id> <property_value> <property_name>
+
+                        sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue);
+                        GuiSetStyle(controlId, propertyId, (int)propertyValue);
+
+                    } break;
+                    case 'f':
+                    {
+                        // Style font: f <gen_font_size> <charmap_file> <font_file>
+
+                        int fontSize = 0;
+                        char charmapFileName[256] = { 0 };
+                        char fontFileName[256] = { 0 };
+                        sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName);
+
+                        Font font = { 0 };
+                        int *codepoints = NULL;
+                        int codepointCount = 0;
+
+                        if (charmapFileName[0] != '0')
+                        {
+                            // Load text data from file
+                            // NOTE: Expected an UTF-8 array of codepoints, no separation
+                            char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName));
+                            codepoints = LoadCodepoints(textData, &codepointCount);
+                            UnloadFileText(textData);
+                        }
+
+                        if (fontFileName[0] != '\0')
+                        {
+                            // In case a font is already loaded and it is not default internal font, unload it
+                            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+
+                            if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount);
+                            else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0);   // Default to 95 standard codepoints
+                        }
+
+                        // If font texture not properly loaded, revert to default font and size/spacing
+                        if (font.texture.id == 0)
+                        {
+                            font = GetFontDefault();
+                            GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+                            GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+                        }
+
+                        UnloadCodepoints(codepoints);
+
+                        if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font);
+
+                    } break;
+                    default: break;
+                }
+
+                fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+            }
+        }
+        else tryBinary = true;
+
+        fclose(rgsFile);
+    }
+
+    if (tryBinary)
+    {
+        rgsFile = fopen(fileName, "rb");
+
+        if (rgsFile != NULL)
+        {
+            fseek(rgsFile, 0, SEEK_END);
+            int fileDataSize = ftell(rgsFile);
+            fseek(rgsFile, 0, SEEK_SET);
+
+            if (fileDataSize > 0)
+            {
+                unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
+                fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
+
+                GuiLoadStyleFromMemory(fileData, fileDataSize);
+
+                RAYGUI_FREE(fileData);
+            }
+
+            fclose(rgsFile);
+        }
+    }
+}
+
+// Load style default over global style
+void GuiLoadStyleDefault(void)
+{
+    // We set this variable first to avoid cyclic function calls
+    // when calling GuiSetStyle() and GuiGetStyle()
+    guiStyleLoaded = true;
+
+    // Initialize default LIGHT style property values
+    // WARNING: Default value are applied to all controls on set but
+    // they can be overwritten later on for every custom control
+    GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff);
+    GuiSetStyle(DEFAULT, BORDER_WIDTH, 1);
+    GuiSetStyle(DEFAULT, TEXT_PADDING, 0);
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    // Initialize default extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(DEFAULT, TEXT_SIZE, 10);                // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, TEXT_SPACING, 1);              // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff);       // DEFAULT specific property
+    GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property
+    GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15);        // DEFAULT, 15 pixels between lines
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);   // DEFAULT, text aligned vertically to middle of text-bounds
+
+    // Initialize control-specific property values
+    // NOTE: Those properties are in default list but require specific values by control type
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
+    GuiSetStyle(SLIDER, TEXT_PADDING, 4);
+    GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT);
+    GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0);
+    GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiSetStyle(TEXTBOX, TEXT_PADDING, 4);
+    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
+    GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
+    GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+
+    // Initialize extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(TOGGLE, GROUP_PADDING, 2);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 16);
+    GuiSetStyle(SLIDER, SLIDER_PADDING, 1);
+    GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1);
+    GuiSetStyle(CHECKBOX, CHECK_PADDING, 1);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
+    GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
+    GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
+    GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16);
+    GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
+    GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16);
+    GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2);
+
+    if (guiFont.texture.id != GetFontDefault().texture.id)
+    {
+        // Unload previous font texture
+        UnloadTexture(guiFont.texture);
+        RAYGUI_FREE(guiFont.recs);
+        RAYGUI_FREE(guiFont.glyphs);
+        guiFont.recs = NULL;
+        guiFont.glyphs = NULL;
+
+        // Setup default raylib font
+        guiFont = GetFontDefault();
+
+        // NOTE: Default raylib font character 95 is a white square
+        Rectangle whiteChar = guiFont.recs[95];
+
+        // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
+        SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 });
+    }
+}
+
+// Get text with icon id prepended
+// NOTE: Useful to add icons by name id (enum) instead of
+// a number that can change between ricon versions
+const char *GuiIconText(int iconId, const char *text)
+{
+#if defined(RAYGUI_NO_ICONS)
+    return NULL;
+#else
+    static char buffer[1024] = { 0 };
+    static char iconBuffer[16] = { 0 };
+
+    if (text != NULL)
+    {
+        memset(buffer, 0, 1024);
+        snprintf(buffer, 1024, "#%03i#", iconId);
+
+        for (int i = 5; i < 1024; i++)
+        {
+            buffer[i] = text[i - 5];
+            if (text[i - 5] == '\0') break;
+        }
+
+        return buffer;
+    }
+    else
+    {
+        snprintf(iconBuffer, 16, "#%03i#", iconId);
+
+        return iconBuffer;
+    }
+#endif
+}
+
+#if !defined(RAYGUI_NO_ICONS)
+// Get full icons data pointer
+unsigned int *GuiGetIcons(void) { return guiIconsPtr; }
+
+// Load raygui icons file (.rgi)
+// NOTE: In case nameIds are required, they can be requested with loadIconsName,
+// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH],
+// WARNING: guiIconsName[]][] memory should be manually freed!
+char **GuiLoadIcons(const char *fileName, bool loadIconsName)
+{
+    // Style File Structure (.rgi)
+    // ------------------------------------------------------
+    // Offset  | Size    | Type       | Description
+    // ------------------------------------------------------
+    // 0       | 4       | char       | Signature: "rGI "
+    // 4       | 2       | short      | Version: 100
+    // 6       | 2       | short      | reserved
+
+    // 8       | 2       | short      | Num icons (N)
+    // 10      | 2       | short      | Icons size (Options: 16, 32, 64) (S)
+
+    // Icons name id (32 bytes per name id)
+    // foreach (icon)
+    // {
+    //   12+32*i  | 32   | char       | Icon NameId
+    // }
+
+    // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size)
+    // S*S pixels/32bit per unsigned int = K unsigned int per icon
+    // foreach (icon)
+    // {
+    //   ...   | K       | unsigned int | Icon Data
+    // }
+
+    FILE *rgiFile = fopen(fileName, "rb");
+
+    char **guiIconsName = NULL;
+
+    if (rgiFile != NULL)
+    {
+        char signature[5] = { 0 };
+        short version = 0;
+        short reserved = 0;
+        short iconCount = 0;
+        short iconSize = 0;
+
+        fread(signature, 1, 4, rgiFile);
+        fread(&version, sizeof(short), 1, rgiFile);
+        fread(&reserved, sizeof(short), 1, rgiFile);
+        fread(&iconCount, sizeof(short), 1, rgiFile);
+        fread(&iconSize, sizeof(short), 1, rgiFile);
+
+        if ((signature[0] == 'r') &&
+            (signature[1] == 'G') &&
+            (signature[2] == 'I') &&
+            (signature[3] == ' '))
+        {
+            if (loadIconsName)
+            {
+                guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+                for (int i = 0; i < iconCount; i++)
+                {
+                    guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                    fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
+                }
+            }
+            else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
+
+            // Read icons data directly over internal icons array
+            fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
+        }
+
+        fclose(rgiFile);
+    }
+
+    return guiIconsName;
+}
+
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+    char **guiIconsName = NULL;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    short iconCount = 0;
+    short iconSize = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+    memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'I') &&
+        (signature[3] == ' '))
+    {
+        if (loadIconsName)
+        {
+            guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+            for (int i = 0; i < iconCount; i++)
+            {
+                guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+                fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+            }
+        }
+        else
+        {
+            // Skip icon name data if not required
+            fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+        }
+
+        int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+        guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+        memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+    }
+
+    return guiIconsName;
+}
+
+// Draw selected icon using rectangles pixel-by-pixel
+void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
+{
+    #define BIT_CHECK(a,b) ((a) & (1u<<(b)))
+
+    for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++)
+    {
+        for (int k = 0; k < 32; k++)
+        {
+            if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k))
+            {
+            #if !defined(RAYGUI_STANDALONE)
+                GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color);
+            #endif
+            }
+
+            if ((k == 15) || (k == 31)) y++;
+        }
+    }
+}
+
+// Set icon drawing size
+void GuiSetIconScale(int scale)
+{
+    if (scale >= 1) guiIconScale = scale;
+}
+
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    Vector2 textSize = { 0 };
+    int textIconOffset = 0;
+
+    if ((text != NULL) && (text[0] != '\0'))
+    {
+        if (text[0] == '#')
+        {
+            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+            {
+                if (text[i] == '#')
+                {
+                    textIconOffset = i;
+                    break;
+                }
+            }
+        }
+
+        text += textIconOffset;
+
+        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+        // Custom MeasureText() implementation
+        if ((guiFont.texture.id > 0) && (text != NULL))
+        {
+            // Get size in bytes of text, considering end of line and line break
+            int size = 0;
+            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+            {
+                if ((text[i] != '\0') && (text[i] != '\n')) size++;
+                else break;
+            }
+
+            float scaleFactor = fontSize/(float)guiFont.baseSize;
+            textSize.y = (float)guiFont.baseSize*scaleFactor;
+            float glyphWidth = 0.0f;
+
+            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+            {
+                int codepoint = GetCodepointNext(&text[i], &codepointSize);
+                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+    }
+
+    return (int)textSize.x;
+}
+
+#endif      // !RAYGUI_NO_ICONS
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Load style from memory
+// WARNING: Binary files only
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    int propertyCount = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'S') &&
+        (signature[3] == ' '))
+    {
+        short controlId = 0;
+        short propertyId = 0;
+        unsigned int propertyValue = 0;
+
+        for (int i = 0; i < propertyCount; i++)
+        {
+            memcpy(&controlId, fileDataPtr, sizeof(short));
+            memcpy(&propertyId, fileDataPtr + 2, sizeof(short));
+            memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int));
+            fileDataPtr += 8;
+
+            if (controlId == 0) // DEFAULT control
+            {
+                // If a DEFAULT property is loaded, it is propagated to all controls
+                // NOTE: All DEFAULT properties should be defined first in the file
+                GuiSetStyle(0, (int)propertyId, propertyValue);
+
+                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue);
+            }
+            else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
+        }
+
+        // Font loading is highly dependant on raylib API to load font data and image
+
+#if !defined(RAYGUI_STANDALONE)
+        // Load custom font if available
+        int fontDataSize = 0;
+        memcpy(&fontDataSize, fileDataPtr, sizeof(int));
+        fileDataPtr += 4;
+
+        if (fontDataSize > 0)
+        {
+            Font font = { 0 };
+            int fontType = 0;   // 0-Normal, 1-SDF
+
+            memcpy(&font.baseSize, fileDataPtr, sizeof(int));
+            memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int));
+            memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            // Load font white rectangle
+            Rectangle fontWhiteRec = { 0 };
+            memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle));
+            fileDataPtr += 16;
+
+            // Load font image parameters
+            int fontImageUncompSize = 0;
+            int fontImageCompSize = 0;
+            memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int));
+            memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int));
+            fileDataPtr += 8;
+
+            Image imFont = { 0 };
+            imFont.mipmaps = 1;
+            memcpy(&imFont.width, fileDataPtr, sizeof(int));
+            memcpy(&imFont.height, fileDataPtr + 4, sizeof(int));
+            memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize))
+            {
+                // Compressed font atlas image data (DEFLATE), it requires DecompressData()
+                int dataUncompSize = 0;
+                unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
+                memcpy(compData, fileDataPtr, fontImageCompSize);
+                fileDataPtr += fontImageCompSize;
+
+                imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
+
+                // Security check, dataUncompSize must match the provided fontImageUncompSize
+                if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
+
+                RAYGUI_FREE(compData);
+            }
+            else
+            {
+                // Font atlas image data is not compressed
+                imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
+                memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
+                fileDataPtr += fontImageUncompSize;
+            }
+
+            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+            font.texture = LoadTextureFromImage(imFont);
+
+            RAYGUI_FREE(imFont.data);
+
+            // Validate font atlas texture was loaded correctly
+            if (font.texture.id != 0)
+            {
+                // Load font recs data
+                int recsDataSize = font.glyphCount*sizeof(Rectangle);
+                int recsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed recs data
+                    memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
+                {
+                    // Recs data is compressed, uncompress it
+                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
+                    fileDataPtr += recsDataCompressedSize;
+
+                    int recsDataUncompSize = 0;
+                    font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted");
+
+                    RAYGUI_FREE(recsDataCompressed);
+                }
+                else
+                {
+                    // Recs data is uncompressed
+                    font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle));
+                        fileDataPtr += sizeof(Rectangle);
+                    }
+                }
+
+                // Load font glyphs info data
+                int glyphsDataSize = font.glyphCount*16;    // 16 bytes data per glyph
+                int glyphsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed glyphs data
+                    memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                // Allocate required glyphs space to fill with data
+                font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
+
+                if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
+                {
+                    // Glyphs data is compressed, uncompress it
+                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
+                    fileDataPtr += glyphsDataCompressedSize;
+
+                    int glyphsDataUncompSize = 0;
+                    unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted");
+
+                    unsigned char *glyphsDataUncompPtr = glyphsDataUncomp;
+
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int));
+                        glyphsDataUncompPtr += 16;
+                    }
+
+                    RAYGUI_FREE(glypsDataCompressed);
+                    RAYGUI_FREE(glyphsDataUncomp);
+                }
+                else
+                {
+                    // Glyphs data is uncompressed
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int));
+                        fileDataPtr += 16;
+                    }
+                }
+            }
+            else font = GetFontDefault();   // Fallback in case of errors loading font atlas texture
+
+            GuiSetFont(font);
+
+            // Set font texture source rectangle to be used as white texture to draw shapes
+            // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call
+            if ((fontWhiteRec.x > 0) &&
+                (fontWhiteRec.y > 0) &&
+                (fontWhiteRec.width > 0) &&
+                (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec);
+        }
+#endif
+    }
+}
+
+// Get text bounds considering control bounds
+static Rectangle GetTextBounds(int control, Rectangle bounds)
+{
+    Rectangle textBounds = bounds;
+
+    textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH);
+    textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING);
+    textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);
+    textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);    // NOTE: Text is processed line per line!
+
+    // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds
+    switch (control)
+    {
+        case COMBOBOX:
+        case DROPDOWNBOX:
+        case LISTVIEW:
+            // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW
+        case SLIDER:
+        case CHECKBOX:
+        case VALUEBOX:
+        case CONTROL11:
+            // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
+        default:
+        {
+            // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText()
+            if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING);
+            else textBounds.x += GuiGetStyle(control, TEXT_PADDING);
+        }
+        break;
+    }
+
+    return textBounds;
+}
+
+// Get text icon if provided and move text cursor
+// NOTE: We support up to 999 values for iconId
+static const char *GetTextIcon(const char *text, int *iconId)
+{
+#if !defined(RAYGUI_NO_ICONS)
+    *iconId = -1;
+    if (text[0] == '#')     // Maybe we have an icon!
+    {
+        char iconValue[4] = { 0 };  // Maximum length for icon value: 3 digits + '\0'
+
+        int pos = 1;
+        while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9'))
+        {
+            iconValue[pos - 1] = text[pos];
+            pos++;
+        }
+
+        if (text[pos] == '#')
+        {
+            *iconId = TextToInteger(iconValue);
+
+            // Move text pointer after icon
+            // WARNING: If only icon provided, it could point to EOL character: '\0'
+            if (*iconId >= 0) text += (pos + 1);
+        }
+    }
+#endif
+
+    return text;
+}
+
+// Get text divided into lines (by line-breaks '\n')
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
+{
+    #define RAYGUI_MAX_TEXT_LINES   128
+
+    static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+    for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL;    // Init NULL pointers to substrings
+
+    int textSize = (int)strlen(text);
+
+    lines[0] = text;
+    *count = 1;
+
+    for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
+    {
+        if (text[i] == '\n')
+        {
+            k++;
+            lines[k] = &text[i + 1]; // WARNING: next value is valid?
+            *count += 1;
+        }
+    }
+
+    return lines;
+}
+
+// Get text width to next space for provided string
+static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex)
+{
+    float width = 0;
+    int codepointByteCount = 0;
+    int codepoint = 0;
+    int index = 0;
+    float glyphWidth = 0;
+    float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+    for (int i = 0; text[i] != '\0'; i++)
+    {
+        if (text[i] != ' ')
+        {
+            codepoint = GetCodepoint(&text[i], &codepointByteCount);
+            index = GetGlyphIndex(guiFont, codepoint);
+            glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor;
+            width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+        }
+        else
+        {
+            *nextSpaceIndex = i;
+            break;
+        }
+    }
+
+    return width;
+}
+
+// Gui draw text using default font
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint)
+{
+    #define TEXT_VALIGN_PIXEL_OFFSET(h)  ((int)h%2)     // Vertical alignment for pixel perfect
+
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    if ((text == NULL) || (text[0] == '\0')) return;    // Security check
+
+    // PROCEDURE:
+    //   - Text is processed line per line
+    //   - For every line, horizontal alignment is defined
+    //   - For all text, vertical alignment is defined (multiline text only)
+    //   - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only)
+
+    // Get text lines (using '\n' as delimiter) to be processed individually
+    // WARNING: We can't use GuiTextSplit() function because it can be already used
+    // before the GuiDrawText() call and its buffer is static, it would be overriden :(
+    int lineCount = 0;
+    const char **lines = GetTextLines(text, &lineCount);
+
+    // Text style variables
+    //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
+    int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);    // Wrap-mode only available in read-only mode, no for text editing
+
+    // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap
+    float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2);
+    float posOffsetY = 0.0f;
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        int iconId = 0;
+        lines[i] = GetTextIcon(lines[i], &iconId);      // Check text for icon and move cursor
+
+        // Get text position depending on alignment and iconId
+        //---------------------------------------------------------------------------------
+        Vector2 textBoundsPosition = { textBounds.x, textBounds.y };
+        float textBoundsWidthOffset = 0.0f;
+
+        // NOTE: We get text size after icon has been processed
+        // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+        int textSizeX = GuiGetTextWidth(lines[i]);
+
+        // If text requires an icon, add size to measure
+        if (iconId >= 0)
+        {
+            textSizeX += RAYGUI_ICON_SIZE*guiIconScale;
+
+            // WARNING: If only icon provided, text could be pointing to EOF character: '\0'
+#if !defined(RAYGUI_NO_ICONS)
+            if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING;
+#endif
+        }
+
+        // Check guiTextAlign global variables
+        switch (alignment)
+        {
+            case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break;
+            case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x +  textBounds.width/2 - textSizeX/2; break;
+            case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break;
+            default: break;
+        }
+
+        if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x;
+
+        switch (alignmentVertical)
+        {
+            // Only valid in case of wordWrap = 0;
+            case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break;
+            case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            default: break;
+        }
+
+        // NOTE: Make sure we get pixel-perfect coordinates,
+        // In case of decimals we got weird text positioning
+        textBoundsPosition.x = (float)((int)textBoundsPosition.x);
+        textBoundsPosition.y = (float)((int)textBoundsPosition.y);
+        //---------------------------------------------------------------------------------
+
+        // Draw text (with icon if available)
+        //---------------------------------------------------------------------------------
+#if !defined(RAYGUI_NO_ICONS)
+        if (iconId >= 0)
+        {
+            // NOTE: We consider icon height, probably different than text size
+            GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint);
+            textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+            textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+        }
+#endif
+        // Get size in bytes of text,
+        // considering end of line and line break
+        int lineSize = 0;
+        for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ }
+        float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+        int lastSpaceIndex = 0;
+        bool tempWrapCharMode = false;
+
+        int textOffsetY = 0;
+        float textOffsetX = 0.0f;
+        float glyphWidth = 0;
+
+        int ellipsisWidth = GuiGetTextWidth("...");
+        bool textOverflow = false;
+        for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
+        {
+            int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
+            int index = GetGlyphIndex(guiFont, codepoint);
+
+            // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+            // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+            if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size
+
+            // Get glyph width to check if it goes out of bounds
+            if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor);
+            else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor;
+
+            // Wrap mode text measuring, to validate if
+            // it can be drawn or a new line is required
+            if (wrapMode == TEXT_WRAP_CHAR)
+            {
+                // Jump to next line if current character reach end of the box limits
+                if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+
+                    if (tempWrapCharMode)   // Wrap at char level when too long words
+                    {
+                        wrapMode = TEXT_WRAP_WORD;
+                        tempWrapCharMode = false;
+                    }
+                }
+            }
+            else if (wrapMode == TEXT_WRAP_WORD)
+            {
+                if (codepoint == 32) lastSpaceIndex = c;
+
+                // Get width to next space in line
+                int nextSpaceIndex = 0;
+                float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex);
+
+                int nextSpaceIndex2 = 0;
+                float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2);
+
+                if (nextWordSize > textBounds.width - textBoundsWidthOffset)
+                {
+                    // Considering the case the next word is longer than bounds
+                    tempWrapCharMode = true;
+                    wrapMode = TEXT_WRAP_CHAR;
+                }
+                else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+                }
+            }
+
+            if (codepoint == '\n') break;   // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint
+            else
+            {
+                // TODO: There are multiple types of spaces in Unicode,
+                // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html
+                if ((codepoint != ' ') && (codepoint != '\t'))      // Do not draw codepoints with no glyph
+                {
+                    if (wrapMode == TEXT_WRAP_NONE)
+                    {
+                        // Draw only required text glyphs fitting the textBounds.width
+                        if (textSizeX > textBounds.width)
+                        {
+                            if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth))
+                            {
+                                DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                            }
+                            else if (!textOverflow)
+                            {
+                                textOverflow = true;
+
+                                for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3)
+                                {
+                                    DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                                }
+                            }
+                        }
+                        else
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                    else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD))
+                    {
+                        // Draw only glyphs inside the bounds
+                        if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE)))
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                }
+
+                if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+        else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING));
+        //---------------------------------------------------------------------------------
+    }
+
+#if defined(RAYGUI_DEBUG_TEXT_BOUNDS)
+    GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f));
+#endif
+}
+
+// Gui draw rectangle using default raygui plain style with borders
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color)
+{
+    if (color.a > 0)
+    {
+        // Draw rectangle filled with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha));
+    }
+
+    if (borderWidth > 0)
+    {
+        // Draw rectangle border lines with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+    }
+
+#if defined(RAYGUI_DEBUG_RECS_BOUNDS)
+    DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f));
+#endif
+}
+
+// Draw tooltip using control bounds
+static void GuiTooltip(Rectangle controlRec)
+{
+    if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode)
+    {
+        Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+
+        if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
+
+        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
+
+        int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
+        int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_PADDING, 0);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
+        GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
+    }
+}
+
+// Split controls text into multiple strings
+// Also check for multiple columns (required by GuiToggleGroup())
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+    // NOTE: Those definitions could be externally provided if required
+
+    // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user
+    // textRow is an externally provided array of integers that stores row number for every splitted string
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE     1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };   // String pointers array (points to buffer data)
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };         // Buffer data (text input copy with '\0' added)
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 1;
+
+    if (textRow != NULL) textRow[0] = 0;
+
+    // Count how many substrings we have on text and point to every one
+    for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+    {
+        buffer[i] = text[i];
+        if (buffer[i] == '\0') break;
+        else if ((buffer[i] == delimiter) || (buffer[i] == '\n'))
+        {
+            result[counter] = buffer + i + 1;
+
+            if (textRow != NULL)
+            {
+                if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1;
+                else textRow[counter] = textRow[counter - 1];
+            }
+
+            buffer[i] = '\0';   // Set an end of string at this point
+
+            counter++;
+            if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+        }
+    }
+
+    *count = counter;
+
+    return result;
+}
+
+// Convert color data from RGB to HSV
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertRGBtoHSV(Vector3 rgb)
+{
+    Vector3 hsv = { 0 };
+    float min = 0.0f;
+    float max = 0.0f;
+    float delta = 0.0f;
+
+    min = (rgb.x < rgb.y)? rgb.x : rgb.y;
+    min = (min < rgb.z)? min  : rgb.z;
+
+    max = (rgb.x > rgb.y)? rgb.x : rgb.y;
+    max = (max > rgb.z)? max  : rgb.z;
+
+    hsv.z = max;            // Value
+    delta = max - min;
+
+    if (delta < 0.00001f)
+    {
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    if (max > 0.0f)
+    {
+        // NOTE: If max is 0, this divide would cause a crash
+        hsv.y = (delta/max);    // Saturation
+    }
+    else
+    {
+        // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    // NOTE: Comparing float values could not work properly
+    if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta;    // Between yellow & magenta
+    else
+    {
+        if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta;  // Between cyan & yellow
+        else hsv.x = 4.0f + (rgb.x - rgb.y)/delta;      // Between magenta & cyan
+    }
+
+    hsv.x *= 60.0f;     // Convert to degrees
+
+    if (hsv.x < 0.0f) hsv.x += 360.0f;
+
+    return hsv;
+}
+
+// Convert color data from HSV to RGB
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertHSVtoRGB(Vector3 hsv)
+{
+    Vector3 rgb = { 0 };
+    float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f;
+    long i = 0;
+
+    // NOTE: Comparing float values could not work properly
+    if (hsv.y <= 0.0f)
+    {
+        rgb.x = hsv.z;
+        rgb.y = hsv.z;
+        rgb.z = hsv.z;
+        return rgb;
+    }
+
+    hh = hsv.x;
+    if (hh >= 360.0f) hh = 0.0f;
+    hh /= 60.0f;
+
+    i = (long)hh;
+    ff = hh - i;
+    p = hsv.z*(1.0f - hsv.y);
+    q = hsv.z*(1.0f - (hsv.y*ff));
+    t = hsv.z*(1.0f - (hsv.y*(1.0f - ff)));
+
+    switch (i)
+    {
+        case 0:
+        {
+            rgb.x = hsv.z;
+            rgb.y = t;
+            rgb.z = p;
+        } break;
+        case 1:
+        {
+            rgb.x = q;
+            rgb.y = hsv.z;
+            rgb.z = p;
+        } break;
+        case 2:
+        {
+            rgb.x = p;
+            rgb.y = hsv.z;
+            rgb.z = t;
+        } break;
+        case 3:
+        {
+            rgb.x = p;
+            rgb.y = q;
+            rgb.z = hsv.z;
+        } break;
+        case 4:
+        {
+            rgb.x = t;
+            rgb.y = p;
+            rgb.z = hsv.z;
+        } break;
+        case 5:
+        default:
+        {
+            rgb.x = hsv.z;
+            rgb.y = p;
+            rgb.z = q;
+        } break;
+    }
+
+    return rgb;
+}
+
+// Scroll bar control (used by GuiScrollPanel())
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
+{
+    GuiState state = guiState;
+
+    // Is the scrollbar horizontal or vertical?
+    bool isVertical = (bounds.width > bounds.height)? false : true;
+
+    // The size (width or height depending on scrollbar type) of the spinner buttons
+    const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)?
+        (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) :
+        (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0;
+
+    // Arrow buttons [<] [>] [∧] [∨]
+    Rectangle arrowUpLeft = { 0 };
+    Rectangle arrowDownRight = { 0 };
+
+    // Actual area of the scrollbar excluding the arrow buttons
+    Rectangle scrollbar = { 0 };
+
+    // Slider bar that moves     --[///]-----
+    Rectangle slider = { 0 };
+
+    // Normalize value
+    if (value > maxValue) value = maxValue;
+    if (value < minValue) value = minValue;
+
+    int valueRange = maxValue - minValue;
+    if (valueRange <= 0) valueRange = 1;
+
+    int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+    if (sliderSize < 1) sliderSize = 1;  // TODO: Consider a minimum slider size
+
+    // Calculate rectangles for all of the components
+    arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
+        (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)spinnerSize, (float)spinnerSize };
+
+    if (isVertical)
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)),
+            bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)),
+            (float)sliderSize };
+    }
+    else    // horizontal
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)),
+            bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            (float)sliderSize,
+            bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) };
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
+                !CheckCollisionPointRec(mousePoint, arrowUpLeft) &&
+                !CheckCollisionPointRec(mousePoint, arrowDownRight))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Handle mouse wheel
+            int wheel = (int)GetMouseWheelMove();
+            if (wheel != 0) value += wheel;
+
+            // Handle mouse button down
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                // Check arrows click
+                if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // If click on scrollbar position but not on slider, place slider directly on that position
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+
+                state = STATE_PRESSED;
+            }
+
+            // Keyboard control on mouse hover scrollbar
+            /*
+            if (isVertical)
+            {
+                if (IsKeyDown(KEY_DOWN)) value += 5;
+                else if (IsKeyDown(KEY_UP)) value -= 5;
+            }
+            else
+            {
+                if (IsKeyDown(KEY_RIGHT)) value += 5;
+                else if (IsKeyDown(KEY_LEFT)) value -= 5;
+            }
+            */
+        }
+
+        // Normalize value
+        if (value > maxValue) value = maxValue;
+        if (value < minValue) value = minValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)));   // Draw the background
+
+    GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)));     // Draw the scrollbar active area background
+    GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3)));         // Draw the slider bar
+
+    // Draw arrows (using icon if available)
+    if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE))
+    {
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText(isVertical? "^" : "<",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+        GuiDrawText(isVertical? "v" : ">",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(isVertical? "#121#" : "#118#",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL
+        GuiDrawText(isVertical? "#120#" : "#119#",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    return value;
+}
+
+// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+// WARNING: It multiplies current alpha by alpha scale factor
+static Color GuiFade(Color color, float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) };
+
+    return result;
+}
+
+#if defined(RAYGUI_STANDALONE)
+// Returns a Color struct from hexadecimal value
+static Color GetColor(int hexValue)
+{
+    Color color;
+
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
+
+    return color;
+}
+
+// Returns hexadecimal value for a Color
+static int ColorToInt(Color color)
+{
+    return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
+}
+
+// Check if point is inside rectangle
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
+{
+    bool collision = false;
+
+    if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) &&
+        (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
+
+    return collision;
+}
+
+// Formatting of text with variables to 'embed'
+static const char *TextFormat(const char *text, ...)
+{
+    #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE)
+        #define RAYGUI_TEXTFORMAT_MAX_SIZE   256
+    #endif
+
+    static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE];
+
+    va_list args;
+    va_start(args, text);
+    vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
+    va_end(args);
+
+    return buffer;
+}
+
+// Draw rectangle with vertical gradient fill color
+// NOTE: This function is only used by GuiColorPicker()
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
+{
+    Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height };
+    DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+}
+
+// Split string into multiple strings
+const char **TextSplit(const char *text, char delimiter, int *count)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE      1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 0;
+
+    if (text != NULL)
+    {
+        counter = 1;
+
+        // Count how many substrings we have on text and point to every one
+        for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+        {
+            buffer[i] = text[i];
+            if (buffer[i] == '\0') break;
+            else if (buffer[i] == delimiter)
+            {
+                buffer[i] = '\0';   // Set an end of string at this point
+                result[counter] = buffer + i + 1;
+                counter++;
+
+                if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            }
+        }
+    }
+
+    *count = counter;
+    return result;
+}
+
+// Get integer value from text
+// NOTE: This function replaces atoi() [stdlib.h]
+static int TextToInteger(const char *text)
+{
+    int value = 0;
+    int sign = 1;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1;
+        text++;
+    }
+
+    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
+
+    return value*sign;
+}
+
+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+    float value = 0.0f;
+    float sign = 1.0f;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1.0f;
+        text++;
+    }
+
+    int i = 0;
+    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+    if (text[i++] != '.') value *= sign;
+    else
+    {
+        float divisor = 10.0f;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        {
+            value += ((float)(text[i] - '0'))/divisor;
+            divisor = divisor*10.0f;
+        }
+    }
+
+    return value;
+}
+
+// Encode codepoint into UTF-8 text (char array size returned as parameter)
+static const char *CodepointToUTF8(int codepoint, int *byteSize)
+{
+    static char utf8[6] = { 0 };
+    int size = 0;
+
+    if (codepoint <= 0x7f)
+    {
+        utf8[0] = (char)codepoint;
+        size = 1;
+    }
+    else if (codepoint <= 0x7ff)
+    {
+        utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
+        utf8[1] = (char)((codepoint & 0x3f) | 0x80);
+        size = 2;
+    }
+    else if (codepoint <= 0xffff)
+    {
+        utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
+        utf8[1] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[2] = (char)((codepoint & 0x3f) | 0x80);
+        size = 3;
+    }
+    else if (codepoint <= 0x10ffff)
+    {
+        utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
+        utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
+        utf8[2] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[3] = (char)((codepoint & 0x3f) | 0x80);
+        size = 4;
+    }
+
+    *byteSize = size;
+
+    return utf8;
+}
+
+// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
+// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
+// Total number of bytes processed are returned as a parameter
+// NOTE: the standard says U+FFFD should be returned in case of errors
+// but that character is not supported by the default font in raylib
+static int GetCodepointNext(const char *text, int *codepointSize)
+{
+    const char *ptr = text;
+    int codepoint = 0x3f;       // Codepoint (defaults to '?')
+    *codepointSize = 1;
+
+    // Get current codepoint and bytes processed
+    if (0xf0 == (0xf8 & ptr[0]))
+    {
+        // 4 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
+        *codepointSize = 4;
+    }
+    else if (0xe0 == (0xf0 & ptr[0]))
+    {
+        // 3 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
+        *codepointSize = 3;
+    }
+    else if (0xc0 == (0xe0 & ptr[0]))
+    {
+        // 2 byte UTF-8 codepoint
+        if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
+        *codepointSize = 2;
+    }
+    else if (0x00 == (0x80 & ptr[0]))
+    {
+        // 1 byte UTF-8 codepoint
+        codepoint = ptr[0];
+        *codepointSize = 1;
+    }
+
+    return codepoint;
+}
+#endif      // RAYGUI_STANDALONE
+
+#endif      // RAYGUI_IMPLEMENTATION
diff --git a/raylib/examples/examples_template.c b/raylib/examples/examples_template.c
--- a/raylib/examples/examples_template.c
+++ b/raylib/examples/examples_template.c
@@ -6,42 +6,43 @@
 
     1. File naming: <module>_<description> - Lower case filename, words separated by underscore,
        no more than 3-4 words in total to describe the example. <module> referes to the primary
-       raylib module the example is more related with (code, shapes, textures, models, shaders, raudio).
+       raylib module the example is more related with (code, shapes, textures, models, shaders, raudio)
        i.e: core_input_multitouch, shapes_lines_bezier, shaders_palette_switch
 
     2. Follow below template structure, example info should list the module, the short description
-       and the author of the example, twitter or github info could be also provided for the author.
-       Short description should also be used on the title of the window.
+       and the author of the example, twitter or github info could be also provided for the author
+       Short description should also be used on the title of the window
 
     3. Code should be organized by sections:[Initialization]- [Update] - [Draw] - [De-Initialization]
        Place your code between the dotted lines for every section, please don't mix update logic with drawing
-       and remember to unload all loaded resources.
+       and remember to unload all loaded resources
 
     4. Code should follow raylib conventions: https://github.com/raysan5/raylib/wiki/raylib-coding-conventions
-       Try to be very organized, using line-breaks appropiately.
+       Try to be very organized, using line-breaks appropiately
 
-    5. Add comments to the specific parts of code the example is focus on.
-       Don't abuse with comments, try to be clear and impersonal on the comments.
+    5. Add comments to the specific parts of code the example is focus on
+       Don't abuse with comments, try to be clear and impersonal on the comments
 
-    6. Try to keep the example simple, under 300 code lines if possible. Try to avoid external dependencies.
-       Try to avoid defining functions outside the main(). Example should be as self-contained as possible.
+    6. Try to keep the example simple, under 300 code lines if possible. Try to avoid external dependencies
+       Try to avoid defining functions outside the main(). Example should be as self-contained as possible
 
     7. About external resources, they should be placed in a [resources] folder and those resources
-       should be open and free for use and distribution. Avoid propietary content.
+       should be open and free for use and distribution. Avoid propietary content
 
-    8. Try to keep the example simple but with a creative touch.
+    8. Try to keep the example simple but with a creative touch
        Simple but beautiful examples are more appealing to users!
 
     9. In case of additional information is required, just come to raylib Discord channel: example-contributions
 
     10. Have fun!
 
-    The following files should be updated when adding a new example, it's planned to create some
-    script to automatize this process but not available yet.
+    The following files must be updated when adding a new example,
+    but it can be automatically done using the raylib provided tool: rexm
+    So, no worries if just the .c/.png are provided when adding the example.
 
      - raylib/examples/<category>/<category>_example_name.c
      - raylib/examples/<category>/<category>_example_name.png
-     - raylib/examples/<category>/resources/*.*
+     - raylib/examples/<category>/resources/..
      - raylib/examples/Makefile
      - raylib/examples/Makefile.Web
      - raylib/examples/README.md
@@ -56,16 +57,18 @@
 
 /*******************************************************************************************
 *
-*   raylib [core] example - Basic window
+*   raylib [<module>] example - <name/short description>
 *
-*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*   Example complexity rating: [★☆☆☆] 1/4
 *
-*   Example contributed by <user_name> (@<user_github>) and reviewed by Ramon Santamaria (@raysan5)
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
 *
+*   Example contributed by <author_name> (@<user_github>) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 <user_name> (@<user_github>)
+*   Copyright (c) <year_created>-<year_updated> <author_name> (@<user_github>)
 *
 ********************************************************************************************/
 
@@ -81,7 +84,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
+    InitWindow(screenWidth, screenHeight, "raylib [<module>] example - <name>");
 
     // TODO: Load resources / Initialize variables at this point
 
@@ -102,9 +105,11 @@
 
             ClearBackground(RAYWHITE);
 
-            // TODO: Draw everything that requires to be drawn at this point:
+            // TODO: Draw everything that requires to be drawn at this point
 
-            DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);  // Example
+            DrawLineEx((Vector2){ 0, 0 }, (Vector2){ screenWidth, screenHeight }, 2.0f, RED);
+            DrawLineEx((Vector2){ 0, screenHeight }, (Vector2){ screenWidth, 0 }, 2.0f, RED);
+            DrawText("example base code template", 260, 400, 20, LIGHTGRAY);
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/models/models_animation.c b/raylib/examples/models/models_animation.c
deleted file mode 100644
--- a/raylib/examples/models/models_animation.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Load 3d model with animations and play them
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 3.5
-*
-*   Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Culacant (@culacant) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************
-*
-*   NOTE: To export a model from blender, make sure it is not posed, the vertices need to be 
-*         in the same position as they would be in edit mode and the scale of your models is 
-*         set to 0. Scaling can be done from the export menu.
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - model animation");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera mode type
-
-    Model model = LoadModel("resources/models/iqm/guy.iqm");                    // Load the animated model mesh and basic data
-    Texture2D texture = LoadTexture("resources/models/iqm/guytex.png");         // Load model texture and set material
-    SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture);     // Set model material map texture
-
-    Vector3 position = { 0.0f, 0.0f, 0.0f };            // Set model position
-
-    // Load animation data
-    int animsCount = 0;
-    ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount);
-    int animFrameCounter = 0;
-
-    DisableCursor();                    // Catch cursor
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
-
-        // Play animation when spacebar is held down
-        if (IsKeyDown(KEY_SPACE))
-        {
-            animFrameCounter++;
-            UpdateModelAnimation(model, anims[0], animFrameCounter);
-            if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
-
-                for (int i = 0; i < model.boneCount; i++)
-                {
-                    DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED);
-                }
-
-                DrawGrid(10, 1.0f);         // Draw a grid
-
-            EndMode3D();
-
-            DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON);
-            DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texture);                     // Unload texture
-    UnloadModelAnimations(anims, animsCount);   // Unload model animations data
-    UnloadModel(model);                         // Unload model
-
-    CloseWindow();                  // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/models/models_animation_gpu_skinning.c b/raylib/examples/models/models_animation_gpu_skinning.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_animation_gpu_skinning.c
@@ -0,0 +1,121 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - animation gpu skinning
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
+*   Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2024-2025 Daniel Holden (@orangeduck)
+*
+*   Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - animation gpu skinning");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
+    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };  // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };      // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                            // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;         // Camera projection type
+
+    // Load gltf model
+    Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
+
+    // Load skinning shader
+    Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
+                                       TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
+
+    characterModel.materials[1].shader = skinningShader;
+
+    // Load gltf model animations
+    int animsCount = 0;
+    unsigned int animIndex = 0;
+    unsigned int animCurrentFrame = 0;
+    ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
+
+    Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
+
+    DisableCursor();                    // Limit cursor to relative movement inside the window
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_THIRD_PERSON);
+
+        // Select current animation
+        if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
+        else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
+
+        // Update model animation
+        ModelAnimation anim = modelAnimations[animIndex];
+        animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
+        characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
+        UpdateModelAnimationBones(characterModel, anim, animCurrentFrame);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                // Draw character mesh, pose calculation is done in shader (GPU skinning)
+                DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
+
+                DrawGrid(10, 1.0f);
+
+            EndMode3D();
+
+            DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModelAnimations(modelAnimations, animsCount); // Unload model animation
+    UnloadModel(characterModel);    // Unload model and meshes/material
+    UnloadShader(skinningShader);   // Unload GPU skinning shader
+
+    CloseWindow();                  // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_animation_playing.c b/raylib/examples/models/models_animation_playing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_animation_playing.c
@@ -0,0 +1,113 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - animation playing
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 3.5
+*
+*   Example contributed by Culacant (@culacant) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Culacant (@culacant) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************
+*
+*   NOTE: To export a model from blender, make sure it is not posed, the vertices need to be
+*         in the same position as they would be in edit mode and the scale of your models is
+*         set to 0. Scaling can be done from the export menu
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - animation playing");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera mode type
+
+    Model model = LoadModel("resources/models/iqm/guy.iqm");                    // Load the animated model mesh and basic data
+    Texture2D texture = LoadTexture("resources/models/iqm/guytex.png");         // Load model texture and set material
+    SetMaterialTexture(&model.materials[0], MATERIAL_MAP_DIFFUSE, texture);     // Set model material map texture
+
+    Vector3 position = { 0.0f, 0.0f, 0.0f };            // Set model position
+
+    // Load animation data
+    int animsCount = 0;
+    ModelAnimation *anims = LoadModelAnimations("resources/models/iqm/guyanim.iqm", &animsCount);
+    int animFrameCounter = 0;
+
+    DisableCursor();                    // Catch cursor
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+
+        // Play animation when spacebar is held down
+        if (IsKeyDown(KEY_SPACE))
+        {
+            animFrameCounter++;
+            UpdateModelAnimation(model, anims[0], animFrameCounter);
+            if (animFrameCounter >= anims[0].frameCount) animFrameCounter = 0;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                DrawModelEx(model, position, (Vector3){ 1.0f, 0.0f, 0.0f }, -90.0f, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
+
+                for (int i = 0; i < model.boneCount; i++)
+                {
+                    DrawCube(anims[0].framePoses[animFrameCounter][i].translation, 0.2f, 0.2f, 0.2f, RED);
+                }
+
+                DrawGrid(10, 1.0f);         // Draw a grid
+
+            EndMode3D();
+
+            DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, 10, 20, MAROON);
+            DrawText("(c) Guy IQM 3D model by @culacant", screenWidth - 200, screenHeight - 20, 10, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture);                     // Unload texture
+    UnloadModelAnimations(anims, animsCount);   // Unload model animations data
+    UnloadModel(model);                         // Unload model
+
+    CloseWindow();                  // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_basic_voxel.c b/raylib/examples/models/models_basic_voxel.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_basic_voxel.c
@@ -0,0 +1,152 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - basic voxel
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Tim Little (@timlittle) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Tim Little (@timlittle)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+
+#define WORLD_SIZE 8   // Size of our voxel world (8x8x8 cubes)
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel");
+
+    DisableCursor();                    // Lock mouse to window center
+
+    // Define the camera to look into our 3d world (first person)
+    Camera3D camera = { 0 };
+    camera.position = (Vector3){ -2.0f, 0.0f, -2.0f };  // Camera position at ground level
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector
+    camera.fovy = 45.0f;                                 // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;              // Camera projection type
+
+    // Create a cube model
+    Mesh cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f);      // Create a unit cube mesh
+    Model cubeModel = LoadModelFromMesh(cubeMesh);       // Convert mesh to a model
+    cubeModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = BEIGE;
+
+    // Initialize voxel world - fill with voxels
+    bool voxels[WORLD_SIZE][WORLD_SIZE][WORLD_SIZE] = { false };
+    for (int x = 0; x < WORLD_SIZE; x++)
+    {
+        for (int y = 0; y < WORLD_SIZE; y++)
+        {
+            for (int z = 0; z < WORLD_SIZE; z++)
+            {
+                voxels[x][y][z] = true;
+            }
+        }
+    }
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+
+        // Handle voxel removal with mouse click
+        if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+        {
+            // Cast a ray from the screen center (where crosshair would be)
+            Vector2 screenCenter = { screenWidth/2.0f, screenHeight/2.0f };
+            Ray ray = GetMouseRay(screenCenter, camera);
+
+            // Check ray collision with all voxels
+            bool voxelRemoved = false;
+            for (int x = 0; (x < WORLD_SIZE) && !voxelRemoved; x++)
+            {
+                for (int y = 0; (y < WORLD_SIZE) && !voxelRemoved; y++)
+                {
+                    for (int z = 0; (z < WORLD_SIZE) && !voxelRemoved; z++)
+                    {
+                        if (!voxels[x][y][z]) continue; // Skip empty voxels
+
+                        // Build a bounding box for this voxel
+                        Vector3 position = { (float)x, (float)y, (float)z };
+                        BoundingBox box = {
+                            (Vector3){ position.x - 0.5f, position.y - 0.5f, position.z - 0.5f },
+                            (Vector3){ position.x + 0.5f, position.y + 0.5f, position.z + 0.5f }
+                        };
+
+                        // Check ray-box collision
+                        RayCollision collision = GetRayCollisionBox(ray, box);
+                        if (collision.hit)
+                        {
+                            voxels[x][y][z] = false;    // Remove this voxel
+                            voxelRemoved = true;        // Exit all loops
+                        }
+                    }
+                }
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                DrawGrid(10, 1.0f);
+
+                // Draw all voxels
+                for (int x = 0; x < WORLD_SIZE; x++)
+                {
+                    for (int y = 0; y < WORLD_SIZE; y++)
+                    {
+                        for (int z = 0; z < WORLD_SIZE; z++)
+                        {
+                            if (!voxels[x][y][z]) continue;
+
+                            Vector3 position = { (float)x, (float)y, (float)z };
+                            DrawModel(cubeModel, position, 1.0f, BEIGE);
+                            DrawCubeWires(position, 1.0f, 1.0f, 1.0f, BLACK);
+                        }
+                    }
+                }
+
+            EndMode3D();
+
+            DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY);
+            DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModel(cubeModel);
+    CloseWindow();
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_billboard.c b/raylib/examples/models/models_billboard.c
deleted file mode 100644
--- a/raylib/examples/models/models_billboard.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Drawing billboards
-*
-*   Example originally created with raylib 1.3, last time updated with raylib 3.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-#include "raymath.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - drawing billboards");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 5.0f, 4.0f, 5.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    Texture2D bill = LoadTexture("resources/billboard.png");    // Our billboard texture
-    Vector3 billPositionStatic = { 0.0f, 2.0f, 0.0f };          // Position of static billboard
-    Vector3 billPositionRotating = { 1.0f, 2.0f, 1.0f };        // Position of rotating billboard
-
-    // Entire billboard texture, source is used to take a segment from a larger texture.
-    Rectangle source = { 0.0f, 0.0f, (float)bill.width, (float)bill.height };
-
-    // NOTE: Billboard locked on axis-Y
-    Vector3 billUp = { 0.0f, 1.0f, 0.0f };
-
-    // Set the height of the rotating billboard to 1.0 with the aspect ratio fixed
-    Vector2 size = { source.width/source.height, 1.0f };
-
-    // Rotate around origin
-    // Here we choose to rotate around the image center
-    Vector2 origin = Vector2Scale(size, 0.5f);
-
-    // Distance is needed for the correct billboard draw order
-    // Larger distance (further away from the camera) should be drawn prior to smaller distance.
-    float distanceStatic;
-    float distanceRotating;
-    float rotation = 0.0f;
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-
-        rotation += 0.4f;
-        distanceStatic = Vector3Distance(camera.position, billPositionStatic);
-        distanceRotating = Vector3Distance(camera.position, billPositionRotating);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                DrawGrid(10, 1.0f);        // Draw a grid
-
-                // Draw order matters!
-                if (distanceStatic > distanceRotating) 
-                {
-                    DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
-                    DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
-                } 
-                else
-                {
-                    DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
-                    DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
-                }
-                
-            EndMode3D();
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(bill);        // Unload texture
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/models/models_billboard_rendering.c b/raylib/examples/models/models_billboard_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_billboard_rendering.c
@@ -0,0 +1,115 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - billboard rendering
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 1.3, last time updated with raylib 3.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - billboard rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 5.0f, 4.0f, 5.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    Texture2D bill = LoadTexture("resources/billboard.png");    // Our billboard texture
+    Vector3 billPositionStatic = { 0.0f, 2.0f, 0.0f };          // Position of static billboard
+    Vector3 billPositionRotating = { 1.0f, 2.0f, 1.0f };        // Position of rotating billboard
+
+    // Entire billboard texture, source is used to take a segment from a larger texture
+    Rectangle source = { 0.0f, 0.0f, (float)bill.width, (float)bill.height };
+
+    // NOTE: Billboard locked on axis-Y
+    Vector3 billUp = { 0.0f, 1.0f, 0.0f };
+
+    // Set the height of the rotating billboard to 1.0 with the aspect ratio fixed
+    Vector2 size = { source.width/source.height, 1.0f };
+
+    // Rotate around origin
+    // Here we choose to rotate around the image center
+    Vector2 origin = Vector2Scale(size, 0.5f);
+
+    // Distance is needed for the correct billboard draw order
+    // Larger distance (further away from the camera) should be drawn prior to smaller distance
+    float distanceStatic;
+    float distanceRotating;
+    float rotation = 0.0f;
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        rotation += 0.4f;
+        distanceStatic = Vector3Distance(camera.position, billPositionStatic);
+        distanceRotating = Vector3Distance(camera.position, billPositionRotating);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                DrawGrid(10, 1.0f);        // Draw a grid
+
+                // Draw order matters!
+                if (distanceStatic > distanceRotating)
+                {
+                    DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
+                    DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
+                }
+                else
+                {
+                    DrawBillboardPro(camera, bill, source, billPositionRotating, billUp, size, origin, rotation, WHITE);
+                    DrawBillboard(camera, bill, billPositionStatic, 2.0f, WHITE);
+                }
+
+            EndMode3D();
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(bill);        // Unload texture
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_bone_socket.c b/raylib/examples/models/models_bone_socket.c
--- a/raylib/examples/models/models_bone_socket.c
+++ b/raylib/examples/models/models_bone_socket.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [core] example - Using bones as socket for calculating the positioning of something
-* 
+*   raylib [models] example - bone socket
+*
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 4.5, last time updated with raylib 4.5
 *
 *   Example contributed by iP (@ipzaur) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2024 iP (@ipzaur)
+*   Copyright (c) 2024-2025 iP (@ipzaur)
 *
 ********************************************************************************************/
 
@@ -49,7 +51,7 @@
         LoadModel("resources/models/gltf/greenman_sword.glb"),  // Index for the sword model is the same as BONE_SOCKET_HAND_R
         LoadModel("resources/models/gltf/greenman_shield.glb")  // Index for the shield model is the same as BONE_SOCKET_HAND_L
     };
-    
+
     bool showEquip[3] = { true, true, true };   // Toggle on/off equip
 
     // Load gltf model animations
@@ -61,7 +63,7 @@
     // indices of bones for sockets
     int boneSocketIndex[BONE_SOCKETS] = { -1, -1, -1 };
 
-    // search bones for sockets 
+    // search bones for sockets
     for (int i = 0; i < characterModel.boneCount; i++)
     {
         if (TextIsEqual(characterModel.bones[i].name, "socket_hat"))
@@ -69,13 +71,13 @@
             boneSocketIndex[BONE_SOCKET_HAT] = i;
             continue;
         }
-        
+
         if (TextIsEqual(characterModel.bones[i].name, "socket_hand_R"))
         {
             boneSocketIndex[BONE_SOCKET_HAND_R] = i;
             continue;
         }
-        
+
         if (TextIsEqual(characterModel.bones[i].name, "socket_hand_L"))
         {
             boneSocketIndex[BONE_SOCKET_HAND_L] = i;
@@ -97,7 +99,7 @@
         // Update
         //----------------------------------------------------------------------------------
         UpdateCamera(&camera, CAMERA_THIRD_PERSON);
-        
+
         // Rotate character
         if (IsKeyDown(KEY_F)) angle = (angle + 1)%360;
         else if (IsKeyDown(KEY_H)) angle = (360 + angle - 1)%360;
@@ -110,7 +112,7 @@
         if (IsKeyPressed(KEY_ONE)) showEquip[BONE_SOCKET_HAT] = !showEquip[BONE_SOCKET_HAT];
         if (IsKeyPressed(KEY_TWO)) showEquip[BONE_SOCKET_HAND_R] = !showEquip[BONE_SOCKET_HAND_R];
         if (IsKeyPressed(KEY_THREE)) showEquip[BONE_SOCKET_HAND_L] = !showEquip[BONE_SOCKET_HAND_L];
-        
+
         // Update model animation
         ModelAnimation anim = modelAnimations[animIndex];
         animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
@@ -138,7 +140,7 @@
                     Transform *transform = &anim.framePoses[animCurrentFrame][boneSocketIndex[i]];
                     Quaternion inRotation = characterModel.bindPose[boneSocketIndex[i]].rotation;
                     Quaternion outRotation = transform->rotation;
-                    
+
                     // Calculate socket rotation (angle between bone in initial pose and same bone in current animation frame)
                     Quaternion rotate = QuaternionMultiply(outRotation, QuaternionInvert(inRotation));
                     Matrix matrixTransform = QuaternionToMatrix(rotate);
@@ -146,7 +148,7 @@
                     matrixTransform = MatrixMultiply(matrixTransform, MatrixTranslate(transform->translation.x, transform->translation.y, transform->translation.z));
                     // Transform the socket using the transform of the character (angle and translate)
                     matrixTransform = MatrixMultiply(matrixTransform, characterModel.transform);
-                    
+
                     // Draw mesh at socket position with socket angle rotation
                     DrawMesh(equipModel[i].meshes[0], equipModel[i].materials[1], matrixTransform);
                 }
@@ -166,7 +168,7 @@
     //--------------------------------------------------------------------------------------
     UnloadModelAnimations(modelAnimations, animsCount);
     UnloadModel(characterModel);         // Unload character model and meshes/material
-    
+
     // Unload equipment model and meshes/material
     for (int i = 0; i < BONE_SOCKETS; i++) UnloadModel(equipModel[i]);
 
diff --git a/raylib/examples/models/models_box_collisions.c b/raylib/examples/models/models_box_collisions.c
--- a/raylib/examples/models/models_box_collisions.c
+++ b/raylib/examples/models/models_box_collisions.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Detect basic 3d collisions (box vs sphere vs box)
+*   raylib [models] example - box collisions
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/models/models_cubicmap.c b/raylib/examples/models/models_cubicmap.c
deleted file mode 100644
--- a/raylib/examples/models/models_cubicmap.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Cubicmap loading and drawing
-*
-*   Example originally created with raylib 1.8, last time updated with raylib 3.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - cubesmap loading and drawing");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 16.0f, 14.0f, 16.0f };     // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                    // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
-
-    Image image = LoadImage("resources/cubicmap.png");      // Load cubicmap image (RAM)
-    Texture2D cubicmap = LoadTextureFromImage(image);       // Convert image to texture to display (VRAM)
-
-    Mesh mesh = GenMeshCubicmap(image, (Vector3){ 1.0f, 1.0f, 1.0f });
-    Model model = LoadModelFromMesh(mesh);
-
-    // NOTE: By default each cube is mapped to one part of texture atlas
-    Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");    // Load map texture
-    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;    // Set map diffuse texture
-
-    Vector3 mapPosition = { -16.0f, 0.0f, -8.0f };          // Set model position
-
-    UnloadImage(image);     // Unload cubesmap image from RAM, already uploaded to VRAM
-
-    bool pause = false;     // Pause camera orbital rotation (and zoom)
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        if (IsKeyPressed(KEY_P)) pause = !pause;
-
-        if (!pause) UpdateCamera(&camera, CAMERA_ORBITAL);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                DrawModel(model, mapPosition, 1.0f, WHITE);
-
-            EndMode3D();
-
-            DrawTextureEx(cubicmap, (Vector2){ screenWidth - cubicmap.width*4.0f - 20, 20.0f }, 0.0f, 4.0f, WHITE);
-            DrawRectangleLines(screenWidth - cubicmap.width*4 - 20, 20, cubicmap.width*4, cubicmap.height*4, GREEN);
-
-            DrawText("cubicmap image used to", 658, 90, 10, GRAY);
-            DrawText("generate map 3d model", 658, 104, 10, GRAY);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(cubicmap);    // Unload cubicmap texture
-    UnloadTexture(texture);     // Unload map texture
-    UnloadModel(model);         // Unload map model
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/models/models_cubicmap_rendering.c b/raylib/examples/models/models_cubicmap_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_cubicmap_rendering.c
@@ -0,0 +1,101 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - cubicmap rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 1.8, last time updated with raylib 3.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - cubicmap rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 16.0f, 14.0f, 16.0f };     // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                    // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
+
+    Image image = LoadImage("resources/cubicmap.png");      // Load cubicmap image (RAM)
+    Texture2D cubicmap = LoadTextureFromImage(image);       // Convert image to texture to display (VRAM)
+
+    Mesh mesh = GenMeshCubicmap(image, (Vector3){ 1.0f, 1.0f, 1.0f });
+    Model model = LoadModelFromMesh(mesh);
+
+    // NOTE: By default each cube is mapped to one part of texture atlas
+    Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");    // Load map texture
+    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;    // Set map diffuse texture
+
+    Vector3 mapPosition = { -16.0f, 0.0f, -8.0f };          // Set model position
+
+    UnloadImage(image);     // Unload cubesmap image from RAM, already uploaded to VRAM
+
+    bool pause = false;     // Pause camera orbital rotation (and zoom)
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_P)) pause = !pause;
+
+        if (!pause) UpdateCamera(&camera, CAMERA_ORBITAL);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                DrawModel(model, mapPosition, 1.0f, WHITE);
+
+            EndMode3D();
+
+            DrawTextureEx(cubicmap, (Vector2){ screenWidth - cubicmap.width*4.0f - 20, 20.0f }, 0.0f, 4.0f, WHITE);
+            DrawRectangleLines(screenWidth - cubicmap.width*4 - 20, 20, cubicmap.width*4, cubicmap.height*4, GREEN);
+
+            DrawText("cubicmap image used to", 658, 90, 10, GRAY);
+            DrawText("generate map 3d model", 658, 104, 10, GRAY);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(cubicmap);    // Unload cubicmap texture
+    UnloadTexture(texture);     // Unload map texture
+    UnloadModel(model);         // Unload map model
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_decals.c b/raylib/examples/models/models_decals.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_decals.c
@@ -0,0 +1,605 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - decals
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
+*   Based on previous work by @mrdoob
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 JP Mortiboys (@themushroompirates) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#include <string.h>         // Required for: memcpy()
+
+#undef FLT_MAX
+#define FLT_MAX     340282346638528859811704183484516925440.0f     // Maximum value of a float, from bit pattern 01111111011111111111111111111111
+
+#define MAX_DECALS 256
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct MeshBuilder {
+    int vertexCount;
+    int vertexCapacity;
+    Vector3 *vertices;
+    Vector2 *uvs;
+} MeshBuilder;
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3]);
+static void FreeMeshBuilder(MeshBuilder *mb);
+static Mesh BuildMesh(MeshBuilder *mb);
+static Mesh GenMeshDecal(Model inputModel, Matrix projection, float decalSize, float decalOffset);
+static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s);
+static void FreeDecalMeshData(void) { GenMeshDecal((Model){ .meshCount = -1 }, (Matrix){ 0 }, 0.0f, 0.0f); }
+static bool GuiButton(Rectangle rec, const char *label);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - decals");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
+    camera.target = (Vector3){ 0.0f, 1.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.6f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    // Load character model
+    Model model = LoadModel("resources/models/obj/character.obj");
+
+    // Apply character skin
+    Texture2D modelTexture = LoadTexture("resources/models/obj/character_diffuse.png");
+    SetTextureFilter(modelTexture, TEXTURE_FILTER_BILINEAR);
+    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = modelTexture;
+
+    BoundingBox modelBBox = GetMeshBoundingBox(model.meshes[0]);    // Get mesh bounding box
+
+    camera.target  = Vector3Lerp(modelBBox.min, modelBBox.max, 0.5f);
+    camera.position = Vector3Scale(modelBBox.max, 1.0f);
+    camera.position.x *= 0.1f;
+
+    float modelSize = fminf(
+        fminf(fabsf(modelBBox.max.x - modelBBox.min.x), fabsf(modelBBox.max.y - modelBBox.min.y)),
+        fabsf(modelBBox.max.z - modelBBox.min.z));
+
+    camera.position = (Vector3){ 0.0f, modelBBox.max.y*1.2f, modelSize*3.0f };
+
+    float decalSize = modelSize*0.25f;
+    float decalOffset = 0.01f;
+
+    Model placementCube = LoadModelFromMesh(GenMeshCube(decalSize, decalSize, decalSize));
+    placementCube.materials[0].maps[0].color = LIME;
+
+    Material decalMaterial = LoadMaterialDefault();
+    decalMaterial.maps[0].color = YELLOW;
+
+    Image decalImage = LoadImage("resources/raylib_logo.png");
+    ImageResizeNN(&decalImage, decalImage.width/4, decalImage.height/4);
+    Texture decalTexture = LoadTextureFromImage(decalImage);
+    UnloadImage(decalImage);
+
+    SetTextureFilter(decalTexture, TEXTURE_FILTER_BILINEAR);
+    decalMaterial.maps[MATERIAL_MAP_DIFFUSE].texture = decalTexture;
+    decalMaterial.maps[MATERIAL_MAP_DIFFUSE].color = RAYWHITE;
+
+    bool showModel = true;
+    Model decalModels[MAX_DECALS] = { 0 };
+    int decalCount = 0;
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) UpdateCamera(&camera, CAMERA_THIRD_PERSON);
+
+        // Display information about closest hit
+        RayCollision collision = { 0 };
+        collision.distance = FLT_MAX;
+        collision.hit = false;
+
+        // Get mouse ray
+        Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
+
+        // Check ray collision against bounding box first, before trying the full ray-mesh test
+        RayCollision boxHitInfo = GetRayCollisionBox(ray, modelBBox);
+
+        if ((boxHitInfo.hit) && (decalCount < MAX_DECALS))
+        {
+            // Check ray collision against model meshes
+            RayCollision meshHitInfo = { 0 };
+            for (int m = 0; m < model.meshCount; m++)
+            {
+                // NOTE: We consider the model.transform for the collision check but
+                // it can be checked against any transform Matrix, used when checking against same
+                // model drawn multiple times with multiple transforms
+                meshHitInfo = GetRayCollisionMesh(ray, model.meshes[m], model.transform);
+                if (meshHitInfo.hit)
+                {
+                    // Save the closest hit mesh
+                    if (!collision.hit || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo;
+                }
+            }
+
+            if (meshHitInfo.hit) collision = meshHitInfo;
+        }
+
+        // Add decal to mesh on hit point
+        if (collision.hit && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (decalCount < MAX_DECALS))
+        {
+            // Create the transformation to project the decal
+            Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
+            Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){ 0.0f, 1.0f, 0.0f });
+
+            // Spin the placement around a bit
+            splat = MatrixMultiply(splat, MatrixRotateZ(DEG2RAD*((float)GetRandomValue(-180, 180))));
+
+            Mesh decalMesh = GenMeshDecal(model, splat, decalSize, decalOffset);
+
+            if (decalMesh.vertexCount > 0)
+            {
+                int decalIndex = decalCount++;
+                decalModels[decalIndex] = LoadModelFromMesh(decalMesh);
+                decalModels[decalIndex].materials[0].maps[0] = decalMaterial.maps[0];
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                // Draw the model at the origin and default scale
+                if (showModel) DrawModel(model, (Vector3){0.0f, 0.0f, 0.0f}, 1.0f, WHITE);
+
+                // Draw the decal models
+                for (int i = 0; i < decalCount; i++) DrawModel(decalModels[i], (Vector3){0}, 1.0f, WHITE);
+
+                // If we hit the mesh, draw the box for the decal
+                if (collision.hit)
+                {
+                    Vector3 origin = Vector3Add(collision.point, Vector3Scale(collision.normal, 1.0f));
+                    Matrix splat = MatrixLookAt(collision.point, origin, (Vector3){0,1,0});
+                    placementCube.transform = MatrixInvert(splat);
+                    DrawModel(placementCube, (Vector3){0}, 1.0f, Fade(WHITE, 0.5f));
+                }
+
+                DrawGrid(10, 10.0f);
+            EndMode3D();
+
+            float yPos = 10;
+            float x0 = GetScreenWidth() - 300.0f;
+            float x1 = x0 + 100;
+            float x2 = x1 + 100;
+
+            DrawText("Vertices", (int)x1, (int)yPos, 10, LIME);
+            DrawText("Triangles", (int)x2, (int)yPos, 10, LIME);
+            yPos += 15;
+
+            int vertexCount = 0;
+            int triangleCount = 0;
+
+            for (int i = 0; i < model.meshCount; i++)
+            {
+                vertexCount += model.meshes[i].vertexCount;
+                triangleCount += model.meshes[i].triangleCount;
+            }
+
+            DrawText("Main model", (int)x0, (int)yPos, 10, LIME);
+            DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME);
+            DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME);
+            yPos += 15;
+
+            for (int i = 0; i < decalCount; i++)
+            {
+                if (i == 20)
+                {
+                    DrawText("...", (int)x0, (int)yPos, 10, LIME);
+                    yPos += 15;
+                }
+
+                if (i < 20)
+                {
+                    DrawText(TextFormat("Decal #%d", i+1), (int)x0, (int)yPos, 10, LIME);
+                    DrawText(TextFormat("%d", decalModels[i].meshes[0].vertexCount), (int)x1, (int)yPos, 10, LIME);
+                    DrawText(TextFormat("%d", decalModels[i].meshes[0].triangleCount), (int)x2, (int)yPos, 10, LIME);
+                    yPos += 15;
+                }
+
+                vertexCount += decalModels[i].meshes[0].vertexCount;
+                triangleCount += decalModels[i].meshes[0].triangleCount;
+            }
+
+            DrawText("TOTAL", (int)x0, (int)yPos, 10, LIME);
+            DrawText(TextFormat("%d", vertexCount), (int)x1, (int)yPos, 10, LIME);
+            DrawText(TextFormat("%d", triangleCount), (int)x2, (int)yPos, 10, LIME);
+            yPos += 15;
+
+            DrawText("Hold RMB to move camera", 10, 430, 10, GRAY);
+            DrawText("(c) Character model and texture from kenney.nl", screenWidth - 260, screenHeight - 20, 10, GRAY);
+
+            // UI elements
+            if (GuiButton((Rectangle){ 10, screenHeight - 1000.f, 100, 60 }, showModel ? "Hide Model" : "Show Model")) showModel = !showModel;
+
+            if (GuiButton((Rectangle){ 10 + 110, screenHeight - 100.0f, 100, 60 }, "Clear Decals"))
+            {
+                // Clear decals, unload all decal models
+                for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
+                decalCount = 0;
+            }
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModel(model);
+    UnloadTexture(modelTexture);
+
+    // Unload decal models
+    for (int i = 0; i < decalCount; i++) UnloadModel(decalModels[i]);
+
+    UnloadTexture(decalTexture);
+
+    FreeDecalMeshData();        // Free the data for decal generation
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Add triangles to mesh builder (dynamic array manager)
+static void AddTriangleToMeshBuilder(MeshBuilder *mb, Vector3 vertices[3])
+{
+    // Reallocate and copy if we need to
+    if (mb->vertexCapacity <= (mb->vertexCount + 3))
+    {
+        int newVertexCapacity = (1 + (mb->vertexCapacity/256))*256;
+        Vector3 *newVertices = (Vector3 *)MemAlloc(newVertexCapacity*sizeof(Vector3));
+
+        if (mb->vertexCapacity > 0)
+        {
+            memcpy(newVertices, mb->vertices, mb->vertexCount*sizeof(Vector3));
+            MemFree(mb->vertices);
+        }
+
+        mb->vertices = newVertices;
+        mb->vertexCapacity = newVertexCapacity;
+    }
+
+    // Add 3 vertices
+    int index = mb->vertexCount;
+    mb->vertexCount += 3;
+
+    for (int i = 0; i < 3; i++) mb->vertices[index+i] = vertices[i];
+}
+
+// Free mesh builder
+static void FreeMeshBuilder(MeshBuilder *mb)
+{
+    MemFree(mb->vertices);
+    if (mb->uvs) MemFree(mb->uvs);
+    *mb = (MeshBuilder){ 0 };
+}
+
+// Build a Mesh from MeshBuilder data
+static Mesh BuildMesh(MeshBuilder *mb)
+{
+    Mesh outMesh = { 0 };
+
+    outMesh.vertexCount = mb->vertexCount;
+    outMesh.triangleCount = mb->vertexCount/3;
+    outMesh.vertices = MemAlloc(outMesh.vertexCount*3*sizeof(float));
+    if (mb->uvs) outMesh.texcoords = MemAlloc(outMesh.vertexCount*2*sizeof(float));
+
+    for (int i = 0; i < mb->vertexCount; i++)
+    {
+        outMesh.vertices[3*i+0] = mb->vertices[i].x;
+        outMesh.vertices[3*i+1] = mb->vertices[i].y;
+        outMesh.vertices[3*i+2] = mb->vertices[i].z;
+
+        if (mb->uvs)
+        {
+            outMesh.texcoords[2*i+0] = mb->uvs[i].x;
+            outMesh.texcoords[2*i+1] = mb->uvs[i].y;
+        }
+    }
+
+    UploadMesh(&outMesh, false);
+
+    return outMesh;
+}
+
+// Clip segment
+static Vector3 ClipSegment(Vector3 v0, Vector3 v1, Vector3 p, float s)
+{
+    float d0 = Vector3DotProduct(v0, p) - s;
+    float d1 = Vector3DotProduct(v1, p) - s;
+    float s0 = d0/(d0 - d1);
+
+    Vector3 position = Vector3Lerp(v0, v1, s0);
+
+    return position;
+}
+
+// Generate mesh decals for provided model
+static Mesh GenMeshDecal(Model target, Matrix projection, float decalSize, float decalOffset)
+{
+    // We're going to use these to build up our decal meshes
+    // They'll resize automatically as we go, we'll free them at the end
+    static MeshBuilder meshBuilders[2] = { 0 };
+
+    // Ugly way of telling us to free the static MeshBuilder data
+    if (target.meshCount == -1)
+    {
+        FreeMeshBuilder(&meshBuilders[0]);
+        FreeMeshBuilder(&meshBuilders[1]);
+        return (Mesh){ 0 };
+    }
+
+    // We're going to need the inverse matrix
+    Matrix invProj = MatrixInvert(projection);
+
+    // Reset the mesh builders
+    meshBuilders[0].vertexCount = 0;
+    meshBuilders[1].vertexCount = 0;
+
+    // We'll be flip-flopping between the two mesh builders
+    // Reading from one and writing to the other, then swapping
+    int mbIndex = 0;
+
+    // First pass, just get any triangle inside the bounding box (for each mesh of the model)
+    for (int meshIndex = 0; meshIndex < target.meshCount; meshIndex++)
+    {
+        Mesh mesh = target.meshes[meshIndex];
+        for (int tri = 0; tri < mesh.triangleCount; tri++)
+        {
+            Vector3 vertices[3] = { 0 };
+
+            // The way we calculate the vertices of the mesh triangle
+            // depend on whether the mesh vertices are indexed or not
+            if (mesh.indices == 0)
+            {
+                for (int v = 0; v < 3; v++)
+                {
+                    vertices[v] = (Vector3){
+                        mesh.vertices[3*3*tri + 3*v + 0],
+                        mesh.vertices[3*3*tri + 3*v + 1],
+                        mesh.vertices[3*3*tri + 3*v + 2]
+                    };
+                }
+            }
+            else
+            {
+                for (int v = 0; v < 3; v++)
+                {
+                    vertices[v] = (Vector3){
+                        mesh.vertices[ 3*mesh.indices[3*tri+0] + v],
+                        mesh.vertices[ 3*mesh.indices[3*tri+1] + v],
+                        mesh.vertices[ 3*mesh.indices[3*tri+2] + v]
+                    };
+                }
+            }
+
+            // Transform all 3 vertices of the triangle
+            // and check if they are inside our decal box
+            int insideCount = 0;
+            for (int i = 0; i < 3; i++)
+            {
+                // To projection space
+                Vector3 v = Vector3Transform(vertices[i], projection);
+
+                if ((fabsf(v.x) < decalSize) || (fabsf(v.y) <= decalSize) || (fabsf(v.z) <= decalSize)) insideCount++;
+
+                // We need to keep the transformed vertex
+                vertices[i] = v;
+            }
+
+            // If any of them are inside, we add the triangle - we'll clip it later
+            if (insideCount > 0) AddTriangleToMeshBuilder(&meshBuilders[mbIndex], vertices);
+        }
+    }
+
+    // Clipping time! We need to clip against all 6 directions
+    Vector3 planes[6] = {
+       {  1,  0,  0 },
+       { -1,  0,  0 },
+       {  0,  1,  0 },
+       {  0, -1,  0 },
+       {  0,  0,  1 },
+       {  0,  0, -1 }
+    };
+
+    for (int face = 0; face < 6; face++)
+    {
+        // Swap current model builder (so we read from the one we just wrote to)
+        mbIndex = 1 - mbIndex;
+
+        MeshBuilder *inMesh = &meshBuilders[1 - mbIndex];
+        MeshBuilder *outMesh = &meshBuilders[mbIndex];
+
+        // Reset write builder
+        outMesh->vertexCount = 0;
+
+        float s = 0.5f*decalSize;
+
+        for (int i = 0; i < inMesh->vertexCount; i += 3)
+        {
+            Vector3 nV1, nV2, nV3, nV4;
+
+            float d1 = Vector3DotProduct(inMesh->vertices[ i + 0 ], planes[face] ) - s;
+            float d2 = Vector3DotProduct(inMesh->vertices[ i + 1 ], planes[face] ) - s;
+            float d3 = Vector3DotProduct(inMesh->vertices[ i + 2 ], planes[face] ) - s;
+
+            int v1Out = (d1 > 0);
+            int v2Out = (d2 > 0);
+            int v3Out = (d3 > 0);
+
+            // Calculate, how many vertices of the face lie outside of the clipping plane
+            int total = v1Out + v2Out + v3Out;
+
+            switch (total)
+            {
+                case 0:
+                {
+                    // The entire face lies inside of the plane, no clipping needed
+                    AddTriangleToMeshBuilder(outMesh, (Vector3[3]){inMesh->vertices[i], inMesh->vertices[i+1], inMesh->vertices[i+2]});
+                } break;
+                case 1:
+                {
+                    // One vertex lies outside of the plane, perform clipping
+                    if (v1Out)
+                    {
+                        nV1 = inMesh->vertices[i + 1];
+                        nV2 = inMesh->vertices[i + 2];
+                        nV3 = ClipSegment(inMesh->vertices[i], nV1, planes[face], s);
+                        nV4 = ClipSegment(inMesh->vertices[i], nV2, planes[face], s);
+                    }
+
+                    if (v2Out)
+                    {
+                        nV1 = inMesh->vertices[i];
+                        nV2 = inMesh->vertices[i + 2];
+                        nV3 = ClipSegment(inMesh->vertices[i + 1], nV1, planes[face], s);
+                        nV4 = ClipSegment(inMesh->vertices[i + 1], nV2, planes[face], s);
+
+                        AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV3, nV2, nV1});
+                        AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV2, nV3, nV4});
+                        break;
+                    }
+
+                    if (v3Out)
+                    {
+                        nV1 = inMesh->vertices[i];
+                        nV2 = inMesh->vertices[i + 1];
+                        nV3 = ClipSegment(inMesh->vertices[i + 2], nV1, planes[face], s);
+                        nV4 = ClipSegment(inMesh->vertices[i + 2], nV2, planes[face], s);
+                    }
+
+                    AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
+                    AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV4, nV3, nV2});
+                } break;
+                case 2:
+                {
+                    // Two vertices lies outside of the plane, perform clipping
+                    if (!v1Out)
+                    {
+                        nV1 = inMesh->vertices[i];
+                        nV2 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
+                        nV3 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
+                        AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
+                    }
+
+                    if (!v2Out)
+                    {
+                        nV1 = inMesh->vertices[i + 1];
+                        nV2 = ClipSegment(nV1, inMesh->vertices[i + 2], planes[face], s);
+                        nV3 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
+                        AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
+                    }
+
+                    if (!v3Out)
+                    {
+                        nV1 = inMesh->vertices[i + 2];
+                        nV2 = ClipSegment(nV1, inMesh->vertices[i], planes[face], s);
+                        nV3 = ClipSegment(nV1, inMesh->vertices[i + 1], planes[face], s);
+                        AddTriangleToMeshBuilder(outMesh, (Vector3[3]){nV1, nV2, nV3});
+                    }
+                } break;
+                case 3: // The entire face lies outside of the plane, so let's discard the corresponding vertices
+                default: break;
+            }
+        }
+    }
+
+    // Now we just need to re-transform the vertices
+    MeshBuilder *theMesh = &meshBuilders[mbIndex];
+
+    // Allocate room for UVs
+    if (theMesh->vertexCount > 0)
+    {
+        theMesh->uvs = (Vector2 *)MemAlloc(sizeof(Vector2)*theMesh->vertexCount);
+
+        for (int i = 0; i < theMesh->vertexCount; i++)
+        {
+            // Calculate the UVs based on the projected coords
+            // They are clipped to (-decalSize .. decalSize) and we want them (0..1)
+            theMesh->uvs[i].x = (theMesh->vertices[i].x/decalSize + 0.5f);
+            theMesh->uvs[i].y = (theMesh->vertices[i].y/decalSize + 0.5f);
+
+            // Tiny nudge in the normal direction so it renders properly over the mesh
+            theMesh->vertices[i].z -= decalOffset;
+
+            // From projection space to world space
+            theMesh->vertices[i] = Vector3Transform(theMesh->vertices[i], invProj);
+        }
+
+        // Decal model data ready, create the mesh and return it
+        return BuildMesh(theMesh);
+    }
+    else
+    {
+        // Return a blank mesh as there's nothing to add
+        return (Mesh){ 0 };
+    }
+}
+
+// Button UI element
+static bool GuiButton(Rectangle rec, const char *label)
+{
+    Color bgColor = GRAY;
+    bool pressed = false;
+
+    if (CheckCollisionPointRec(GetMousePosition(), rec))
+    {
+        bgColor = LIGHTGRAY;
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) pressed = true;
+    }
+
+    DrawRectangleRec(rec, bgColor);
+    DrawRectangleLinesEx(rec, 2.0f, DARKGRAY);
+
+    int fontSize = 10;
+    int textWidth = MeasureText(label, fontSize);
+
+    DrawText(label, (int)(rec.x + rec.width*0.5f - textWidth*0.5f), (int)(rec.y + rec.height*0.5f - fontSize*0.5f), fontSize, DARKGRAY);
+
+    return pressed;
+}
diff --git a/raylib/examples/models/models_directional_billboard.c b/raylib/examples/models/models_directional_billboard.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_directional_billboard.c
@@ -0,0 +1,116 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - directional billboard
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*   Killbot art by patvanmackelberg https://opengameart.org/content/killbot-8-directional under CC0
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+#include <stdlib.h>
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - directional billboard");
+
+    // Set up the camera
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 2.0f, 1.0f, 2.0f }; // Starting position
+    camera.target = (Vector3){ 0.0f, 0.5f, 0.0f };  // Target position
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Up vector
+    camera.fovy = 45.0f; // FOV
+    camera.projection = CAMERA_PERSPECTIVE; // Projection type (Standard 3D perspective)
+
+    // Load billboard texture
+    Texture skillbot = LoadTexture("resources/skillbot.png");
+
+    // Timer to update animation
+    float anim_timer = 0.0f;
+    // Animation frame
+    unsigned int anim = 0;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        // Update timer with delta time
+        anim_timer += GetFrameTime();
+
+        // Update frame index after a certain amount of time (half a second)
+        if (anim_timer > 0.5f)
+        {
+            anim_timer = 0.0f;
+            anim += 1;
+        }
+
+        // Reset frame index to zero on overflow
+        if (anim >= 4) anim = 0;
+
+        // Find the current direction frame based on the camera position to the billboard object
+        float dir = (float)floor(((Vector2Angle((Vector2){ 2.0f, 0.0f }, (Vector2){ camera.position.x, camera.position.z })/PI)*4.0f) + 0.25f);
+
+        // Correct frame index if angle is negative
+        if (dir < 0.0f)
+        {
+            dir = 8.0f - (float)abs((int)dir);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+        ClearBackground(RAYWHITE);
+
+        BeginMode3D(camera);
+
+            DrawGrid(10, 1.0f);
+
+            // Draw billboard pointing straight up to the sky, rotated relative to the camera and offset from the bottom
+            DrawBillboardPro(camera, skillbot, (Rectangle){ 0.0f + (anim*24.0f), 0.0f + (dir*24.0f), 24.0f, 24.0f }, Vector3Zero(), (Vector3){ 0.0f, 1.0f, 0.0f }, Vector2One(), (Vector2){ 0.5f, 0.0f }, 0, WHITE);
+
+        EndMode3D();
+
+        // Render various variables for reference
+        DrawText(TextFormat("animation: %d", anim), 10, 10, 20, DARKGRAY);
+        DrawText(TextFormat("direction frame: %.0f", dir), 10, 40, 20, DARKGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    // Unload billboard texture
+    UnloadTexture(skillbot);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_draw_cube_texture.c b/raylib/examples/models/models_draw_cube_texture.c
deleted file mode 100644
--- a/raylib/examples/models/models_draw_cube_texture.c
+++ /dev/null
@@ -1,245 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Draw textured cube
-*
-*   Example originally created with raylib 4.5, last time updated with raylib 4.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2022-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "rlgl.h"       // Required to define vertex data (immediate-mode style)
-
-//------------------------------------------------------------------------------------
-// Custom Functions Declaration
-//------------------------------------------------------------------------------------
-void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured
-void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color); // Draw cube with a region of a texture
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - draw cube texture");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 0.0f, 10.0f, 10.0f };
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
-    camera.fovy = 45.0f;
-    camera.projection = CAMERA_PERSPECTIVE;
-    
-    // Load texture to be applied to the cubes sides
-    Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // TODO: Update your variables here
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                // Draw cube with an applied texture
-                DrawCubeTexture(texture, (Vector3){ -2.0f, 2.0f, 0.0f }, 2.0f, 4.0f, 2.0f, WHITE);
-
-                // Draw cube with an applied texture, but only a defined rectangle piece of the texture
-                DrawCubeTextureRec(texture, (Rectangle){ 0.0f, texture.height/2.0f, texture.width/2.0f, texture.height/2.0f }, 
-                    (Vector3){ 2.0f, 1.0f, 0.0f }, 2.0f, 2.0f, 2.0f, WHITE);
-
-                DrawGrid(10, 1.0f);        // Draw a grid
-
-            EndMode3D();
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texture); // Unload texture
-    
-    CloseWindow();          // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-//------------------------------------------------------------------------------------
-// Custom Functions Definition
-//------------------------------------------------------------------------------------
-// Draw cube textured
-// NOTE: Cube position is the center position
-void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color)
-{
-    float x = position.x;
-    float y = position.y;
-    float z = position.z;
-
-    // Set desired texture to be enabled while drawing following vertex data
-    rlSetTexture(texture.id);
-
-    // Vertex data transformation can be defined with the commented lines,
-    // but in this example we calculate the transformed vertex data directly when calling rlVertex3f()
-    //rlPushMatrix();
-        // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
-        //rlTranslatef(2.0f, 0.0f, 0.0f);
-        //rlRotatef(45, 0, 1, 0);
-        //rlScalef(2.0f, 2.0f, 2.0f);
-
-        rlBegin(RL_QUADS);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-            // Front Face
-            rlNormal3f(0.0f, 0.0f, 1.0f);       // Normal Pointing Towards Viewer
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Right Of The Texture and Quad
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left Of The Texture and Quad
-            // Back Face
-            rlNormal3f(0.0f, 0.0f, - 1.0f);     // Normal Pointing Away From Viewer
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right Of The Texture and Quad
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Left Of The Texture and Quad
-            // Top Face
-            rlNormal3f(0.0f, 1.0f, 0.0f);       // Normal Pointing Up
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Bottom Left Of The Texture and Quad
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right Of The Texture and Quad
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
-            // Bottom Face
-            rlNormal3f(0.0f, - 1.0f, 0.0f);     // Normal Pointing Down
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Right Of The Texture and Quad
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top Left Of The Texture and Quad
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
-            // Right face
-            rlNormal3f(1.0f, 0.0f, 0.0f);       // Normal Pointing Right
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right Of The Texture and Quad
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left Of The Texture and Quad
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
-            // Left Face
-            rlNormal3f( - 1.0f, 0.0f, 0.0f);    // Normal Pointing Left
-            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Left Of The Texture and Quad
-            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
-            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Right Of The Texture and Quad
-            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
-        rlEnd();
-    //rlPopMatrix();
-
-    rlSetTexture(0);
-}
-
-// Draw cube with texture piece applied to all faces
-void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color)
-{
-    float x = position.x;
-    float y = position.y;
-    float z = position.z;
-    float texWidth = (float)texture.width;
-    float texHeight = (float)texture.height;
-
-    // Set desired texture to be enabled while drawing following vertex data
-    rlSetTexture(texture.id);
-
-    // We calculate the normalized texture coordinates for the desired texture-source-rectangle
-    // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent 
-    rlBegin(RL_QUADS);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        // Front face
-        rlNormal3f(0.0f, 0.0f, 1.0f);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z + length/2);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z + length/2);
-
-        // Back face
-        rlNormal3f(0.0f, 0.0f, - 1.0f);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z - length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z - length/2);
-
-        // Top face
-        rlNormal3f(0.0f, 1.0f, 0.0f);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z - length/2);
-
-        // Bottom face
-        rlNormal3f(0.0f, - 1.0f, 0.0f);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z + length/2);
-
-        // Right face
-        rlNormal3f(1.0f, 0.0f, 0.0f);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z - length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z - length/2);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x + width/2, y + height/2, z + length/2);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x + width/2, y - height/2, z + length/2);
-
-        // Left face
-        rlNormal3f( - 1.0f, 0.0f, 0.0f);
-        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z - length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
-        rlVertex3f(x - width/2, y - height/2, z + length/2);
-        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z + length/2);
-        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
-        rlVertex3f(x - width/2, y + height/2, z - length/2);
-
-    rlEnd();
-
-    rlSetTexture(0);
-}
diff --git a/raylib/examples/models/models_first_person_maze.c b/raylib/examples/models/models_first_person_maze.c
--- a/raylib/examples/models/models_first_person_maze.c
+++ b/raylib/examples/models/models_first_person_maze.c
@@ -2,12 +2,14 @@
 *
 *   raylib [models] example - first person maze
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -78,18 +80,23 @@
         if (playerCellY < 0) playerCellY = 0;
         else if (playerCellY >= cubicmap.height) playerCellY = cubicmap.height - 1;
 
-        // Check map collisions using image data and player position
-        // TODO: Improvement: Just check player surrounding cells for collision
-        for (int y = 0; y < cubicmap.height; y++)
+        // Check map collisions using image data and player position against surrounding cells only
+        for (int y = playerCellY - 1; y <= playerCellY + 1; y++)
         {
-            for (int x = 0; x < cubicmap.width; x++)
+            // Avoid map accessing out of bounds
+            if ((y >= 0) && (y < cubicmap.height))
             {
-                if ((mapPixels[y*cubicmap.width + x].r == 255) &&       // Collision: white pixel, only check R channel
-                    (CheckCollisionCircleRec(playerPos, playerRadius,
-                    (Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f })))
+                for (int x = playerCellX - 1; x <= playerCellX + 1; x++)
                 {
-                    // Collision detected, reset camera position
-                    camera.position = oldCamPos;
+                    // NOTE: Collision: Only checking R channel for white pixel
+                    if (((x >= 0) && (x < cubicmap.width)) &&
+                        (mapPixels[y*cubicmap.width + x].r == 255) &&
+                        (CheckCollisionCircleRec(playerPos, playerRadius,
+                        (Rectangle){ mapPosition.x - 0.5f + x*1.0f, mapPosition.z - 0.5f + y*1.0f, 1.0f, 1.0f })))
+                    {
+                        // Collision detected, reset camera position
+                        camera.position = oldCamPos;
+                    }
                 }
             }
         }
diff --git a/raylib/examples/models/models_geometric_shapes.c b/raylib/examples/models/models_geometric_shapes.c
--- a/raylib/examples/models/models_geometric_shapes.c
+++ b/raylib/examples/models/models_geometric_shapes.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Draw some basic geometric shapes (cube, sphere, cylinder...)
+*   raylib [models] example - geometric shapes
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/models/models_gpu_skinning.c b/raylib/examples/models/models_gpu_skinning.c
deleted file mode 100644
--- a/raylib/examples/models/models_gpu_skinning.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [core] example - Doing skinning on the gpu using a vertex shader
-* 
-*   Example originally created with raylib 4.5, last time updated with raylib 4.5
-*
-*   Example contributed by Daniel Holden (@orangeduck) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2024 Daniel Holden (@orangeduck)
-* 
-*   Note: Due to limitations in the Apple OpenGL driver, this feature does not work on MacOS
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "raymath.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - GPU skinning");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 5.0f, 5.0f, 5.0f }; // Camera position
-    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };  // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };      // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                            // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;         // Camera projection type
-
-    // Load gltf model
-    Model characterModel = LoadModel("resources/models/gltf/greenman.glb"); // Load character model
-    
-    // Load skinning shader
-    Shader skinningShader = LoadShader(TextFormat("resources/shaders/glsl%i/skinning.vs", GLSL_VERSION),
-                                       TextFormat("resources/shaders/glsl%i/skinning.fs", GLSL_VERSION));
-    
-    characterModel.materials[1].shader = skinningShader;
-    
-    // Load gltf model animations
-    int animsCount = 0;
-    unsigned int animIndex = 0;
-    unsigned int animCurrentFrame = 0;
-    ModelAnimation *modelAnimations = LoadModelAnimations("resources/models/gltf/greenman.glb", &animsCount);
-
-    Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
-
-    DisableCursor();                    // Limit cursor to relative movement inside the window
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_THIRD_PERSON);
-        
-        // Select current animation
-        if (IsKeyPressed(KEY_T)) animIndex = (animIndex + 1)%animsCount;
-        else if (IsKeyPressed(KEY_G)) animIndex = (animIndex + animsCount - 1)%animsCount;
-
-        // Update model animation
-        ModelAnimation anim = modelAnimations[animIndex];
-        animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount;
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-            
-                // Draw character
-                characterModel.transform = MatrixTranslate(position.x, position.y, position.z);
-                UpdateModelAnimationBoneMatrices(characterModel, anim, animCurrentFrame);
-                DrawMesh(characterModel.meshes[0], characterModel.materials[1], characterModel.transform);
-
-                DrawGrid(10, 1.0f);
-            EndMode3D();
-
-            DrawText("Use the T/G to switch animation", 10, 10, 20, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadModelAnimations(modelAnimations, animsCount);
-    UnloadModel(characterModel);         // Unload character model and meshes/material
-    UnloadShader(skinningShader);
-    
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/models/models_heightmap.c b/raylib/examples/models/models_heightmap.c
deleted file mode 100644
--- a/raylib/examples/models/models_heightmap.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Heightmap loading and drawing
-*
-*   Example originally created with raylib 1.8, last time updated with raylib 3.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing");
-
-    // Define our custom camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 18.0f, 21.0f, 18.0f };     // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                    // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
-
-    Image image = LoadImage("resources/heightmap.png");     // Load heightmap image (RAM)
-    Texture2D texture = LoadTextureFromImage(image);        // Convert image to texture (VRAM)
-
-    Mesh mesh = GenMeshHeightmap(image, (Vector3){ 16, 8, 16 }); // Generate heightmap mesh (RAM and VRAM)
-    Model model = LoadModelFromMesh(mesh);                  // Load model from generated mesh
-
-    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture
-    Vector3 mapPosition = { -8.0f, 0.0f, -8.0f };           // Define model position
-
-    UnloadImage(image);             // Unload heightmap image from RAM, already uploaded to VRAM
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                DrawModel(model, mapPosition, 1.0f, RED);
-
-                DrawGrid(20, 1.0f);
-
-            EndMode3D();
-
-            DrawTexture(texture, screenWidth - texture.width - 20, 20, WHITE);
-            DrawRectangleLines(screenWidth - texture.width - 20, 20, texture.width, texture.height, GREEN);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texture);     // Unload texture
-    UnloadModel(model);         // Unload model
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/models/models_heightmap_rendering.c b/raylib/examples/models/models_heightmap_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_heightmap_rendering.c
@@ -0,0 +1,92 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - heightmap rendering
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 1.8, last time updated with raylib 3.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap rendering");
+
+    // Define our custom camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 18.0f, 21.0f, 18.0f };     // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                    // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
+
+    Image image = LoadImage("resources/heightmap.png");     // Load heightmap image (RAM)
+    Texture2D texture = LoadTextureFromImage(image);        // Convert image to texture (VRAM)
+
+    Mesh mesh = GenMeshHeightmap(image, (Vector3){ 16, 8, 16 }); // Generate heightmap mesh (RAM and VRAM)
+    Model model = LoadModelFromMesh(mesh);                  // Load model from generated mesh
+
+    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture
+    Vector3 mapPosition = { -8.0f, 0.0f, -8.0f };           // Define model position
+
+    UnloadImage(image);             // Unload heightmap image from RAM, already uploaded to VRAM
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                DrawModel(model, mapPosition, 1.0f, RED);
+
+                DrawGrid(20, 1.0f);
+
+            EndMode3D();
+
+            DrawTexture(texture, screenWidth - texture.width - 20, 20, WHITE);
+            DrawRectangleLines(screenWidth - texture.width - 20, 20, texture.width, texture.height, GREEN);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture);     // Unload texture
+    UnloadModel(model);         // Unload model
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_loading.c b/raylib/examples/models/models_loading.c
--- a/raylib/examples/models/models_loading.c
+++ b/raylib/examples/models/models_loading.c
@@ -1,15 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Models loading
+*   raylib [models] example - loading
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: raylib supports multiple models file formats:
 *
 *     - OBJ  > Text file format. Must include vertex position-texcoords-normals information,
-*              if files references some .mtl materials file, it will be loaded (or try to).
+*              if files references some .mtl materials file, it will be loaded (or try to)
 *     - GLTF > Text/binary file format. Includes lot of information and it could
-*              also reference external files, raylib will try loading mesh and materials data.
+*              also reference external files, raylib will try loading mesh and materials data
 *     - IQM  > Binary file format. Includes mesh vertex data but also animation data,
-*              raylib can load .iqm animations.
+*              raylib can load .iqm animations
 *     - VOX  > Binary file format. MagikaVoxel mesh format:
 *              https://github.com/ephtracy/voxel-model/blob/master/MagicaVoxel-file-format-vox.txt
 *     - M3D  > Binary file format. Model 3D format:
@@ -20,7 +22,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -36,7 +38,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - models loading");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - loading");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
diff --git a/raylib/examples/models/models_loading_gltf.c b/raylib/examples/models/models_loading_gltf.c
--- a/raylib/examples/models/models_loading_gltf.c
+++ b/raylib/examples/models/models_loading_gltf.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - loading gltf with animations
+*   raylib [models] example - loading gltf
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   LIMITATIONS:
 *     - Only supports 1 armature per file, and skips loading it if there are multiple armatures
 *     - Only supports linear interpolation (default method in Blender when checked
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -30,7 +32,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf animations");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - loading gltf");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -43,7 +45,7 @@
     // Load gltf model
     Model model = LoadModel("resources/models/gltf/robot.glb");
     Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position
-    
+
     // Load gltf model animations
     int animsCount = 0;
     unsigned int animIndex = 0;
diff --git a/raylib/examples/models/models_loading_m3d.c b/raylib/examples/models/models_loading_m3d.c
--- a/raylib/examples/models/models_loading_m3d.c
+++ b/raylib/examples/models/models_loading_m3d.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Load models M3D
+*   raylib [models] example - loading m3d
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 4.5, last time updated with raylib 4.5
 *
 *   Example contributed by bzt (@bztsrc) and reviewed by Ramon Santamaria (@raysan5)
@@ -13,7 +15,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 bzt (@bztsrc)
+*   Copyright (c) 2022-2025 bzt (@bztsrc)
 *
 ********************************************************************************************/
 
@@ -29,7 +31,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - M3D model loading");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - loading m3d");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -117,7 +119,7 @@
                     // without a -1, we would always draw a cube at the origin
                     for (int i = 0; i < model.boneCount - 1; i++)
                     {
-                        // By default the model is loaded in bind-pose by LoadModel().
+                        // By default the model is loaded in bind-pose by LoadModel()
                         // But if UpdateModelAnimation() has been called at least once
                         // then the model is already in animation pose, so we need the animated skeleton
                         if (!animPlaying || !animsCount)
diff --git a/raylib/examples/models/models_loading_vox.c b/raylib/examples/models/models_loading_vox.c
--- a/raylib/examples/models/models_loading_vox.c
+++ b/raylib/examples/models/models_loading_vox.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Load models vox (MagicaVoxel)
+*   raylib [models] example - loading vox
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 4.0, last time updated with raylib 4.0
 *
 *   Example contributed by Johann Nadalutti (@procfxgen) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Johann Nadalutti (@procfxgen) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -33,173 +35,157 @@
 //------------------------------------------------------------------------------------
 int main(void)
 {
-	// Initialization
-	//--------------------------------------------------------------------------------------
-	const int screenWidth = 800;
-	const int screenHeight = 450;
-
-	const char* voxFileNames[] = {
-		"resources/models/vox/chr_knight.vox",
-		"resources/models/vox/chr_sword.vox",
-		"resources/models/vox/monu9.vox",
-		"resources/models/vox/fez.vox"
-	};
-
-	InitWindow(screenWidth, screenHeight, "raylib [models] example - magicavoxel loading");
-
-	// Define the camera to look into our 3d world
-	Camera camera = { 0 };
-	camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
-	camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
-	camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-	camera.fovy = 45.0f;                                // Camera field-of-view Y
-	camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-	//--------------------------------------------------------------------------------------
-	// Load MagicaVoxel files
-	Model models[MAX_VOX_FILES] = { 0 };
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
 
-	for (int i = 0; i < MAX_VOX_FILES; i++)
-	{
-		// Load VOX file and measure time
-		double t0 = GetTime() * 1000.0;
-		models[i] = LoadModel(voxFileNames[i]);
-		double t1 = GetTime() * 1000.0;
+    const char *voxFileNames[] = {
+        "resources/models/vox/chr_knight.vox",
+        "resources/models/vox/chr_sword.vox",
+        "resources/models/vox/monu9.vox",
+        "resources/models/vox/fez.vox"
+    };
 
-		TraceLog(LOG_WARNING, TextFormat("[%s] File loaded in %.3f ms", voxFileNames[i], t1 - t0));
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - loading vox");
 
-		// Compute model translation matrix to center model on draw position (0, 0 , 0)
-		BoundingBox bb = GetModelBoundingBox(models[i]);
-		Vector3 center = { 0 };
-		center.x = bb.min.x + (((bb.max.x - bb.min.x) / 2));
-		center.z = bb.min.z + (((bb.max.z - bb.min.z) / 2));
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
 
-		Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
-		models[i].transform = matTranslate;
-	}
+    // Load MagicaVoxel files
+    Model models[MAX_VOX_FILES] = { 0 };
 
-	int currentModel = 0;
+    for (int i = 0; i < MAX_VOX_FILES; i++)
+    {
+        // Load VOX file and measure time
+        double t0 = GetTime()*1000.0;
+        models[i] = LoadModel(voxFileNames[i]);
+        double t1 = GetTime()*1000.0;
 
-	//--------------------------------------------------------------------------------------
-	// Load voxel shader
-	Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
-		TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
+        TraceLog(LOG_INFO, TextFormat("[%s] Model file loaded in %.3f ms", voxFileNames[i], t1 - t0));
 
-	// Get some required shader locations
-	shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
-	// NOTE: "matModel" location name is automatically assigned on shader loading, 
-	// no need to get the location again if using that uniform name
-	//shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
+        // Compute model translation matrix to center model on draw position (0, 0 , 0)
+        BoundingBox bb = GetModelBoundingBox(models[i]);
+        Vector3 center = { 0 };
+        center.x = bb.min.x + (((bb.max.x - bb.min.x)/2));
+        center.z = bb.min.z + (((bb.max.z - bb.min.z)/2));
 
-	// Ambient light level (some basic lighting)
-	int ambientLoc = GetShaderLocation(shader, "ambient");
-	SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
+        Matrix matTranslate = MatrixTranslate(-center.x, 0, -center.z);
+        models[i].transform = matTranslate;
+    }
 
-	// Assign out lighting shader to model
-	for (int i = 0; i < MAX_VOX_FILES; i++)
-	{
-		Model m = models[i];
-		for (int j = 0; j < m.materialCount; j++)
-		{
-			m.materials[j].shader = shader;
-		}
-	}
+    int currentModel = 0;
+    Vector3 modelpos = { 0 };
+    Vector3 camerarot = { 0 };
 
-	// Create lights
-	Light lights[MAX_LIGHTS] = { 0 };
-	lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
-	lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
-	lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
-	lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
+    // Load voxel shader
+    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/voxel_lighting.vs", GLSL_VERSION),
+        TextFormat("resources/shaders/glsl%i/voxel_lighting.fs", GLSL_VERSION));
 
+    // Get some required shader locations
+    shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+    // NOTE: "matModel" location name is automatically assigned on shader loading,
+    // no need to get the location again if using that uniform name
+    //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
 
-	SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    // Ambient light level (some basic lighting)
+    int ambientLoc = GetShaderLocation(shader, "ambient");
+    SetShaderValue(shader, ambientLoc, (float[4]) { 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
 
-	//--------------------------------------------------------------------------------------
-	Vector3 modelpos = { 0 };
-	Vector3 camerarot = { 0 };
+    // Assign out lighting shader to model
+    for (int i = 0; i < MAX_VOX_FILES; i++)
+    {
+        for (int j = 0; j < models[i].materialCount; j++) models[i].materials[j].shader = shader;
+    }
 
-	// Main game loop
-	while (!WindowShouldClose())    // Detect window close button or ESC key
-	{
-		// Update
-		//----------------------------------------------------------------------------------
-		if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
-		{
-			const Vector2 mouseDelta = GetMouseDelta();
-			camerarot.x = mouseDelta.x * 0.05f;
-			camerarot.y = mouseDelta.y * 0.05f;
-		}
-		else
-		{
-			camerarot.x = 0;
-			camerarot.y = 0;
-		}
+    // Create lights
+    Light lights[MAX_LIGHTS] = { 0 };
+    lights[0] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, -20 }, Vector3Zero(), GRAY, shader);
+    lights[1] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, 20 }, Vector3Zero(), GRAY, shader);
+    lights[2] = CreateLight(LIGHT_POINT, (Vector3) { -20, 20, 20 }, Vector3Zero(), GRAY, shader);
+    lights[3] = CreateLight(LIGHT_POINT, (Vector3) { 20, -20, -20 }, Vector3Zero(), GRAY, shader);
 
-		UpdateCameraPro(&camera,
-			(Vector3) {
-			(IsKeyDown(KEY_W) || IsKeyDown(KEY_UP)) * 0.1f -      // Move forward-backward
-				(IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN)) * 0.1f,
-				(IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT)) * 0.1f -   // Move right-left
-				(IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT)) * 0.1f,
-				0.0f                                                // Move up-down
-		},
-			camerarot,
-			GetMouseWheelMove() * -2.0f);                              // Move to target (zoom)
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
 
-		// Cycle between models on mouse click
-		if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1) % MAX_VOX_FILES;
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))
+        {
+            const Vector2 mouseDelta = GetMouseDelta();
+            camerarot.x = mouseDelta.x*0.05f;
+            camerarot.y = mouseDelta.y*0.05f;
+        }
+        else
+        {
+            camerarot.x = 0;
+            camerarot.y = 0;
+        }
 
-		// Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
-		float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
-		SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+        UpdateCameraPro(&camera,
+            (Vector3){ (IsKeyDown(KEY_W) || IsKeyDown(KEY_UP))*0.1f - (IsKeyDown(KEY_S) || IsKeyDown(KEY_DOWN))*0.1f, // Move forward-backward
+                       (IsKeyDown(KEY_D) || IsKeyDown(KEY_RIGHT))*0.1f - (IsKeyDown(KEY_A) || IsKeyDown(KEY_LEFT))*0.1f, // Move right-left
+                       0.0f }, // Move up-down
+            camerarot, // Camera rotation
+            GetMouseWheelMove()*-2.0f); // Move to target (zoom)
 
-		// Update light values (actually, only enable/disable them)
-		for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
+        // Cycle between models on mouse click
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) currentModel = (currentModel + 1)%MAX_VOX_FILES;
 
-		//----------------------------------------------------------------------------------
-		// Draw
-		//----------------------------------------------------------------------------------
-		BeginDrawing();
+        // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
+        float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+        SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
 
-		ClearBackground(RAYWHITE);
+        // Update light values (actually, only enable/disable them)
+        for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
+        //----------------------------------------------------------------------------------
 
-		// Draw 3D model
-		BeginMode3D(camera);
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
 
-		DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
-		DrawGrid(10, 1.0);
+            ClearBackground(RAYWHITE);
 
-		// Draw spheres to show where the lights are
-		for (int i = 0; i < MAX_LIGHTS; i++)
-		{
-			if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
-			else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
-		}
+            // Draw 3D model
+            BeginMode3D(camera);
+                DrawModel(models[currentModel], modelpos, 1.0f, WHITE);
+                DrawGrid(10, 1.0);
 
-		EndMode3D();
+                // Draw spheres to show where the lights are
+                for (int i = 0; i < MAX_LIGHTS; i++)
+                {
+                    if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
+                    else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
+                }
+            EndMode3D();
 
-		// Display info
-		DrawRectangle(10, 400, 340, 60, Fade(SKYBLUE, 0.5f));
-		DrawRectangleLines(10, 400, 340, 60, Fade(DARKBLUE, 0.5f));
-		DrawText("MOUSE LEFT BUTTON to CYCLE VOX MODELS", 40, 410, 10, BLUE);
-		DrawText("MOUSE MIDDLE BUTTON to ZOOM OR ROTATE CAMERA", 40, 420, 10, BLUE);
-		DrawText("UP-DOWN-LEFT-RIGHT KEYS to MOVE CAMERA", 40, 430, 10, BLUE);
-		DrawText(TextFormat("File: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
+            // Display info
+            DrawRectangle(10, 40, 340, 70, Fade(SKYBLUE, 0.5f));
+            DrawRectangleLines(10, 40, 340, 70, Fade(DARKBLUE, 0.5f));
+            DrawText("- MOUSE LEFT BUTTON: CYCLE VOX MODELS", 20, 50, 10, BLUE);
+            DrawText("- MOUSE MIDDLE BUTTON: ZOOM OR ROTATE CAMERA", 20, 70, 10, BLUE);
+            DrawText("- UP-DOWN-LEFT-RIGHT KEYS: MOVE CAMERA", 20, 90, 10, BLUE);
+            DrawText(TextFormat("Model file: %s", GetFileName(voxFileNames[currentModel])), 10, 10, 20, GRAY);
 
-		EndDrawing();
-		//----------------------------------------------------------------------------------
-	}
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
 
-	// De-Initialization
-	//--------------------------------------------------------------------------------------
-	// Unload models data (GPU VRAM)
-	for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    // Unload models data (GPU VRAM)
+    for (int i = 0; i < MAX_VOX_FILES; i++) UnloadModel(models[i]);
 
-	CloseWindow();          // Close window and OpenGL context
-	//--------------------------------------------------------------------------------------
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
 
-	return 0;
+    return 0;
 }
-
-
diff --git a/raylib/examples/models/models_mesh_generation.c b/raylib/examples/models/models_mesh_generation.c
--- a/raylib/examples/models/models_mesh_generation.c
+++ b/raylib/examples/models/models_mesh_generation.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib example - procedural mesh generation
+*   raylib [models] example - mesh generation
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.8, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -15,6 +17,9 @@
 
 #define NUM_MODELS  9               // Parametric 3d shapes to generate
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 static Mesh GenMeshCustom(void);    // Generate a simple triangle mesh from code
 
 //------------------------------------------------------------------------------------
@@ -45,18 +50,9 @@
     models[6] = LoadModelFromMesh(GenMeshKnot(1.0f, 2.0f, 16, 128));
     models[7] = LoadModelFromMesh(GenMeshPoly(5, 2.0f));
     models[8] = LoadModelFromMesh(GenMeshCustom());
-    
-    // Generated meshes could be exported as .obj files
-    //ExportMesh(models[0].meshes[0], "plane.obj");
-    //ExportMesh(models[1].meshes[0], "cube.obj");
-    //ExportMesh(models[2].meshes[0], "sphere.obj");
-    //ExportMesh(models[3].meshes[0], "hemisphere.obj");
-    //ExportMesh(models[4].meshes[0], "cylinder.obj");
-    //ExportMesh(models[5].meshes[0], "torus.obj");
-    //ExportMesh(models[6].meshes[0], "knot.obj");
-    //ExportMesh(models[7].meshes[0], "poly.obj");
-    //ExportMesh(models[8].meshes[0], "custom.obj");
 
+    // NOTE: Generated meshes could be exported using ExportMesh()
+
     // Set checked texture as default diffuse component for all models material
     for (int i = 0; i < NUM_MODELS; i++) models[i].materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
 
@@ -112,7 +108,7 @@
             DrawRectangleLines(30, 400, 310, 30, Fade(DARKBLUE, 0.5f));
             DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL MODELS", 40, 410, 10, BLUE);
 
-            switch(currentModel)
+            switch (currentModel)
             {
                 case 0: DrawText("PLANE", 680, 10, 20, DARKBLUE); break;
                 case 1: DrawText("CUBE", 680, 10, 20, DARKBLUE); break;
@@ -143,6 +139,9 @@
     return 0;
 }
 
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
 // Generate a simple triangle mesh from code
 static Mesh GenMeshCustom(void)
 {
diff --git a/raylib/examples/models/models_mesh_picking.c b/raylib/examples/models/models_mesh_picking.c
--- a/raylib/examples/models/models_mesh_picking.c
+++ b/raylib/examples/models/models_mesh_picking.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Mesh picking in 3d mode, ground plane, triangle, mesh
+*   raylib [models] example - mesh picking
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 1.7, last time updated with raylib 4.0
 *
 *   Example contributed by Joel Davis (@joeld42) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,13 +11,14 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 #include "raymath.h"
 
+#undef FLT_MAX
 #define FLT_MAX     340282346638528859811704183484516925440.0f     // Maximum value of a float, from bit pattern 01111111011111111111111111111111
 
 //------------------------------------------------------------------------------------
@@ -82,7 +85,7 @@
 
         // Display information about closest hit
         RayCollision collision = { 0 };
-        char *hitObjectName = "None";
+        const char *hitObjectName = "None";
         collision.distance = FLT_MAX;
         collision.hit = false;
         Color cursorColor = WHITE;
@@ -135,7 +138,7 @@
             RayCollision meshHitInfo = { 0 };
             for (int m = 0; m < tower.meshCount; m++)
             {
-                // NOTE: We consider the model.transform for the collision check but 
+                // NOTE: We consider the model.transform for the collision check but
                 // it can be checked against any transform Matrix, used when checking against same
                 // model drawn multiple times with multiple transforms
                 meshHitInfo = GetRayCollisionMesh(ray, tower.meshes[m], tower.transform);
@@ -143,7 +146,7 @@
                 {
                     // Save the closest hit mesh
                     if ((!collision.hit) || (collision.distance > meshHitInfo.distance)) collision = meshHitInfo;
-                    
+
                     break;  // Stop once one mesh collision is detected, the colliding mesh is m
                 }
             }
diff --git a/raylib/examples/models/models_orthographic_projection.c b/raylib/examples/models/models_orthographic_projection.c
--- a/raylib/examples/models/models_orthographic_projection.c
+++ b/raylib/examples/models/models_orthographic_projection.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Show the difference between perspective and orthographic projection
+*   raylib [models] example - orthographic projection
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 2.0, last time updated with raylib 3.7
 *
 *   Example contributed by Max Danielsson (@autious) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Max Danielsson (@autious) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Max Danielsson (@autious) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -28,7 +30,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - geometric shapes");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - orthographic projection");
 
     // Define the camera to look into our 3d world
     Camera camera = { { 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, FOVY_PERSPECTIVE, CAMERA_PERSPECTIVE };
diff --git a/raylib/examples/models/models_point_rendering.c b/raylib/examples/models/models_point_rendering.c
--- a/raylib/examples/models/models_point_rendering.c
+++ b/raylib/examples/models/models_point_rendering.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib example - point rendering
+*   raylib [models] example - point rendering
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 5.0
 *
 *   Example contributed by Reese Gallagher (@satchelfrost) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,31 +11,34 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2024 Reese Gallagher (@satchelfrost)
+*   Copyright (c) 2024-2025 Reese Gallagher (@satchelfrost)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 
 #include <stdlib.h>             // Required for: rand()
-#include <math.h>               // Required for: cos(), sin()
+#include <math.h>               // Required for: cosf(), sinf()
 
 #define MAX_POINTS 10000000     // 10 million
 #define MIN_POINTS 1000         // 1 thousand
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 // Generate mesh using points
-Mesh GenMeshPoints(int numPoints);
+static Mesh GenMeshPoints(int numPoints);
 
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
     const int screenWidth = 800;
     const int screenHeight = 450;
-    
+
     InitWindow(screenWidth, screenHeight, "raylib [models] example - point rendering");
 
     Camera camera = {
@@ -48,15 +53,15 @@
     bool useDrawModelPoints = true;
     bool numPointsChanged = false;
     int numPoints = 1000;
-    
+
     Mesh mesh = GenMeshPoints(numPoints);
     Model model = LoadModelFromMesh(mesh);
-    
-    //SetTargetFPS(60);
+
+    SetTargetFPS(60);
     //--------------------------------------------------------------------------------------
 
     // Main game loop
-    while(!WindowShouldClose())
+    while (!WindowShouldClose())
     {
         // Update
         //----------------------------------------------------------------------------------
@@ -87,15 +92,12 @@
         // Draw
         //----------------------------------------------------------------------------------
         BeginDrawing();
+
             ClearBackground(BLACK);
 
             BeginMode3D(camera);
-
                 // The new method only uploads the points once to the GPU
-                if (useDrawModelPoints)
-                {
-                    DrawModelPoints(model, position, 1.0f, WHITE);
-                }
+                if (useDrawModelPoints) DrawModelPoints(model, position, 1.0f, WHITE);
                 else
                 {
                     // The old method must continually draw the "points" (lines)
@@ -112,27 +114,26 @@
                             .b = mesh.colors[i*4 + 2],
                             .a = mesh.colors[i*4 + 3],
                         };
-                        
+
                         DrawPoint3D(pos, color);
                     }
                 }
 
                 // Draw a unit sphere for reference
                 DrawSphereWires(position, 1.0f, 10, 10, YELLOW);
-                
             EndMode3D();
 
             // Draw UI text
-            DrawText(TextFormat("Point Count: %d", numPoints), 20, screenHeight - 50, 40, WHITE);
-            DrawText("Up - increase points", 20, 70, 20, WHITE);
-            DrawText("Down - decrease points", 20, 100, 20, WHITE);
-            DrawText("Space - drawing function", 20, 130, 20, WHITE);
-            
-            if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 20, 160, 20, GREEN);
-            else DrawText("Using: DrawPoint3D()", 20, 160, 20, RED);
-            
+            DrawText(TextFormat("Point Count: %d", numPoints), 10, screenHeight - 50, 40, WHITE);
+            DrawText("UP - Increase points", 10, 40, 20, WHITE);
+            DrawText("DOWN - Decrease points", 10, 70, 20, WHITE);
+            DrawText("SPACE - Drawing function", 10, 100, 20, WHITE);
+
+            if (useDrawModelPoints) DrawText("Using: DrawModelPoints()", 10, 130, 20, GREEN);
+            else DrawText("Using: DrawPoint3D()", 10, 130, 20, RED);
+
             DrawFPS(10, 10);
-            
+
         EndDrawing();
         //----------------------------------------------------------------------------------
     }
@@ -146,29 +147,32 @@
     return 0;
 }
 
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
 // Generate a spherical point cloud
-Mesh GenMeshPoints(int numPoints)
+static Mesh GenMeshPoints(int numPoints)
 {
-    Mesh mesh = { 
+    Mesh mesh = {
         .triangleCount = 1,
         .vertexCount = numPoints,
         .vertices = (float *)MemAlloc(numPoints*3*sizeof(float)),
         .colors = (unsigned char*)MemAlloc(numPoints*4*sizeof(unsigned char)),
     };
 
-    // https://en.wikipedia.org/wiki/Spherical_coordinate_system
+    // REF: https://en.wikipedia.org/wiki/Spherical_coordinate_system
     for (int i = 0; i < numPoints; i++)
     {
-        float theta = PI*rand()/RAND_MAX;
-        float phi = 2.0f*PI*rand()/RAND_MAX;
-        float r = 10.0f*rand()/RAND_MAX;
-        
-        mesh.vertices[i*3 + 0] = r*sin(theta)*cos(phi);
-        mesh.vertices[i*3 + 1] = r*sin(theta)*sin(phi);
-        mesh.vertices[i*3 + 2] = r*cos(theta);
-        
+        float theta = ((float)PI*rand())/((float)RAND_MAX);
+        float phi = (2.0f*PI*rand())/((float)RAND_MAX);
+        float r = (10.0f*rand())/((float)RAND_MAX);
+
+        mesh.vertices[i*3 + 0] = r*sinf(theta)*cosf(phi);
+        mesh.vertices[i*3 + 1] = r*sinf(theta)*sinf(phi);
+        mesh.vertices[i*3 + 2] = r*cosf(theta);
+
         Color color = ColorFromHSV(r*360.0f, 1.0f, 1.0f);
-        
+
         mesh.colors[i*4 + 0] = color.r;
         mesh.colors[i*4 + 1] = color.g;
         mesh.colors[i*4 + 2] = color.b;
@@ -177,6 +181,6 @@
 
     // Upload mesh data from CPU (RAM) to GPU (VRAM) memory
     UploadMesh(&mesh, false);
-    
+
     return mesh;
 }
diff --git a/raylib/examples/models/models_rlgl_solar_system.c b/raylib/examples/models/models_rlgl_solar_system.c
--- a/raylib/examples/models/models_rlgl_solar_system.c
+++ b/raylib/examples/models/models_rlgl_solar_system.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - rlgl module usage with push/pop matrix transformations
+*   raylib [models] example - rlgl solar system
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding)
 *
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -39,7 +41,7 @@
     const float moonRadius = 0.16f;
     const float moonOrbitRadius = 1.5f;
 
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl module usage with push/pop matrix transformations");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - rlgl solar system");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -126,9 +128,8 @@
 }
 
 //--------------------------------------------------------------------------------------------
-// Module Functions Definitions (local)
+// Module Functions Definition
 //--------------------------------------------------------------------------------------------
-
 // Draw sphere without any matrix transformation
 // NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f
 void DrawSphereBasic(Color color)
diff --git a/raylib/examples/models/models_rotating_cube.c b/raylib/examples/models/models_rotating_cube.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_rotating_cube.c
@@ -0,0 +1,94 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - rotating cube
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Jopestpe (@jopestpe)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jopestpe (@jopestpe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - rotating cube");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 0.0f, 3.0f, 3.0f };
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    camera.fovy = 45.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+
+    // Load image to create texture for the cube
+    Model model = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
+    Image img = LoadImage("resources/cubicmap_atlas.png");
+    Image crop = ImageFromImage(img, (Rectangle){0, img.height/2.0f, img.width/2.0f, img.height/2.0f});
+    Texture2D texture = LoadTextureFromImage(crop);
+    UnloadImage(img);
+    UnloadImage(crop);
+
+    model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
+
+    float rotation = 0.0f;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        rotation += 1.0f;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                // Draw model defining: position, size, rotation-axis, rotation (degrees), size, and tint-color
+                DrawModelEx(model, (Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ 0.5f, 1.0f, 0.0f },
+                    rotation, (Vector3){ 1.0f, 1.0f, 1.0f }, WHITE);
+
+                DrawGrid(10, 1.0f);
+
+            EndMode3D();
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture); // Unload texture
+    UnloadModel(model);     // Unload model
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_skybox.c b/raylib/examples/models/models_skybox.c
deleted file mode 100644
--- a/raylib/examples/models/models_skybox.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [models] example - Skybox loading and drawing
-*
-*   Example originally created with raylib 1.8, last time updated with raylib 4.0
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "rlgl.h"
-#include "raymath.h"      // Required for: MatrixPerspective(), MatrixLookAt()
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-// Generate cubemap (6 faces) from equirectangular (panorama) texture
-static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox loading and drawing");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 1.0f, 1.0f, 1.0f };    // Camera position
-    camera.target = (Vector3){ 4.0f, 1.0f, 4.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    // Load skybox model
-    Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
-    Model skybox = LoadModelFromMesh(cube);
-
-    // Set this to true to use an HDR Texture, Note that raylib must be built with HDR Support for this to work SUPPORT_FILEFORMAT_HDR
-    bool useHDR = false;
-
-    // Load skybox shader and set required locations
-    // NOTE: Some locations are automatically set at shader loading
-    skybox.materials[0].shader = LoadShader(TextFormat("resources/shaders/glsl%i/skybox.vs", GLSL_VERSION),
-                                            TextFormat("resources/shaders/glsl%i/skybox.fs", GLSL_VERSION));
-
-    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MATERIAL_MAP_CUBEMAP }, SHADER_UNIFORM_INT);
-    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]) { useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
-    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR ? 1 : 0 }, SHADER_UNIFORM_INT);
-
-    // Load cubemap shader and setup required shader locations
-    Shader shdrCubemap = LoadShader(TextFormat("resources/shaders/glsl%i/cubemap.vs", GLSL_VERSION),
-                                    TextFormat("resources/shaders/glsl%i/cubemap.fs", GLSL_VERSION));
-
-    SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
-
-    char skyboxFileName[256] = { 0 };
-    
-    if (useHDR)
-    {
-        TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr");
-
-        // Load HDR panorama (sphere) texture
-        Texture2D panorama = LoadTexture(skyboxFileName);
-
-        // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture
-        // NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping
-        // NOTE 2: It seems on some Android devices WebGL, fbo does not properly support a FLOAT-based attachment,
-        // despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
-        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
-
-        UnloadTexture(panorama);        // Texture not required anymore, cubemap already generated
-    }
-    else
-    {
-        Image img = LoadImage("resources/skybox.png");
-        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);    // CUBEMAP_LAYOUT_PANORAMA
-        UnloadImage(img);
-    }
-
-    DisableCursor();                    // Limit cursor to relative movement inside the window
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
-
-        // Load new cubemap texture on drag&drop
-        if (IsFileDropped())
-        {
-            FilePathList droppedFiles = LoadDroppedFiles();
-
-            if (droppedFiles.count == 1)         // Only support one file dropped
-            {
-                if (IsFileExtension(droppedFiles.paths[0], ".png;.jpg;.hdr;.bmp;.tga"))
-                {
-                    // Unload current cubemap texture to load new one
-                    UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture);
-                    
-                    if (useHDR)
-                    {
-                        // Load HDR panorama (sphere) texture
-                        Texture2D panorama = LoadTexture(droppedFiles.paths[0]);
-
-                        // Generate cubemap from panorama texture
-                        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
-                        
-                        UnloadTexture(panorama);    // Texture not required anymore, cubemap already generated
-                    }
-                    else
-                    {
-                        Image img = LoadImage(droppedFiles.paths[0]);
-                        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
-                        UnloadImage(img);
-                    }
-
-                    TextCopy(skyboxFileName, droppedFiles.paths[0]);
-                }
-            }
-
-            UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-
-                // We are inside the cube, we need to disable backface culling!
-                rlDisableBackfaceCulling();
-                rlDisableDepthMask();
-                    DrawModel(skybox, (Vector3){0, 0, 0}, 1.0f, WHITE);
-                rlEnableBackfaceCulling();
-                rlEnableDepthMask();
-
-                DrawGrid(10, 1.0f);
-
-            EndMode3D();
-
-            if (useHDR) DrawText(TextFormat("Panorama image from hdrihaven.com: %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK);
-            else DrawText(TextFormat(": %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadShader(skybox.materials[0].shader);
-    UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture);
-
-    UnloadModel(skybox);        // Unload skybox model
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-// Generate cubemap texture from HDR texture
-static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format)
-{
-    TextureCubemap cubemap = { 0 };
-
-    rlDisableBackfaceCulling();     // Disable backface culling to render inside the cube
-
-    // STEP 1: Setup framebuffer
-    //------------------------------------------------------------------------------------------
-    unsigned int rbo = rlLoadTextureDepth(size, size, true);
-    cubemap.id = rlLoadTextureCubemap(0, size, format);
-
-    unsigned int fbo = rlLoadFramebuffer();
-    rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
-    rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
-
-    // Check if framebuffer is complete with attachments (valid)
-    if (rlFramebufferComplete(fbo)) TraceLog(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo);
-    //------------------------------------------------------------------------------------------
-
-    // STEP 2: Draw to framebuffer
-    //------------------------------------------------------------------------------------------
-    // NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces)
-    rlEnableShader(shader.id);
-
-    // Define projection matrix and send it to shader
-    Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection);
-
-    // Define view matrix for every side of the cubemap
-    Matrix fboViews[6] = {
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  1.0f,  0.0f,  0.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f,  0.0f,  0.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  1.0f,  0.0f }, (Vector3){ 0.0f,  0.0f,  1.0f }),
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f, -1.0f,  0.0f }, (Vector3){ 0.0f,  0.0f, -1.0f }),
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  0.0f,  1.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
-        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f,  0.0f })
-    };
-
-    rlViewport(0, 0, size, size);   // Set viewport to current fbo dimensions
-    
-    // Activate and enable texture for drawing to cubemap faces
-    rlActiveTextureSlot(0);
-    rlEnableTexture(panorama.id);
-
-    for (int i = 0; i < 6; i++)
-    {
-        // Set the view matrix for the current cube face
-        rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
-        
-        // Select the current cubemap face attachment for the fbo
-        // WARNING: This function by default enables->attach->disables fbo!!!
-        rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0);
-        rlEnableFramebuffer(fbo);
-
-        // Load and draw a cube, it uses the current enabled texture
-        rlClearScreenBuffers();
-        rlLoadDrawCube();
-
-        // ALTERNATIVE: Try to use internal batch system to draw the cube instead of rlLoadDrawCube
-        // for some reason this method does not work, maybe due to cube triangles definition? normals pointing out?
-        // TODO: Investigate this issue...
-        //rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system!
-        //rlClearScreenBuffers();
-        //DrawCubeV(Vector3Zero(), Vector3One(), WHITE);
-        //rlDrawRenderBatchActive();
-    }
-    //------------------------------------------------------------------------------------------
-
-    // STEP 3: Unload framebuffer and reset state
-    //------------------------------------------------------------------------------------------
-    rlDisableShader();          // Unbind shader
-    rlDisableTexture();         // Unbind texture
-    rlDisableFramebuffer();     // Unbind framebuffer
-    rlUnloadFramebuffer(fbo);   // Unload framebuffer (and automatically attached depth texture/renderbuffer)
-
-    // Reset viewport dimensions to default
-    rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
-    rlEnableBackfaceCulling();
-    //------------------------------------------------------------------------------------------
-
-    cubemap.width = size;
-    cubemap.height = size;
-    cubemap.mipmaps = 1;
-    cubemap.format = format;
-
-    return cubemap;
-}
diff --git a/raylib/examples/models/models_skybox_rendering.c b/raylib/examples/models/models_skybox_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_skybox_rendering.c
@@ -0,0 +1,282 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - skybox rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 1.8, last time updated with raylib 4.0
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+#include "raymath.h"      // Required for: MatrixPerspective(), MatrixLookAt()
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+// Generate cubemap (6 faces) from equirectangular (panorama) texture
+static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - skybox rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 1.0f, 1.0f, 1.0f };    // Camera position
+    camera.target = (Vector3){ 4.0f, 1.0f, 4.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    // Load skybox model
+    Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
+    Model skybox = LoadModelFromMesh(cube);
+
+    // Set this to true to use an HDR Texture
+    // NOTE: raylib must be built with HDR Support for this to work: SUPPORT_FILEFORMAT_HDR
+    bool useHDR = false;
+
+    // Load skybox shader and set required locations
+    // NOTE: Some locations are automatically set at shader loading
+    skybox.materials[0].shader = LoadShader(TextFormat("resources/shaders/glsl%i/skybox.vs", GLSL_VERSION),
+                                            TextFormat("resources/shaders/glsl%i/skybox.fs", GLSL_VERSION));
+
+    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "environmentMap"), (int[1]){ MATERIAL_MAP_CUBEMAP }, SHADER_UNIFORM_INT);
+    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "doGamma"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
+    SetShaderValue(skybox.materials[0].shader, GetShaderLocation(skybox.materials[0].shader, "vflipped"), (int[1]){ useHDR? 1 : 0 }, SHADER_UNIFORM_INT);
+
+    // Load cubemap shader and setup required shader locations
+    Shader shdrCubemap = LoadShader(TextFormat("resources/shaders/glsl%i/cubemap.vs", GLSL_VERSION),
+                                    TextFormat("resources/shaders/glsl%i/cubemap.fs", GLSL_VERSION));
+
+    SetShaderValue(shdrCubemap, GetShaderLocation(shdrCubemap, "equirectangularMap"), (int[1]){ 0 }, SHADER_UNIFORM_INT);
+
+    char skyboxFileName[256] = { 0 };
+
+    if (useHDR)
+    {
+        TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr");
+
+        // Load HDR panorama (sphere) texture
+        Texture2D panorama = LoadTexture(skyboxFileName);
+
+        // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture
+        // NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping
+        // NOTE 2: It seems on some Android devices WebGL, fbo does not properly support a FLOAT-based attachment,
+        // despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
+        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
+
+        UnloadTexture(panorama);        // Texture not required anymore, cubemap already generated
+    }
+    else
+    {
+        // TODO: WARNING: On PLATFORM_WEB it requires a big amount of memory to process input image 
+        // and generate the required cubemap image to be passed to rlLoadTextureCubemap()
+        Image image = LoadImage("resources/skybox.png");
+        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
+        UnloadImage(image);
+    }
+
+    DisableCursor();                    // Limit cursor to relative movement inside the window
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+
+        // Load new cubemap texture on drag&drop
+        if (IsFileDropped())
+        {
+            FilePathList droppedFiles = LoadDroppedFiles();
+
+            if (droppedFiles.count == 1)         // Only support one file dropped
+            {
+                if (IsFileExtension(droppedFiles.paths[0], ".png;.jpg;.hdr;.bmp;.tga"))
+                {
+                    // Unload current cubemap texture to load new one
+                    UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture);
+
+                    if (useHDR)
+                    {
+                        // Load HDR panorama (sphere) texture
+                        Texture2D panorama = LoadTexture(droppedFiles.paths[0]);
+
+                        // Generate cubemap from panorama texture
+                        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
+
+                        UnloadTexture(panorama);    // Texture not required anymore, cubemap already generated
+                    }
+                    else
+                    {
+                        Image image = LoadImage(droppedFiles.paths[0]);
+                        skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = LoadTextureCubemap(image, CUBEMAP_LAYOUT_AUTO_DETECT);
+                        UnloadImage(image);
+                    }
+
+                    TextCopy(skyboxFileName, droppedFiles.paths[0]);
+                }
+            }
+
+            UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                // We are inside the cube, we need to disable backface culling!
+                rlDisableBackfaceCulling();
+                rlDisableDepthMask();
+                    DrawModel(skybox, (Vector3){0, 0, 0}, 1.0f, WHITE);
+                rlEnableBackfaceCulling();
+                rlEnableDepthMask();
+
+                DrawGrid(10, 1.0f);
+
+            EndMode3D();
+
+            if (useHDR) DrawText(TextFormat("Panorama image from hdrihaven.com: %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK);
+            else DrawText(TextFormat(": %s", GetFileName(skyboxFileName)), 10, GetScreenHeight() - 20, 10, BLACK);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(skybox.materials[0].shader);
+    UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture);
+
+    UnloadModel(skybox);        // Unload skybox model
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Generate cubemap texture from HDR texture
+static TextureCubemap GenTextureCubemap(Shader shader, Texture2D panorama, int size, int format)
+{
+    TextureCubemap cubemap = { 0 };
+
+    rlDisableBackfaceCulling();     // Disable backface culling to render inside the cube
+
+    // STEP 1: Setup framebuffer
+    //------------------------------------------------------------------------------------------
+    unsigned int rbo = rlLoadTextureDepth(size, size, true);
+    cubemap.id = rlLoadTextureCubemap(0, size, format, 1);
+
+    unsigned int fbo = rlLoadFramebuffer();
+    rlFramebufferAttach(fbo, rbo, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
+    rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X, 0);
+
+    // Check if framebuffer is complete with attachments (valid)
+    if (rlFramebufferComplete(fbo)) TraceLog(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", fbo);
+    //------------------------------------------------------------------------------------------
+
+    // STEP 2: Draw to framebuffer
+    //------------------------------------------------------------------------------------------
+    // NOTE: Shader is used to convert HDR equirectangular environment map to cubemap equivalent (6 faces)
+    rlEnableShader(shader.id);
+
+    // Define projection matrix and send it to shader
+    Matrix matFboProjection = MatrixPerspective(90.0*DEG2RAD, 1.0, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_PROJECTION], matFboProjection);
+
+    // Define view matrix for every side of the cubemap
+    Matrix fboViews[6] = {
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  1.0f,  0.0f,  0.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){ -1.0f,  0.0f,  0.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  1.0f,  0.0f }, (Vector3){ 0.0f,  0.0f,  1.0f }),
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f, -1.0f,  0.0f }, (Vector3){ 0.0f,  0.0f, -1.0f }),
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  0.0f,  1.0f }, (Vector3){ 0.0f, -1.0f,  0.0f }),
+        MatrixLookAt((Vector3){ 0.0f, 0.0f, 0.0f }, (Vector3){  0.0f,  0.0f, -1.0f }, (Vector3){ 0.0f, -1.0f,  0.0f })
+    };
+
+    rlViewport(0, 0, size, size);   // Set viewport to current fbo dimensions
+
+    // Activate and enable texture for drawing to cubemap faces
+    rlActiveTextureSlot(0);
+    rlEnableTexture(panorama.id);
+
+    for (int i = 0; i < 6; i++)
+    {
+        // Set the view matrix for the current cube face
+        rlSetUniformMatrix(shader.locs[SHADER_LOC_MATRIX_VIEW], fboViews[i]);
+
+        // Select the current cubemap face attachment for the fbo
+        // WARNING: This function by default enables->attach->disables fbo!!!
+        rlFramebufferAttach(fbo, cubemap.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_CUBEMAP_POSITIVE_X + i, 0);
+        rlEnableFramebuffer(fbo);
+
+        // Load and draw a cube, it uses the current enabled texture
+        rlClearScreenBuffers();
+        rlLoadDrawCube();
+
+        // ALTERNATIVE: Try to use internal batch system to draw the cube instead of rlLoadDrawCube
+        // for some reason this method does not work, maybe due to cube triangles definition? normals pointing out?
+        // TODO: Investigate this issue...
+        //rlSetTexture(panorama.id); // WARNING: It must be called after enabling current framebuffer if using internal batch system!
+        //rlClearScreenBuffers();
+        //DrawCubeV(Vector3Zero(), Vector3One(), WHITE);
+        //rlDrawRenderBatchActive();
+    }
+    //------------------------------------------------------------------------------------------
+
+    // STEP 3: Unload framebuffer and reset state
+    //------------------------------------------------------------------------------------------
+    rlDisableShader();          // Unbind shader
+    rlDisableTexture();         // Unbind texture
+    rlDisableFramebuffer();     // Unbind framebuffer
+    rlUnloadFramebuffer(fbo);   // Unload framebuffer (and automatically attached depth texture/renderbuffer)
+
+    // Reset viewport dimensions to default
+    rlViewport(0, 0, rlGetFramebufferWidth(), rlGetFramebufferHeight());
+    rlEnableBackfaceCulling();
+    //------------------------------------------------------------------------------------------
+
+    cubemap.width = size;
+    cubemap.height = size;
+    cubemap.mipmaps = 1;
+    cubemap.format = format;
+
+    return cubemap;
+}
diff --git a/raylib/examples/models/models_tesseract_view.c b/raylib/examples/models/models_tesseract_view.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_tesseract_view.c
@@ -0,0 +1,128 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - tesseract view
+*
+*   NOTE: This example only works on platforms that support drag & drop (Windows, Linux, OSX, Html5?)
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Timothy van der Valk (@arceryz) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2024-2025 Timothy van der Valk (@arceryz) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - tesseract view");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 4.0f, 4.0f, 4.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 0.0f, 1.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 50.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera mode type
+
+    // Find the coordinates by setting XYZW to +-1
+    Vector4 tesseract[16] = {
+        {  1,  1,  1, 1 }, {  1,  1,  1, -1 },
+        {  1,  1, -1, 1 }, {  1,  1, -1, -1 },
+        {  1, -1,  1, 1 }, {  1, -1,  1, -1 },
+        {  1, -1, -1, 1 }, {  1, -1, -1, -1 },
+        { -1,  1,  1, 1 }, { -1,  1,  1, -1 },
+        { -1,  1, -1, 1 }, { -1,  1, -1, -1 },
+        { -1, -1,  1, 1 }, { -1, -1,  1, -1 },
+        { -1, -1, -1, 1 }, { -1, -1, -1, -1 },
+    };
+
+    float rotation = 0.0f;
+    Vector3 transformed[16] = { 0 };
+    float wValues[16] = { 0 };
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        rotation = DEG2RAD*45.0f*(float)GetTime();
+
+        for (int i = 0; i < 16; i++)
+        {
+            Vector4 p = tesseract[i];
+
+            // Rotate the XW part of the vector
+            Vector2 rotXW = Vector2Rotate((Vector2){ p.x, p.w }, rotation);
+            p.x = rotXW.x;
+            p.w = rotXW.y;
+
+            // Projection from XYZW to XYZ from perspective point (0, 0, 0, 3)
+            // NOTE: Trace a ray from (0, 0, 0, 3) > p and continue until W = 0
+            float c = 3.0f/(3.0f - p.w);
+            p.x = c*p.x;
+            p.y = c*p.y;
+            p.z = c*p.z;
+
+            // Split XYZ coordinate and W values later for drawing
+            transformed[i] = (Vector3){ p.x, p.y, p.z };
+            wValues[i] = p.w;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                for (int i = 0; i < 16; i++)
+                {
+                    // Draw spheres to indicate the W value
+                    DrawSphere(transformed[i], fabsf(wValues[i]*0.1f), RED);
+
+                    for (int j = 0; j < 16; j++)
+                    {
+                        // Two lines are connected if they differ by 1 coordinate
+                        // This way we dont have to keep an edge list
+                        Vector4 v1 = tesseract[i];
+                        Vector4 v2 = tesseract[j];
+                        int diff = (int)(v1.x == v2.x) + (int)(v1.y == v2.y) + (int)(v1.z == v2.z) + (int)(v1.w == v2.w);
+
+                        // Draw only differing by 1 coordinate and the lower index only (duplicate lines)
+                        if (diff == 3 && i < j) DrawLine3D(transformed[i], transformed[j], MAROON);
+                    }
+                }
+            EndMode3D();
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/models/models_textured_cube.c b/raylib/examples/models/models_textured_cube.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/models/models_textured_cube.c
@@ -0,0 +1,247 @@
+/*******************************************************************************************
+*
+*   raylib [models] example - textured cube
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"       // Required to define vertex data (immediate-mode style)
+
+//------------------------------------------------------------------------------------
+// Custom Functions Declaration
+//------------------------------------------------------------------------------------
+void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color); // Draw cube textured
+void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color); // Draw cube with a region of a texture
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - textured cube");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 0.0f, 10.0f, 10.0f };
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    camera.fovy = 45.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+
+    // Load texture to be applied to the cubes sides
+    Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // TODO: Update your variables here
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                // Draw cube with an applied texture
+                DrawCubeTexture(texture, (Vector3){ -2.0f, 2.0f, 0.0f }, 2.0f, 4.0f, 2.0f, WHITE);
+
+                // Draw cube with an applied texture, but only a defined rectangle piece of the texture
+                DrawCubeTextureRec(texture, (Rectangle){ 0.0f, texture.height/2.0f, texture.width/2.0f, texture.height/2.0f },
+                    (Vector3){ 2.0f, 1.0f, 0.0f }, 2.0f, 2.0f, 2.0f, WHITE);
+
+                DrawGrid(10, 1.0f);        // Draw a grid
+
+            EndMode3D();
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture); // Unload texture
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Custom Functions Definition
+//------------------------------------------------------------------------------------
+// Draw cube textured
+// NOTE: Cube position is the center position
+void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color)
+{
+    float x = position.x;
+    float y = position.y;
+    float z = position.z;
+
+    // Set desired texture to be enabled while drawing following vertex data
+    rlSetTexture(texture.id);
+
+    // Vertex data transformation can be defined with the commented lines,
+    // but in this example we calculate the transformed vertex data directly when calling rlVertex3f()
+    //rlPushMatrix();
+        // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
+        //rlTranslatef(2.0f, 0.0f, 0.0f);
+        //rlRotatef(45, 0, 1, 0);
+        //rlScalef(2.0f, 2.0f, 2.0f);
+
+        rlBegin(RL_QUADS);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+            // Front Face
+            rlNormal3f(0.0f, 0.0f, 1.0f);       // Normal Pointing Towards Viewer
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Right Of The Texture and Quad
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left Of The Texture and Quad
+            // Back Face
+            rlNormal3f(0.0f, 0.0f, - 1.0f);     // Normal Pointing Away From Viewer
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right Of The Texture and Quad
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Left Of The Texture and Quad
+            // Top Face
+            rlNormal3f(0.0f, 1.0f, 0.0f);       // Normal Pointing Up
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Bottom Left Of The Texture and Quad
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right Of The Texture and Quad
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
+            // Bottom Face
+            rlNormal3f(0.0f, - 1.0f, 0.0f);     // Normal Pointing Down
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Right Of The Texture and Quad
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top Left Of The Texture and Quad
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
+            // Right face
+            rlNormal3f(1.0f, 0.0f, 0.0f);       // Normal Pointing Right
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right Of The Texture and Quad
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right Of The Texture and Quad
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left Of The Texture and Quad
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left Of The Texture and Quad
+            // Left Face
+            rlNormal3f( - 1.0f, 0.0f, 0.0f);    // Normal Pointing Left
+            rlTexCoord2f(0.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Left Of The Texture and Quad
+            rlTexCoord2f(1.0f, 0.0f); rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Right Of The Texture and Quad
+            rlTexCoord2f(1.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Right Of The Texture and Quad
+            rlTexCoord2f(0.0f, 1.0f); rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left Of The Texture and Quad
+        rlEnd();
+    //rlPopMatrix();
+
+    rlSetTexture(0);
+}
+
+// Draw cube with texture piece applied to all faces
+void DrawCubeTextureRec(Texture2D texture, Rectangle source, Vector3 position, float width, float height, float length, Color color)
+{
+    float x = position.x;
+    float y = position.y;
+    float z = position.z;
+    float texWidth = (float)texture.width;
+    float texHeight = (float)texture.height;
+
+    // Set desired texture to be enabled while drawing following vertex data
+    rlSetTexture(texture.id);
+
+    // We calculate the normalized texture coordinates for the desired texture-source-rectangle
+    // It means converting from (tex.width, tex.height) coordinates to [0.0f, 1.0f] equivalent
+    rlBegin(RL_QUADS);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        // Front face
+        rlNormal3f(0.0f, 0.0f, 1.0f);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z + length/2);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z + length/2);
+
+        // Back face
+        rlNormal3f(0.0f, 0.0f, - 1.0f);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z - length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z - length/2);
+
+        // Top face
+        rlNormal3f(0.0f, 1.0f, 0.0f);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z - length/2);
+
+        // Bottom face
+        rlNormal3f(0.0f, - 1.0f, 0.0f);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z + length/2);
+
+        // Right face
+        rlNormal3f(1.0f, 0.0f, 0.0f);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z - length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z - length/2);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x + width/2, y + height/2, z + length/2);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x + width/2, y - height/2, z + length/2);
+
+        // Left face
+        rlNormal3f( - 1.0f, 0.0f, 0.0f);
+        rlTexCoord2f(source.x/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z - length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, (source.y + source.height)/texHeight);
+        rlVertex3f(x - width/2, y - height/2, z + length/2);
+        rlTexCoord2f((source.x + source.width)/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z + length/2);
+        rlTexCoord2f(source.x/texWidth, source.y/texHeight);
+        rlVertex3f(x - width/2, y + height/2, z - length/2);
+
+    rlEnd();
+
+    rlSetTexture(0);
+}
diff --git a/raylib/examples/models/models_waving_cubes.c b/raylib/examples/models/models_waving_cubes.c
--- a/raylib/examples/models/models_waving_cubes.c
+++ b/raylib/examples/models/models_waving_cubes.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Waving cubes
+*   raylib [models] example - waving cubes
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.7
 *
 *   Example contributed by Codecat (@codecat) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Codecat (@codecat) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Codecat (@codecat) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -20,7 +22,7 @@
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
@@ -89,7 +91,7 @@
                             };
 
                             // Pick a color with a hue depending on cube position for the rainbow color effect
-                            // NOTE: This function is quite costly to be done per cube and frame, 
+                            // NOTE: This function is quite costly to be done per cube and frame,
                             // pre-catching the results into a separate array could improve performance
                             Color cubeColor = ColorFromHSV((float)(((x + y + z)*18)%360), 0.75f, 0.9f);
 
diff --git a/raylib/examples/models/models_yaw_pitch_roll.c b/raylib/examples/models/models_yaw_pitch_roll.c
--- a/raylib/examples/models/models_yaw_pitch_roll.c
+++ b/raylib/examples/models/models_yaw_pitch_roll.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [models] example - Plane rotations (yaw, pitch, roll)
+*   raylib [models] example - yaw pitch roll
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.8, last time updated with raylib 4.0
 *
 *   Example contributed by Berni (@Berni8k) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Berni (@Berni8k) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -28,7 +30,7 @@
     const int screenHeight = 450;
 
     //SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_WINDOW_HIGHDPI);
-    InitWindow(screenWidth, screenHeight, "raylib [models] example - plane rotations (yaw, pitch, roll)");
+    InitWindow(screenWidth, screenHeight, "raylib [models] example - yaw pitch roll");
 
     Camera camera = { 0 };
     camera.position = (Vector3){ 0.0f, 50.0f, -120.0f };// Camera position perspective
diff --git a/raylib/examples/models/rlights.h b/raylib/examples/models/rlights.h
--- a/raylib/examples/models/rlights.h
+++ b/raylib/examples/models/rlights.h
@@ -109,7 +109,7 @@
 static int lightsCount = 0;    // Current amount of created lights
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 // ...
 
diff --git a/raylib/examples/others/easings_testbed.c b/raylib/examples/others/easings_testbed.c
--- a/raylib/examples/others/easings_testbed.c
+++ b/raylib/examples/others/easings_testbed.c
@@ -1,15 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [easings] example - Easings Testbed
+*   raylib [others] example - easings testbed
 *
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example contributed by Juan Miguel López (@flashback-fx) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Juan Miguel López (@flashback-fx ) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Juan Miguel López (@flashback-fx) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -106,7 +108,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings testbed");
+    InitWindow(screenWidth, screenHeight, "raylib [others] example - easings testbed");
 
     Vector2 ballPosition = { 100.0f, 100.0f };
 
@@ -219,10 +221,12 @@
 }
 
 
-// NoEase function, used when "no easing" is selected for any axis. It just ignores all parameters besides b.
+// NoEase function, used when "no easing" is selected for any axis
+// It just ignores all parameters besides b
 static float NoEase(float t, float b, float c, float d)
 {
-    float burn = t + b + c + d;  // Hack to avoid compiler warning (about unused variables)
+    // Hack to avoid compiler warning (about unused variables)
+    float burn = t + b + c + d;
     d += burn;
 
     return b;
diff --git a/raylib/examples/others/embedded_files_loading.c b/raylib/examples/others/embedded_files_loading.c
--- a/raylib/examples/others/embedded_files_loading.c
+++ b/raylib/examples/others/embedded_files_loading.c
@@ -1,15 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [others] example - Embedded files loading (Wave and Image)
+*   raylib [others] example - embedded files loading
 *
-*   Example originally created with raylib 3.0, last time updated with raylib 2.5
+*   Example complexity rating: [★★☆☆] 2/4
 *
+*   Example originally created with raylib 3.0, last time updated with raylib 3.5
+*
 *   Example contributed by Kristian Holmgren (@defutura) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Kristian Holmgren (@defutura) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 Kristian Holmgren (@defutura) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/others/raylib_opengl_interop.c b/raylib/examples/others/raylib_opengl_interop.c
--- a/raylib/examples/others/raylib_opengl_interop.c
+++ b/raylib/examples/others/raylib_opengl_interop.c
@@ -1,26 +1,28 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - OpenGL point particle system
+*   raylib [others] example - OpenGL interoperatibility
 *
-*   Example originally created with raylib 3.8, last time updated with raylib 2.5
+*   Example complexity rating: [★★★★] 4/4
 *
+*   Example originally created with raylib 3.8, last time updated with raylib 4.0
+*
 *   Example contributed by Stephan Soller (@arkanis) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Stephan Soller (@arkanis) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************
 *
 *   Mixes raylib and plain OpenGL code to draw a GL_POINTS based particle system. The
-*   primary point is to demonstrate raylib and OpenGL interop.
+*   primary point is to demonstrate raylib and OpenGL interop
 *
 *   rlgl batched draw operations internally so we have to flush the current batch before
-*   doing our own OpenGL work (rlDrawRenderBatchActive()).
+*   doing our own OpenGL work (rlDrawRenderBatchActive())
 *
 *   The example also demonstrates how to get the current model view projection matrix of
-*   raylib. That way raylib cameras and so on work as expected.
+*   raylib. That way raylib cameras and so on work as expected
 *
 ********************************************************************************************/
 
@@ -28,18 +30,19 @@
 
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL)
     #if defined(GRAPHICS_API_OPENGL_ES2)
-        #include "glad_gles2.h"       // Required for: OpenGL functionality 
+        #define GLAD_GLES2_IMPLEMENTATION
+        #include "glad_gles2.h"       // Required for: OpenGL functionality
         #define glGenVertexArrays glGenVertexArraysOES
         #define glBindVertexArray glBindVertexArrayOES
         #define glDeleteVertexArrays glDeleteVertexArraysOES
         #define GLSL_VERSION            100
     #else
         #if defined(__APPLE__)
-            #define GL_SILENCE_DEPRECATION // Silence Opengl API deprecation warnings 
+            #define GL_SILENCE_DEPRECATION // Silence Opengl API deprecation warnings
             #include <OpenGL/gl3.h>     // OpenGL 3 library for OSX
             #include <OpenGL/gl3ext.h>  // OpenGL 3 extensions library for OSX
         #else
-            #include "glad.h"       // Required for: OpenGL functionality 
+            #include "glad.h"       // Required for: OpenGL functionality
         #endif
         #define GLSL_VERSION            330
     #endif
@@ -52,6 +55,9 @@
 
 #define MAX_PARTICLES       1000
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 // Particle type
 typedef struct Particle {
     float x;
@@ -69,7 +75,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib - point particles");
+    InitWindow(screenWidth, screenHeight, "raylib [others] example - OpenGL interoperatibility");
 
     Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/point_particle.vs", GLSL_VERSION),
                                TextFormat("resources/shaders/glsl%i/point_particle.fs", GLSL_VERSION));
@@ -84,14 +90,14 @@
     {
         particles[i].x = (float)GetRandomValue(20, screenWidth - 20);
         particles[i].y = (float)GetRandomValue(50, screenHeight - 20);
-        
-        // Give each particle a slightly different period. But don't spread it to much. 
-        // This way the particles line up every so often and you get a glimps of what is going on.
+
+        // Give each particle a slightly different period. But don't spread it to much
+        // This way the particles line up every so often and you get a glimps of what is going on
         particles[i].period = (float)GetRandomValue(10, 30)/10.0f;
     }
 
-    // Create a plain OpenGL vertex buffer with the data and an vertex array object 
-    // that feeds the data from the buffer into the vertexPosition shader attribute.
+    // Create a plain OpenGL vertex buffer with the data and an vertex array object
+    // that feeds the data from the buffer into the vertexPosition shader attribute
     GLuint vao = 0;
     GLuint vbo = 0;
     glGenVertexArrays(1, &vao);
@@ -123,13 +129,13 @@
 
             DrawRectangle(10, 10, 210, 30, MAROON);
             DrawText(TextFormat("%zu particles in one vertex buffer", MAX_PARTICLES), 20, 20, 10, RAYWHITE);
-            
+
             rlDrawRenderBatchActive();      // Draw iternal buffers data (previous draw calls)
 
             // Switch to plain OpenGL
             //------------------------------------------------------------------------------
             glUseProgram(shader.id);
-            
+
                 glUniform1f(currentTimeLoc, GetTime());
 
                 Vector4 color = ColorNormalize((Color){ 255, 0, 0, 128 });
@@ -137,18 +143,18 @@
 
                 // Get the current modelview and projection matrix so the particle system is displayed and transformed
                 Matrix modelViewProjection = MatrixMultiply(rlGetMatrixModelview(), rlGetMatrixProjection());
-                
+
                 glUniformMatrix4fv(shader.locs[SHADER_LOC_MATRIX_MVP], 1, false, MatrixToFloat(modelViewProjection));
 
                 glBindVertexArray(vao);
                     glDrawArrays(GL_POINTS, 0, MAX_PARTICLES);
                 glBindVertexArray(0);
-                
+
             glUseProgram(0);
             //------------------------------------------------------------------------------
-            
+
             DrawFPS(screenWidth - 100, 10);
-            
+
         EndDrawing();
         //----------------------------------------------------------------------------------
     }
diff --git a/raylib/examples/others/raymath_vector_angle.c b/raylib/examples/others/raymath_vector_angle.c
deleted file mode 100644
--- a/raylib/examples/others/raymath_vector_angle.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - Vector Angle
-*
-*   Example originally created with raylib 1.0, last time updated with raylib 4.6
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2023 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
- 
-#include "raylib.h"
-
-#include "raymath.h"
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [math] example - vector angle");
-
-    Vector2 v0 = { screenWidth/2, screenHeight/2 };
-    Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f });
-    Vector2 v2 = { 0 };             // Updated with mouse position
-    
-    float angle = 0.0f;             // Angle in degrees
-    int angleMode = 0;              // 0-Vector2Angle(), 1-Vector2LineAngle()
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        float startangle = 0.0f;
-
-        if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG;
-        if (angleMode == 1) startangle = 0.0f; 
-
-        v2 = GetMousePosition();
-
-        if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode;
-        
-        if(angleMode == 0 && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) v1 = GetMousePosition();
-
-        if (angleMode == 0)
-        {
-            // Calculate angle between two vectors, considering a common origin (v0)
-            Vector2 v1Normal = Vector2Normalize(Vector2Subtract(v1, v0));
-            Vector2 v2Normal = Vector2Normalize(Vector2Subtract(v2, v0));
-
-            angle = Vector2Angle(v1Normal, v2Normal)*RAD2DEG;
-        }
-        else if (angleMode == 1)
-        {
-            // Calculate angle defined by a two vectors line, in reference to horizontal line
-            angle = Vector2LineAngle(v0, v2)*RAD2DEG;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-            
-            if (angleMode == 0)
-            {
-                DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, BLACK);
-                DrawText("Right Click to Move V2", 10, 30, 20, DARKGRAY);
-                
-                DrawLineEx(v0, v1, 2.0f, BLACK);
-                DrawLineEx(v0, v2, 2.0f, RED);
-
-                DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f));
-            }
-            else if (angleMode == 1)
-            {
-                DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK);
-                
-                DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY);
-                DrawLineEx(v0, v2, 2.0f, RED);
-
-                DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f));
-            }
-            
-            DrawText("v0", v0.x, v0.y, 10, DARKGRAY);
-
-            // If the line from v0 to v1 would overlap the text, move it's position up 10
-            if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", v1.x, v1.y-10.0f, 10, DARKGRAY);
-            if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", v1.x, v1.y, 10, DARKGRAY);
-
-            // If angle mode 1, use v1 to emphasize the horizontal line
-            if (angleMode == 1) DrawText("v1", v0.x + 40.0f, v0.y, 10, DARKGRAY);
-
-            // position adjusted by -10 so it isn't hidden by cursor
-            DrawText("v2", v2.x-10.0f, v2.y-10.0f, 10, DARKGRAY);
-
-            DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY);
-            DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME);
-            
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/others/rlgl_compute_shader.c b/raylib/examples/others/rlgl_compute_shader.c
--- a/raylib/examples/others/rlgl_compute_shader.c
+++ b/raylib/examples/others/rlgl_compute_shader.c
@@ -1,18 +1,20 @@
 /*******************************************************************************************
 *
-*   raylib [rlgl] example - compute shader - Conway's Game of Life
+*   raylib [others] example - compute shader
 *
 *   NOTE: This example requires raylib OpenGL 4.3 versions for compute shaders support,
 *         shaders used in this example are #version 430 (OpenGL 4.3)
 *
-*   Example originally created with raylib 4.0, last time updated with raylib 2.5
+*   Example complexity rating: [★★★★] 4/4
 *
+*   Example originally created with raylib 4.0, last time updated with raylib 4.0
+*
 *   Example contributed by Teddy Astie (@tsnake41) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Teddy Astie (@tsnake41)
+*   Copyright (c) 2021-2025 Teddy Astie (@tsnake41)
 *
 ********************************************************************************************/
 
@@ -21,13 +23,16 @@
 
 #include <stdlib.h>
 
-// IMPORTANT: This must match gol*.glsl GOL_WIDTH constant.
-// This must be a multiple of 16 (check golLogic compute dispatch).
+// IMPORTANT: This must match gol*.glsl GOL_WIDTH constant
+// This must be a multiple of 16 (check golLogic compute dispatch)
 #define GOL_WIDTH 768
 
-// Maximum amount of queued draw commands (squares draw from mouse down events).
+// Maximum amount of queued draw commands (squares draw from mouse down events)
 #define MAX_BUFFERED_TRANSFERTS 48
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 // Game Of Life Update Command
 typedef struct GolUpdateCmd {
     unsigned int x;         // x coordinate of the gol command
@@ -49,9 +54,12 @@
 {
     // Initialization
     //--------------------------------------------------------------------------------------
-    InitWindow(GOL_WIDTH, GOL_WIDTH, "raylib [rlgl] example - compute shader - game of life");
+    const int screenWidth = GOL_WIDTH;
+    const int screenHeight = GOL_WIDTH;
 
-    const Vector2 resolution = { GOL_WIDTH, GOL_WIDTH };
+    InitWindow(screenWidth, screenHeight, "raylib [others] example - compute shader");
+
+    const Vector2 resolution = { (float)screenWidth, (float)screenHeight };
     unsigned int brushSize = 8;
 
     // Game of Life logic compute shader
@@ -155,7 +163,7 @@
 
     // De-Initialization
     //--------------------------------------------------------------------------------------
-    // Unload shader buffers objects.
+    // Unload shader buffers objects
     rlUnloadShaderBuffer(ssboA);
     rlUnloadShaderBuffer(ssboB);
     rlUnloadShaderBuffer(ssboTransfert);
diff --git a/raylib/examples/others/rlgl_standalone.c b/raylib/examples/others/rlgl_standalone.c
--- a/raylib/examples/others/rlgl_standalone.c
+++ b/raylib/examples/others/rlgl_standalone.c
@@ -1,11 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [rlgl] example - Using rlgl module as standalone module
+*   raylib [others] example - standalone
 *
 *   rlgl library is an abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, ES 2.0)
 *   that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...)
 *
-*   WARNING: This example is intended only for PLATFORM_DESKTOP and OpenGL 3.3 Core profile.
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 1.6, last time updated with raylib 4.0
+*
+*   WARNING: This example is intended only for PLATFORM_DESKTOP and OpenGL 3.3 Core profile
 *       It could work on other platforms if redesigned for those platforms (out-of-scope)
 *
 *   DEPENDENCIES:
@@ -29,7 +33,7 @@
 *   This example is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software:
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -44,7 +48,7 @@
 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
 *     as being the original software.
 *
-*     3. This notice may not be removed or altered from any source distribution.
+*     3. This notice may not be removed or altered from any source distribution
 *
 ********************************************************************************************/
 
@@ -73,7 +77,7 @@
 #define DARKGRAY   (Color){ 80, 80, 80, 255 }      // Dark Gray
 
 //----------------------------------------------------------------------------------
-// Structures Definition
+// Types and Structures Definition
 //----------------------------------------------------------------------------------
 // Color, 4 components, R8G8B8A8 (32bit)
 typedef struct Color {
@@ -93,7 +97,7 @@
 } Camera;
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Functions Declaration
 //----------------------------------------------------------------------------------
 static void ErrorCallback(int error, const char *description);
 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
@@ -133,7 +137,7 @@
 
     glfwWindowHint(GLFW_SAMPLES, 4);
     glfwWindowHint(GLFW_DEPTH_BITS, 16);
-    
+
     // WARNING: OpenGL 3.3 Core profile only
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
@@ -143,7 +147,7 @@
     glfwWindowHint( GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE );
 #endif
 
-    GLFWwindow *window = glfwCreateWindow(screenWidth, screenHeight, "rlgl standalone", NULL, NULL);
+    GLFWwindow *window = glfwCreateWindow(screenWidth, screenHeight, "raylib [others] example - rlgl standalone", NULL, NULL);
 
     if (!window)
     {
@@ -256,7 +260,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definitions
+// Module Functions Definitions
 //----------------------------------------------------------------------------------
 
 // GLFW3: Error callback
@@ -293,7 +297,7 @@
 // Draw a grid centered at (0, 0, 0)
 static void DrawGrid(int slices, float spacing)
 {
-    int halfSlices = slices / 2;
+    int halfSlices = slices/2;
 
     rlBegin(RL_LINES);
         for (int i = -halfSlices; i <= halfSlices; i++)
diff --git a/raylib/examples/others/web_basic_window.c b/raylib/examples/others/web_basic_window.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/others/web_basic_window.c
@@ -0,0 +1,86 @@
+/*******************************************************************************************
+*
+*   raylib [others] example - basic window
+*
+*   This example has been adapted to compile for PLATFORM_WEB and PLATFORM_DESKTOP
+*   As you will notice, code structure is slightly different to the other examples
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 1.3, last time updated with raylib 5.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_WEB)
+    #include <emscripten/emscripten.h>
+#endif
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+int screenWidth = 800;
+int screenHeight = 450;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+void UpdateDrawFrame(void);     // Update and Draw one frame
+
+//----------------------------------------------------------------------------------
+// Program main entry point
+//----------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    InitWindow(screenWidth, screenHeight, "raylib [others] example - web basic window");
+
+#if defined(PLATFORM_WEB)
+    emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
+#else
+    SetTargetFPS(60);   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        UpdateDrawFrame();
+    }
+#endif
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+void UpdateDrawFrame(void)
+{
+    // Update
+    //----------------------------------------------------------------------------------
+    // TODO: Update your variables here
+    //----------------------------------------------------------------------------------
+
+    // Draw
+    //----------------------------------------------------------------------------------
+    BeginDrawing();
+
+        ClearBackground(RAYWHITE);
+
+        DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
+
+    EndDrawing();
+    //----------------------------------------------------------------------------------
+}
diff --git a/raylib/examples/shaders/raygui.h b/raylib/examples/shaders/raygui.h
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/raygui.h
@@ -0,0 +1,5987 @@
+/*******************************************************************************************
+*
+*   raygui v4.5-dev - A simple and easy-to-use immediate-mode gui library
+*
+*   DESCRIPTION:
+*       raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
+*       available as a standalone library, as long as input and drawing functions are provided
+*
+*   FEATURES:
+*       - Immediate-mode gui, minimal retained data
+*       - +25 controls provided (basic and advanced)
+*       - Styling system for colors, font and metrics
+*       - Icons supported, embedded as a 1-bit icons pack
+*       - Standalone mode option (custom input/graphics backend)
+*       - Multiple support tools provided for raygui development
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Better standalone mode API for easy plug of custom backends
+*       - Externalize required inputs, allow user easier customization
+*
+*   LIMITATIONS:
+*       - No editable multi-line word-wraped text box supported
+*       - No auto-layout mechanism, up to the user to define controls position and size
+*       - Standalone mode requires library modification and some user work to plug another backend
+*
+*   NOTES:
+*       - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
+*         font atlas recs and glyphs, freeing that memory is (usually) up to the user,
+*         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
+*         by default any previously loaded font (texture, recs, glyphs)
+*       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
+*
+*   CONTROLS PROVIDED:
+*     # Container/separators Controls
+*       - WindowBox     --> StatusBar, Panel
+*       - GroupBox      --> Line
+*       - Line
+*       - Panel         --> StatusBar
+*       - ScrollPanel   --> StatusBar
+*       - TabBar        --> Button
+*
+*     # Basic Controls
+*       - Label
+*       - LabelButton   --> Label
+*       - Button
+*       - Toggle
+*       - ToggleGroup   --> Toggle
+*       - ToggleSlider
+*       - CheckBox
+*       - ComboBox
+*       - DropdownBox
+*       - TextBox
+*       - ValueBox      --> TextBox
+*       - Spinner       --> Button, ValueBox
+*       - Slider
+*       - SliderBar     --> Slider
+*       - ProgressBar
+*       - StatusBar
+*       - DummyRec
+*       - Grid
+*
+*     # Advance Controls
+*       - ListView
+*       - ColorPicker   --> ColorPanel, ColorBarHue
+*       - MessageBox    --> Window, Label, Button
+*       - TextInputBox  --> Window, Label, TextBox, Button
+*
+*     It also provides a set of functions for styling the controls based on its properties (size, color)
+*
+*
+*   RAYGUI STYLE (guiStyle):
+*       raygui uses a global data array for all gui style properties (allocated on data segment by default),
+*       when a new style is loaded, it is loaded over the global style... but a default gui style could always be
+*       recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one
+*
+*       The global style array size is fixed and depends on the number of controls and properties:
+*
+*           static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
+*
+*       guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
+*
+*       Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
+*       used for all controls, when any of those base values is set, it is automatically populated to all
+*       controls, so, specific control values overwriting generic style should be set after base values
+*
+*       After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
+*       properties are actually common to all controls and can not be overwritten individually (like BASE ones)
+*       Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR
+*
+*       Custom control properties can be defined using the EXTENDED properties for each independent control.
+*
+*       TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler
+*
+*
+*   RAYGUI ICONS (guiIcons):
+*       raygui could use a global array containing icons data (allocated on data segment by default),
+*       a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set
+*       must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded
+*
+*       Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
+*       requires 8 integers (16*16/32) to be stored in memory.
+*
+*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
+*
+*       The global icons array size is fixed and depends on the number of icons and size:
+*
+*           static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS];
+*
+*       guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+*
+*       TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons
+*
+*   RAYGUI LAYOUT:
+*       raygui currently does not provide an auto-layout mechanism like other libraries,
+*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
+*
+*       TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
+*
+*   CONFIGURATION:
+*       #define RAYGUI_IMPLEMENTATION
+*           Generates the implementation of the library into the included file
+*           If not defined, the library is in header only mode and can be included in other headers
+*           or source files without problems. But only ONE file should hold the implementation
+*
+*       #define RAYGUI_STANDALONE
+*           Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
+*           internally in the library and input management and drawing functions must be provided by
+*           the user (check library implementation for further details)
+*
+*       #define RAYGUI_NO_ICONS
+*           Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
+*
+*       #define RAYGUI_CUSTOM_ICONS
+*           Includes custom ricons.h header defining a set of custom icons,
+*           this file can be generated using rGuiIcons tool
+*
+*       #define RAYGUI_DEBUG_RECS_BOUNDS
+*           Draw control bounds rectangles for debug
+*
+*       #define RAYGUI_DEBUG_TEXT_BOUNDS
+*           Draw text bounds rectangles for debug
+*
+*   VERSIONS HISTORY:
+*       5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
+*                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
+*                         ADDED: GuiValueBoxFloat()
+*                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
+*                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: GuiLoadIconsFromMemory()
+*                         ADDED: Multiple new icons
+*                         REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+*                         REMOVED: GuiSliderPro(), functionality was redundant
+*                         REVIEWED: Controls using text labels to use LABEL properties
+*                         REVIEWED: Replaced sprintf() by snprintf() for more safety
+*                         REVIEWED: GuiTabBar(), close tab with mouse middle button
+*                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
+*                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
+*                         REVIEWED: GuiTextBox(), cursor position initialization
+*                         REVIEWED: GuiSliderPro(), control value change check
+*                         REVIEWED: GuiGrid(), simplified implementation
+*                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
+*                         REVIEWED: GuiDrawText(), improved wrap mode drawing
+*                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: GuiProgressBar(), improved borders computing
+*                         REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
+*                         REVIEWED: Functions descriptions, removed wrong return value reference
+*                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
+*
+*       4.0 (12-Sep-2023) ADDED: GuiToggleSlider()
+*                         ADDED: GuiColorPickerHSV() and GuiColorPanelHSV()
+*                         ADDED: Multiple new icons, mostly compiler related
+*                         ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE
+*                         ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode
+*                         ADDED: Support loading styles with custom font charset from external file
+*                         REDESIGNED: GuiTextBox(), support mouse cursor positioning
+*                         REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only)
+*                         REDESIGNED: GuiProgressBar() to be more visual, progress affects border color
+*                         REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText()
+*                         REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value
+*                         REDESIGNED: GuiComboBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiCheckBox(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSlider(), get parameters by reference and return result value
+*                         REDESIGNED: GuiSliderBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiProgressBar(), get parameters by reference and return result value
+*                         REDESIGNED: GuiListView(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPicker(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorPanel(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value
+*                         REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), get parameters by reference and return result value
+*                         REDESIGNED: GuiGrid(), added extra parameter
+*                         REDESIGNED: GuiListViewEx(), change parameters order
+*                         REDESIGNED: All controls return result as int value
+*                         REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars
+*                         REVIEWED: All examples and specially controls_test_suite
+*                         RENAMED: gui_file_dialog module to gui_window_file_dialog
+*                         UPDATED: All styles to include ISO-8859-15 charset (as much as possible)
+*
+*       3.6 (10-May-2023) ADDED: New icon: SAND_TIMER
+*                         ADDED: GuiLoadStyleFromMemory() (binary only)
+*                         REVIEWED: GuiScrollBar() horizontal movement key
+*                         REVIEWED: GuiTextBox() crash on cursor movement
+*                         REVIEWED: GuiTextBox(), additional inputs support
+*                         REVIEWED: GuiLabelButton(), avoid text cut
+*                         REVIEWED: GuiTextInputBox(), password input
+*                         REVIEWED: Local GetCodepointNext(), aligned with raylib
+*                         REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds
+*
+*       3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle()
+*                         ADDED: Helper functions to split text in separate lines
+*                         ADDED: Multiple new icons, useful for code editing tools
+*                         REMOVED: Unneeded icon editing functions
+*                         REMOVED: GuiTextBoxMulti(), very limited and broken
+*                         REMOVED: MeasureTextEx() dependency, logic directly implemented
+*                         REMOVED: DrawTextEx() dependency, logic directly implemented
+*                         REVIEWED: GuiScrollBar(), improve mouse-click behaviour
+*                         REVIEWED: Library header info, more info, better organized
+*                         REDESIGNED: GuiTextBox() to support cursor movement
+*                         REDESIGNED: GuiDrawText() to divide drawing by lines
+*
+*       3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes
+*                         REMOVED: GuiScrollBar(), only internal
+*                         REDESIGNED: GuiPanel() to support text parameter
+*                         REDESIGNED: GuiScrollPanel() to support text parameter
+*                         REDESIGNED: GuiColorPicker() to support text parameter
+*                         REDESIGNED: GuiColorPanel() to support text parameter
+*                         REDESIGNED: GuiColorBarAlpha() to support text parameter
+*                         REDESIGNED: GuiColorBarHue() to support text parameter
+*                         REDESIGNED: GuiTextInputBox() to support password
+*
+*       3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool)
+*                         REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures
+*                         REVIEWED: External icons usage logic
+*                         REVIEWED: GuiLine() for centered alignment when including text
+*                         RENAMED: Multiple controls properties definitions to prepend RAYGUI_
+*                         RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency
+*                         Projects updated and multiple tweaks
+*
+*       3.0 (04-Nov-2021) Integrated ricons data to avoid external file
+*                         REDESIGNED: GuiTextBoxMulti()
+*                         REMOVED: GuiImageButton*()
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.9 (17-Mar-2021) REMOVED: Tooltip API
+*       2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle()
+*       2.7 (20-Feb-2020) ADDED: Possible tooltips API
+*       2.6 (09-Sep-2019) ADDED: GuiTextInputBox()
+*                         REDESIGNED: GuiListView*(), GuiDropdownBox(), GuiSlider*(), GuiProgressBar(), GuiMessageBox()
+*                         REVIEWED: GuiTextBox(), GuiSpinner(), GuiValueBox(), GuiLoadStyle()
+*                         Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties
+*                         ADDED: 8 new custom styles ready to use
+*                         Multiple minor tweaks and bugs corrected
+*
+*       2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner()
+*       2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed
+*                         Refactor all controls drawing mechanism to use control state
+*       2.2 (05-Feb-2019) ADDED: GuiScrollBar(), GuiScrollPanel(), reviewed GuiListView(), removed Gui*Ex() controls
+*       2.1 (26-Dec-2018) REDESIGNED: GuiCheckBox(), GuiComboBox(), GuiDropdownBox(), GuiToggleGroup() > Use combined text string
+*                         REDESIGNED: Style system (breaking change)
+*       2.0 (08-Nov-2018) ADDED: Support controls guiLock and custom fonts
+*                         REVIEWED: GuiComboBox(), GuiListView()...
+*       1.9 (09-Oct-2018) REVIEWED: GuiGrid(), GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()...
+*       1.8 (01-May-2018) Lot of rework and redesign to align with rGuiStyler and rGuiLayout
+*       1.5 (21-Jun-2017) Working in an improved styles system
+*       1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
+*       1.3 (12-Jun-2017) Complete redesign of style system
+*       1.1 (01-Jun-2017) Complete review of the library
+*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
+*
+*   DEPENDENCIES:
+*       raylib 5.6-dev  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+*
+*   STANDALONE MODE:
+*       By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
+*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
+*
+*       The following functions should be redefined for a custom backend:
+*
+*           - Vector2 GetMousePosition(void);
+*           - float GetMouseWheelMove(void);
+*           - bool IsMouseButtonDown(int button);
+*           - bool IsMouseButtonPressed(int button);
+*           - bool IsMouseButtonReleased(int button);
+*           - bool IsKeyDown(int key);
+*           - bool IsKeyPressed(int key);
+*           - int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+*
+*           - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle()
+*           - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+*
+*           - Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+*           - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle()
+*           - Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+*           - void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+*           - char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+*           - void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+*           - const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+*           - int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+*           - void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+*           - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+*
+*   CONTRIBUTORS:
+*       Ramon Santamaria:   Supervision, review, redesign, update and maintenance
+*       Vlad Adrian:        Complete rewrite of GuiTextBox() to support extended features (2019)
+*       Sergio Martinez:    Review, testing (2015) and redesign of multiple controls (2018)
+*       Adria Arranz:       Testing and implementation of additional controls (2018)
+*       Jordi Jorba:        Testing and implementation of additional controls (2018)
+*       Albert Martos:      Review and testing of the library (2015)
+*       Ian Eito:           Review and testing of the library (2015)
+*       Kevin Gato:         Initial implementation of basic components (2014)
+*       Daniel Nicolas:     Initial implementation of basic components (2014)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RAYGUI_H
+#define RAYGUI_H
+
+#define RAYGUI_VERSION_MAJOR 4
+#define RAYGUI_VERSION_MINOR 5
+#define RAYGUI_VERSION_PATCH 0
+#define RAYGUI_VERSION  "5.0-dev"
+
+#if !defined(RAYGUI_STANDALONE)
+    #include "raylib.h"
+#endif
+
+// Function specifiers in case library is build/used as a shared library (Windows)
+// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
+#if defined(_WIN32)
+    #if defined(BUILD_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+    #elif defined(USE_LIBTYPE_SHARED)
+        #define RAYGUIAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+    #endif
+#endif
+
+// Function specifiers definition
+#ifndef RAYGUIAPI
+    #define RAYGUIAPI       // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define RAYGUI_SUPPORT_LOG_INFO
+#if defined(RAYGUI_SUPPORT_LOG_INFO)
+  #define RAYGUI_LOG(...)           printf(__VA_ARGS__)
+#else
+  #define RAYGUI_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+// NOTE: Some types are required for RAYGUI_STANDALONE usage
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+    #ifndef __cplusplus
+    // Boolean type
+        #ifndef true
+            typedef enum { false, true } bool;
+        #endif
+    #endif
+
+    // Vector2 type
+    typedef struct Vector2 {
+        float x;
+        float y;
+    } Vector2;
+
+    // Vector3 type                 // -- ConvertHSVtoRGB(), ConvertRGBtoHSV()
+    typedef struct Vector3 {
+        float x;
+        float y;
+        float z;
+    } Vector3;
+
+    // Color type, RGBA (32bit)
+    typedef struct Color {
+        unsigned char r;
+        unsigned char g;
+        unsigned char b;
+        unsigned char a;
+    } Color;
+
+    // Rectangle type
+    typedef struct Rectangle {
+        float x;
+        float y;
+        float width;
+        float height;
+    } Rectangle;
+
+    // TODO: Texture2D type is very coupled to raylib, required by Font type
+    // It should be redesigned to be provided by user
+    typedef struct Texture {
+        unsigned int id;        // OpenGL texture id
+        int width;              // Texture base width
+        int height;             // Texture base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Texture;
+
+    // Texture2D, same as Texture
+    typedef Texture Texture2D;
+
+    // Image, pixel data stored in CPU memory (RAM)
+    typedef struct Image {
+        void *data;             // Image raw data
+        int width;              // Image base width
+        int height;             // Image base height
+        int mipmaps;            // Mipmap levels, 1 by default
+        int format;             // Data format (PixelFormat type)
+    } Image;
+
+    // GlyphInfo, font characters glyphs info
+    typedef struct GlyphInfo {
+        int value;              // Character value (Unicode)
+        int offsetX;            // Character offset X when drawing
+        int offsetY;            // Character offset Y when drawing
+        int advanceX;           // Character advance position X
+        Image image;            // Character image data
+    } GlyphInfo;
+
+    // TODO: Font type is very coupled to raylib, mostly required by GuiLoadStyle()
+    // It should be redesigned to be provided by user
+    typedef struct Font {
+        int baseSize;           // Base size (default chars height)
+        int glyphCount;         // Number of glyph characters
+        int glyphPadding;       // Padding around the glyph characters
+        Texture2D texture;      // Texture atlas containing the glyphs
+        Rectangle *recs;        // Rectangles in texture for the glyphs
+        GlyphInfo *glyphs;      // Glyphs info data
+    } Font;
+#endif
+
+// Style property
+// NOTE: Used when exporting style as code for convenience
+typedef struct GuiStyleProp {
+    unsigned short controlId;   // Control identifier
+    unsigned short propertyId;  // Property identifier
+    int propertyValue;          // Property value
+} GuiStyleProp;
+
+/*
+// Controls text style -NOT USED-
+// NOTE: Text style is defined by control
+typedef struct GuiTextStyle {
+    unsigned int size;
+    int charSpacing;
+    int lineSpacing;
+    int alignmentH;
+    int alignmentV;
+    int padding;
+} GuiTextStyle;
+*/
+
+// Gui control state
+typedef enum {
+    STATE_NORMAL = 0,
+    STATE_FOCUSED,
+    STATE_PRESSED,
+    STATE_DISABLED
+} GuiState;
+
+// Gui control text alignment
+typedef enum {
+    TEXT_ALIGN_LEFT = 0,
+    TEXT_ALIGN_CENTER,
+    TEXT_ALIGN_RIGHT
+} GuiTextAlignment;
+
+// Gui control text alignment vertical
+// NOTE: Text vertical position inside the text bounds
+typedef enum {
+    TEXT_ALIGN_TOP = 0,
+    TEXT_ALIGN_MIDDLE,
+    TEXT_ALIGN_BOTTOM
+} GuiTextAlignmentVertical;
+
+// Gui control text wrap mode
+// NOTE: Useful for multiline text
+typedef enum {
+    TEXT_WRAP_NONE = 0,
+    TEXT_WRAP_CHAR,
+    TEXT_WRAP_WORD
+} GuiTextWrapMode;
+
+// Gui controls
+typedef enum {
+    // Default -> populates to all controls when set
+    DEFAULT = 0,
+
+    // Basic controls
+    LABEL,          // Used also for: LABELBUTTON
+    BUTTON,
+    TOGGLE,         // Used also for: TOGGLEGROUP
+    SLIDER,         // Used also for: SLIDERBAR, TOGGLESLIDER
+    PROGRESSBAR,
+    CHECKBOX,
+    COMBOBOX,
+    DROPDOWNBOX,
+    TEXTBOX,        // Used also for: TEXTBOXMULTI
+    VALUEBOX,
+    CONTROL11,
+    LISTVIEW,
+    COLORPICKER,
+    SCROLLBAR,
+    STATUSBAR
+} GuiControl;
+
+// Gui base properties for every control
+// NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties)
+typedef enum {
+    BORDER_COLOR_NORMAL = 0,    // Control border color in STATE_NORMAL
+    BASE_COLOR_NORMAL,          // Control base color in STATE_NORMAL
+    TEXT_COLOR_NORMAL,          // Control text color in STATE_NORMAL
+    BORDER_COLOR_FOCUSED,       // Control border color in STATE_FOCUSED
+    BASE_COLOR_FOCUSED,         // Control base color in STATE_FOCUSED
+    TEXT_COLOR_FOCUSED,         // Control text color in STATE_FOCUSED
+    BORDER_COLOR_PRESSED,       // Control border color in STATE_PRESSED
+    BASE_COLOR_PRESSED,         // Control base color in STATE_PRESSED
+    TEXT_COLOR_PRESSED,         // Control text color in STATE_PRESSED
+    BORDER_COLOR_DISABLED,      // Control border color in STATE_DISABLED
+    BASE_COLOR_DISABLED,        // Control base color in STATE_DISABLED
+    TEXT_COLOR_DISABLED,        // Control text color in STATE_DISABLED
+    BORDER_WIDTH = 12,          // Control border size, 0 for no border
+    //TEXT_SIZE,                  // Control text size (glyphs max height) -> GLOBAL for all controls
+    //TEXT_SPACING,               // Control text spacing between glyphs -> GLOBAL for all controls
+    //TEXT_LINE_SPACING,          // Control text spacing between lines -> GLOBAL for all controls
+    TEXT_PADDING = 13,          // Control text padding, not considering border
+    TEXT_ALIGNMENT = 14,        // Control text horizontal alignment inside control text bound (after border and padding)
+    //TEXT_WRAP_MODE              // Control text wrap-mode inside text bounds -> GLOBAL for all controls
+} GuiControlProperty;
+
+// TODO: Which text styling properties should be global or per-control?
+// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while
+// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and
+// should be configured by user as needed while defining the UI layout
+
+// Gui extended properties depend on control
+// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties)
+//----------------------------------------------------------------------------------
+// DEFAULT extended properties
+// NOTE: Those properties are common to all controls or global
+// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT?
+typedef enum {
+    TEXT_SIZE = 16,             // Text size (glyphs max height)
+    TEXT_SPACING,               // Text spacing between glyphs
+    LINE_COLOR,                 // Line control color
+    BACKGROUND_COLOR,           // Background color
+    TEXT_LINE_SPACING,          // Text spacing between lines
+    TEXT_ALIGNMENT_VERTICAL,    // Text vertical alignment inside text bounds (after border and padding)
+    TEXT_WRAP_MODE              // Text wrap-mode inside text bounds
+    //TEXT_DECORATION             // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline
+    //TEXT_DECORATION_THICK       // Text decoration line thickness
+} GuiDefaultProperty;
+
+// Other possible text properties:
+// TEXT_WEIGHT                  // Normal, Italic, Bold -> Requires specific font change
+// TEXT_INDENT                  // Text indentation -> Now using TEXT_PADDING...
+
+// Label
+//typedef enum { } GuiLabelProperty;
+
+// Button/Spinner
+//typedef enum { } GuiButtonProperty;
+
+// Toggle/ToggleGroup
+typedef enum {
+    GROUP_PADDING = 16,         // ToggleGroup separation between toggles
+} GuiToggleProperty;
+
+// Slider/SliderBar
+typedef enum {
+    SLIDER_WIDTH = 16,          // Slider size of internal bar
+    SLIDER_PADDING              // Slider/SliderBar internal bar padding
+} GuiSliderProperty;
+
+// ProgressBar
+typedef enum {
+    PROGRESS_PADDING = 16,      // ProgressBar internal padding
+} GuiProgressBarProperty;
+
+// ScrollBar
+typedef enum {
+    ARROWS_SIZE = 16,           // ScrollBar arrows size
+    ARROWS_VISIBLE,             // ScrollBar arrows visible
+    SCROLL_SLIDER_PADDING,      // ScrollBar slider internal padding
+    SCROLL_SLIDER_SIZE,         // ScrollBar slider size
+    SCROLL_PADDING,             // ScrollBar scroll padding from arrows
+    SCROLL_SPEED,               // ScrollBar scrolling speed
+} GuiScrollBarProperty;
+
+// CheckBox
+typedef enum {
+    CHECK_PADDING = 16          // CheckBox internal check padding
+} GuiCheckBoxProperty;
+
+// ComboBox
+typedef enum {
+    COMBO_BUTTON_WIDTH = 16,    // ComboBox right button width
+    COMBO_BUTTON_SPACING        // ComboBox button separation
+} GuiComboBoxProperty;
+
+// DropdownBox
+typedef enum {
+    ARROW_PADDING = 16,         // DropdownBox arrow separation from border and items
+    DROPDOWN_ITEMS_SPACING,     // DropdownBox items separation
+    DROPDOWN_ARROW_HIDDEN,      // DropdownBox arrow hidden
+    DROPDOWN_ROLL_UP            // DropdownBox roll up flag (default rolls down)
+} GuiDropdownBoxProperty;
+
+// TextBox/TextBoxMulti/ValueBox/Spinner
+typedef enum {
+    TEXT_READONLY = 16,         // TextBox in read-only mode: 0-text editable, 1-text no-editable
+} GuiTextBoxProperty;
+
+// ValueBox/Spinner
+typedef enum {
+    SPINNER_BUTTON_WIDTH = 16,  // Spinner left/right buttons width
+    SPINNER_BUTTON_SPACING,     // Spinner buttons separation
+} GuiValueBoxProperty;
+
+// Control11
+//typedef enum { } GuiControl11Property;
+
+// ListView
+typedef enum {
+    LIST_ITEMS_HEIGHT = 16,     // ListView items height
+    LIST_ITEMS_SPACING,         // ListView items separation
+    SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
+    SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_NORMAL,   // ListView items border enabled in normal state
+    LIST_ITEMS_BORDER_WIDTH     // ListView items border width
+} GuiListViewProperty;
+
+// ColorPicker
+typedef enum {
+    COLOR_SELECTOR_SIZE = 16,
+    HUEBAR_WIDTH,               // ColorPicker right hue bar width
+    HUEBAR_PADDING,             // ColorPicker right hue bar separation from panel
+    HUEBAR_SELECTOR_HEIGHT,     // ColorPicker right hue bar selector height
+    HUEBAR_SELECTOR_OVERFLOW    // ColorPicker right hue bar selector overflow
+} GuiColorPickerProperty;
+
+#define SCROLLBAR_LEFT_SIDE     0
+#define SCROLLBAR_RIGHT_SIDE    1
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+#if defined(__cplusplus)
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+// Global gui state control functions
+RAYGUIAPI void GuiEnable(void);                                 // Enable gui controls (global state)
+RAYGUIAPI void GuiDisable(void);                                // Disable gui controls (global state)
+RAYGUIAPI void GuiLock(void);                                   // Lock gui controls (global state)
+RAYGUIAPI void GuiUnlock(void);                                 // Unlock gui controls (global state)
+RAYGUIAPI bool GuiIsLocked(void);                               // Check if gui is locked (global state)
+RAYGUIAPI void GuiSetAlpha(float alpha);                        // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f
+RAYGUIAPI void GuiSetState(int state);                          // Set gui state (global state)
+RAYGUIAPI int GuiGetState(void);                                // Get gui state (global state)
+
+// Font set/get functions
+RAYGUIAPI void GuiSetFont(Font font);                           // Set gui custom font (global state)
+RAYGUIAPI Font GuiGetFont(void);                                // Get gui custom font (global state)
+
+// Style set/get functions
+RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property
+RAYGUIAPI int GuiGetStyle(int control, int property);           // Get one style property
+
+// Styles loading functions
+RAYGUIAPI void GuiLoadStyle(const char *fileName);              // Load style file over global style variable (.rgs)
+RAYGUIAPI void GuiLoadStyleDefault(void);                       // Load style default over global style
+
+// Tooltips management functions
+RAYGUIAPI void GuiEnableTooltip(void);                          // Enable gui tooltips (global state)
+RAYGUIAPI void GuiDisableTooltip(void);                         // Disable gui tooltips (global state)
+RAYGUIAPI void GuiSetTooltip(const char *tooltip);              // Set tooltip string
+
+// Icons functionality
+RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported)
+#if !defined(RAYGUI_NO_ICONS)
+RAYGUIAPI void GuiSetIconScale(int scale);                      // Set default icon drawing size
+RAYGUIAPI unsigned int *GuiGetIcons(void);                      // Get raygui icons data pointer
+RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data
+RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
+#endif
+
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text);                // Get text width considering gui style and icon size (if required)
+
+// Controls
+//----------------------------------------------------------------------------------------------------------
+// Container/separator controls, useful for controls organization
+RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title);                                       // Window Box control, shows a window that can be closed
+RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text);                                         // Group Box control with text name
+RAYGUIAPI int GuiLine(Rectangle bounds, const char *text);                                             // Line separator control, could contain text
+RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text);                                            // Panel control, useful to group controls
+RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active);                  // Tab Bar control, returns TAB to be closed or -1
+RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control
+
+// Basic controls set
+RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text);                                            // Label control
+RAYGUIAPI int GuiButton(Rectangle bounds, const char *text);                                           // Button control, returns true when clicked
+RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text);                                      // Label button control, returns true when clicked
+RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active);                             // Toggle Button control
+RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active);                         // Toggle Group control
+RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active);                        // Toggle Slider control
+RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked);                          // Check Box control, returns true when active
+RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active);                            // Combo Box control
+
+RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode);          // Dropdown Box control
+RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control
+RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers
+RAYGUIAPI int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode); // Value box control for float values
+RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode);                   // Text Box control, updates input text
+
+RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control
+RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control
+RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control
+RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text);                                        // Status Bar control, shows info text
+RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text);                                         // Dummy control for placeholders
+RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control
+
+// Advance controls set
+RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active);          // List View control
+RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters
+RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message
+RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret
+RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color);                        // Color Picker control (multiple color controls)
+RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color);                         // Color Panel control
+RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha);                      // Color Bar Alpha control
+RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value);                        // Color Bar Hue control
+RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                // Color Picker control that avoids conversion to RGB on each call (multiple color controls)
+RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv);                 // Color Panel control that updates Hue-Saturation-Value color value, used by GuiColorPickerHSV()
+//----------------------------------------------------------------------------------------------------------
+
+#if !defined(RAYGUI_NO_ICONS)
+
+#if !defined(RAYGUI_CUSTOM_ICONS)
+//----------------------------------------------------------------------------------
+// Icons enumeration
+//----------------------------------------------------------------------------------
+typedef enum {
+    ICON_NONE                     = 0,
+    ICON_FOLDER_FILE_OPEN         = 1,
+    ICON_FILE_SAVE_CLASSIC        = 2,
+    ICON_FOLDER_OPEN              = 3,
+    ICON_FOLDER_SAVE              = 4,
+    ICON_FILE_OPEN                = 5,
+    ICON_FILE_SAVE                = 6,
+    ICON_FILE_EXPORT              = 7,
+    ICON_FILE_ADD                 = 8,
+    ICON_FILE_DELETE              = 9,
+    ICON_FILETYPE_TEXT            = 10,
+    ICON_FILETYPE_AUDIO           = 11,
+    ICON_FILETYPE_IMAGE           = 12,
+    ICON_FILETYPE_PLAY            = 13,
+    ICON_FILETYPE_VIDEO           = 14,
+    ICON_FILETYPE_INFO            = 15,
+    ICON_FILE_COPY                = 16,
+    ICON_FILE_CUT                 = 17,
+    ICON_FILE_PASTE               = 18,
+    ICON_CURSOR_HAND              = 19,
+    ICON_CURSOR_POINTER           = 20,
+    ICON_CURSOR_CLASSIC           = 21,
+    ICON_PENCIL                   = 22,
+    ICON_PENCIL_BIG               = 23,
+    ICON_BRUSH_CLASSIC            = 24,
+    ICON_BRUSH_PAINTER            = 25,
+    ICON_WATER_DROP               = 26,
+    ICON_COLOR_PICKER             = 27,
+    ICON_RUBBER                   = 28,
+    ICON_COLOR_BUCKET             = 29,
+    ICON_TEXT_T                   = 30,
+    ICON_TEXT_A                   = 31,
+    ICON_SCALE                    = 32,
+    ICON_RESIZE                   = 33,
+    ICON_FILTER_POINT             = 34,
+    ICON_FILTER_BILINEAR          = 35,
+    ICON_CROP                     = 36,
+    ICON_CROP_ALPHA               = 37,
+    ICON_SQUARE_TOGGLE            = 38,
+    ICON_SYMMETRY                 = 39,
+    ICON_SYMMETRY_HORIZONTAL      = 40,
+    ICON_SYMMETRY_VERTICAL        = 41,
+    ICON_LENS                     = 42,
+    ICON_LENS_BIG                 = 43,
+    ICON_EYE_ON                   = 44,
+    ICON_EYE_OFF                  = 45,
+    ICON_FILTER_TOP               = 46,
+    ICON_FILTER                   = 47,
+    ICON_TARGET_POINT             = 48,
+    ICON_TARGET_SMALL             = 49,
+    ICON_TARGET_BIG               = 50,
+    ICON_TARGET_MOVE              = 51,
+    ICON_CURSOR_MOVE              = 52,
+    ICON_CURSOR_SCALE             = 53,
+    ICON_CURSOR_SCALE_RIGHT       = 54,
+    ICON_CURSOR_SCALE_LEFT        = 55,
+    ICON_UNDO                     = 56,
+    ICON_REDO                     = 57,
+    ICON_REREDO                   = 58,
+    ICON_MUTATE                   = 59,
+    ICON_ROTATE                   = 60,
+    ICON_REPEAT                   = 61,
+    ICON_SHUFFLE                  = 62,
+    ICON_EMPTYBOX                 = 63,
+    ICON_TARGET                   = 64,
+    ICON_TARGET_SMALL_FILL        = 65,
+    ICON_TARGET_BIG_FILL          = 66,
+    ICON_TARGET_MOVE_FILL         = 67,
+    ICON_CURSOR_MOVE_FILL         = 68,
+    ICON_CURSOR_SCALE_FILL        = 69,
+    ICON_CURSOR_SCALE_RIGHT_FILL  = 70,
+    ICON_CURSOR_SCALE_LEFT_FILL   = 71,
+    ICON_UNDO_FILL                = 72,
+    ICON_REDO_FILL                = 73,
+    ICON_REREDO_FILL              = 74,
+    ICON_MUTATE_FILL              = 75,
+    ICON_ROTATE_FILL              = 76,
+    ICON_REPEAT_FILL              = 77,
+    ICON_SHUFFLE_FILL             = 78,
+    ICON_EMPTYBOX_SMALL           = 79,
+    ICON_BOX                      = 80,
+    ICON_BOX_TOP                  = 81,
+    ICON_BOX_TOP_RIGHT            = 82,
+    ICON_BOX_RIGHT                = 83,
+    ICON_BOX_BOTTOM_RIGHT         = 84,
+    ICON_BOX_BOTTOM               = 85,
+    ICON_BOX_BOTTOM_LEFT          = 86,
+    ICON_BOX_LEFT                 = 87,
+    ICON_BOX_TOP_LEFT             = 88,
+    ICON_BOX_CENTER               = 89,
+    ICON_BOX_CIRCLE_MASK          = 90,
+    ICON_POT                      = 91,
+    ICON_ALPHA_MULTIPLY           = 92,
+    ICON_ALPHA_CLEAR              = 93,
+    ICON_DITHERING                = 94,
+    ICON_MIPMAPS                  = 95,
+    ICON_BOX_GRID                 = 96,
+    ICON_GRID                     = 97,
+    ICON_BOX_CORNERS_SMALL        = 98,
+    ICON_BOX_CORNERS_BIG          = 99,
+    ICON_FOUR_BOXES               = 100,
+    ICON_GRID_FILL                = 101,
+    ICON_BOX_MULTISIZE            = 102,
+    ICON_ZOOM_SMALL               = 103,
+    ICON_ZOOM_MEDIUM              = 104,
+    ICON_ZOOM_BIG                 = 105,
+    ICON_ZOOM_ALL                 = 106,
+    ICON_ZOOM_CENTER              = 107,
+    ICON_BOX_DOTS_SMALL           = 108,
+    ICON_BOX_DOTS_BIG             = 109,
+    ICON_BOX_CONCENTRIC           = 110,
+    ICON_BOX_GRID_BIG             = 111,
+    ICON_OK_TICK                  = 112,
+    ICON_CROSS                    = 113,
+    ICON_ARROW_LEFT               = 114,
+    ICON_ARROW_RIGHT              = 115,
+    ICON_ARROW_DOWN               = 116,
+    ICON_ARROW_UP                 = 117,
+    ICON_ARROW_LEFT_FILL          = 118,
+    ICON_ARROW_RIGHT_FILL         = 119,
+    ICON_ARROW_DOWN_FILL          = 120,
+    ICON_ARROW_UP_FILL            = 121,
+    ICON_AUDIO                    = 122,
+    ICON_FX                       = 123,
+    ICON_WAVE                     = 124,
+    ICON_WAVE_SINUS               = 125,
+    ICON_WAVE_SQUARE              = 126,
+    ICON_WAVE_TRIANGULAR          = 127,
+    ICON_CROSS_SMALL              = 128,
+    ICON_PLAYER_PREVIOUS          = 129,
+    ICON_PLAYER_PLAY_BACK         = 130,
+    ICON_PLAYER_PLAY              = 131,
+    ICON_PLAYER_PAUSE             = 132,
+    ICON_PLAYER_STOP              = 133,
+    ICON_PLAYER_NEXT              = 134,
+    ICON_PLAYER_RECORD            = 135,
+    ICON_MAGNET                   = 136,
+    ICON_LOCK_CLOSE               = 137,
+    ICON_LOCK_OPEN                = 138,
+    ICON_CLOCK                    = 139,
+    ICON_TOOLS                    = 140,
+    ICON_GEAR                     = 141,
+    ICON_GEAR_BIG                 = 142,
+    ICON_BIN                      = 143,
+    ICON_HAND_POINTER             = 144,
+    ICON_LASER                    = 145,
+    ICON_COIN                     = 146,
+    ICON_EXPLOSION                = 147,
+    ICON_1UP                      = 148,
+    ICON_PLAYER                   = 149,
+    ICON_PLAYER_JUMP              = 150,
+    ICON_KEY                      = 151,
+    ICON_DEMON                    = 152,
+    ICON_TEXT_POPUP               = 153,
+    ICON_GEAR_EX                  = 154,
+    ICON_CRACK                    = 155,
+    ICON_CRACK_POINTS             = 156,
+    ICON_STAR                     = 157,
+    ICON_DOOR                     = 158,
+    ICON_EXIT                     = 159,
+    ICON_MODE_2D                  = 160,
+    ICON_MODE_3D                  = 161,
+    ICON_CUBE                     = 162,
+    ICON_CUBE_FACE_TOP            = 163,
+    ICON_CUBE_FACE_LEFT           = 164,
+    ICON_CUBE_FACE_FRONT          = 165,
+    ICON_CUBE_FACE_BOTTOM         = 166,
+    ICON_CUBE_FACE_RIGHT          = 167,
+    ICON_CUBE_FACE_BACK           = 168,
+    ICON_CAMERA                   = 169,
+    ICON_SPECIAL                  = 170,
+    ICON_LINK_NET                 = 171,
+    ICON_LINK_BOXES               = 172,
+    ICON_LINK_MULTI               = 173,
+    ICON_LINK                     = 174,
+    ICON_LINK_BROKE               = 175,
+    ICON_TEXT_NOTES               = 176,
+    ICON_NOTEBOOK                 = 177,
+    ICON_SUITCASE                 = 178,
+    ICON_SUITCASE_ZIP             = 179,
+    ICON_MAILBOX                  = 180,
+    ICON_MONITOR                  = 181,
+    ICON_PRINTER                  = 182,
+    ICON_PHOTO_CAMERA             = 183,
+    ICON_PHOTO_CAMERA_FLASH       = 184,
+    ICON_HOUSE                    = 185,
+    ICON_HEART                    = 186,
+    ICON_CORNER                   = 187,
+    ICON_VERTICAL_BARS            = 188,
+    ICON_VERTICAL_BARS_FILL       = 189,
+    ICON_LIFE_BARS                = 190,
+    ICON_INFO                     = 191,
+    ICON_CROSSLINE                = 192,
+    ICON_HELP                     = 193,
+    ICON_FILETYPE_ALPHA           = 194,
+    ICON_FILETYPE_HOME            = 195,
+    ICON_LAYERS_VISIBLE           = 196,
+    ICON_LAYERS                   = 197,
+    ICON_WINDOW                   = 198,
+    ICON_HIDPI                    = 199,
+    ICON_FILETYPE_BINARY          = 200,
+    ICON_HEX                      = 201,
+    ICON_SHIELD                   = 202,
+    ICON_FILE_NEW                 = 203,
+    ICON_FOLDER_ADD               = 204,
+    ICON_ALARM                    = 205,
+    ICON_CPU                      = 206,
+    ICON_ROM                      = 207,
+    ICON_STEP_OVER                = 208,
+    ICON_STEP_INTO                = 209,
+    ICON_STEP_OUT                 = 210,
+    ICON_RESTART                  = 211,
+    ICON_BREAKPOINT_ON            = 212,
+    ICON_BREAKPOINT_OFF           = 213,
+    ICON_BURGER_MENU              = 214,
+    ICON_CASE_SENSITIVE           = 215,
+    ICON_REG_EXP                  = 216,
+    ICON_FOLDER                   = 217,
+    ICON_FILE                     = 218,
+    ICON_SAND_TIMER               = 219,
+    ICON_WARNING                  = 220,
+    ICON_HELP_BOX                 = 221,
+    ICON_INFO_BOX                 = 222,
+    ICON_PRIORITY                 = 223,
+    ICON_LAYERS_ISO               = 224,
+    ICON_LAYERS2                  = 225,
+    ICON_MLAYERS                  = 226,
+    ICON_MAPS                     = 227,
+    ICON_HOT                      = 228,
+    ICON_LABEL                    = 229,
+    ICON_NAME_ID                  = 230,
+    ICON_SLICING                  = 231,
+    ICON_MANUAL_CONTROL           = 232,
+    ICON_COLLISION                = 233,
+    ICON_CIRCLE_ADD               = 234,
+    ICON_CIRCLE_ADD_FILL          = 235,
+    ICON_CIRCLE_WARNING           = 236,
+    ICON_CIRCLE_WARNING_FILL      = 237,
+    ICON_BOX_MORE                 = 238,
+    ICON_BOX_MORE_FILL            = 239,
+    ICON_BOX_MINUS                = 240,
+    ICON_BOX_MINUS_FILL           = 241,
+    ICON_UNION                    = 242,
+    ICON_INTERSECTION             = 243,
+    ICON_DIFFERENCE               = 244,
+    ICON_SPHERE                   = 245,
+    ICON_CYLINDER                 = 246,
+    ICON_CONE                     = 247,
+    ICON_ELLIPSOID                = 248,
+    ICON_CAPSULE                  = 249,
+    ICON_250                      = 250,
+    ICON_251                      = 251,
+    ICON_252                      = 252,
+    ICON_253                      = 253,
+    ICON_254                      = 254,
+    ICON_255                      = 255
+} GuiIconName;
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+}            // Prevents name mangling of functions
+#endif
+
+#endif // RAYGUI_H
+
+/***********************************************************************************
+*
+*   RAYGUI IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RAYGUI_IMPLEMENTATION)
+
+#include <ctype.h>              // required for: isspace() [GuiTextBox()]
+#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
+#include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
+#include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
+#include <math.h>               // Required for: roundf() [GuiColorPicker()]
+
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+        #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+    #endif
+#else
+    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+    #define RAYGUI_MALLOC(sz)       malloc(sz)
+    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
+    #define RAYGUI_FREE(p)          free(p)
+#endif
+
+#ifdef __cplusplus
+    #define RAYGUI_CLITERAL(name) name
+#else
+    #define RAYGUI_CLITERAL(name) (name)
+#endif
+
+// Check if two rectangles are equal, used to validate a slider bounds as an id
+#ifndef CHECK_BOUNDS_ID
+    #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
+#endif
+
+#if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
+
+// Embedded icons, no external file provided
+#define RAYGUI_ICON_SIZE               16          // Size of icons in pixels (squared)
+#define RAYGUI_ICON_MAX_ICONS         256          // Maximum number of icons
+#define RAYGUI_ICON_MAX_NAME_LENGTH    32          // Maximum length of icon name id
+
+// Icons data is defined by bit array (every bit represents one pixel)
+// Those arrays are stored as unsigned int data arrays, so,
+// every array element defines 32 pixels (bits) of information
+// One icon is defined by 8 int, (8 int * 32 bit = 256 bit = 16*16 pixels)
+// NOTE: Number of elemens depend on RAYGUI_ICON_SIZE (by default 16x16 pixels)
+#define RAYGUI_ICON_DATA_ELEMENTS   (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32)
+
+//----------------------------------------------------------------------------------
+// Icons data for all gui possible icons (allocated on data segment by default)
+//
+// NOTE 1: Every icon is codified in binary form, using 1 bit per pixel, so,
+// every 16x16 icon requires 8 integers (16*16/32) to be stored
+//
+// NOTE 2: A different icon set could be loaded over this array using GuiLoadIcons(),
+// but loaded icons set must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS
+//
+// guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = {
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_NONE
+    0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe,      // ICON_FOLDER_FILE_OPEN
+    0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe,      // ICON_FILE_SAVE_CLASSIC
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100,      // ICON_FOLDER_OPEN
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000,      // ICON_FOLDER_SAVE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc,      // ICON_FILE_OPEN
+    0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc,      // ICON_FILE_SAVE
+    0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc,      // ICON_FILE_EXPORT
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc,      // ICON_FILE_ADD
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc,      // ICON_FILE_DELETE
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_TEXT
+    0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_AUDIO
+    0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_IMAGE
+    0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_PLAY
+    0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4,      // ICON_FILETYPE_VIDEO
+    0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc,      // ICON_FILETYPE_INFO
+    0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0,      // ICON_FILE_COPY
+    0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000,      // ICON_FILE_CUT
+    0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0,      // ICON_FILE_PASTE
+    0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_CURSOR_HAND
+    0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000,      // ICON_CURSOR_POINTER
+    0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000,      // ICON_CURSOR_CLASSIC
+    0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000,      // ICON_PENCIL
+    0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000,      // ICON_PENCIL_BIG
+    0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8,      // ICON_BRUSH_CLASSIC
+    0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080,      // ICON_BRUSH_PAINTER
+    0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000,      // ICON_WATER_DROP
+    0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000,      // ICON_COLOR_PICKER
+    0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000,      // ICON_RUBBER
+    0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040,      // ICON_COLOR_BUCKET
+    0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000,      // ICON_TEXT_T
+    0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f,      // ICON_TEXT_A
+    0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e,      // ICON_SCALE
+    0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe,      // ICON_RESIZE
+    0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_POINT
+    0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000,      // ICON_FILTER_BILINEAR
+    0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002,      // ICON_CROP
+    0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000,      // ICON_CROP_ALPHA
+    0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002,      // ICON_SQUARE_TOGGLE
+    0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000,      // ICON_SYMMETRY
+    0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100,      // ICON_SYMMETRY_HORIZONTAL
+    0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180,      // ICON_SYMMETRY_VERTICAL
+    0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000,      // ICON_LENS
+    0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000,      // ICON_LENS_BIG
+    0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000,      // ICON_EYE_ON
+    0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000,      // ICON_EYE_OFF
+    0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100,      // ICON_FILTER_TOP
+    0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0,      // ICON_FILTER
+    0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_POINT
+    0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL
+    0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG
+    0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280,      // ICON_TARGET_MOVE
+    0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280,      // ICON_CURSOR_MOVE
+    0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e,      // ICON_CURSOR_SCALE
+    0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT
+    0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000,      // ICON_CURSOR_SCALE_LEFT
+    0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO
+    0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000,      // ICON_REREDO
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000,      // ICON_MUTATE
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020,      // ICON_ROTATE
+    0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000,      // ICON_REPEAT
+    0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000,      // ICON_SHUFFLE
+    0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe,      // ICON_EMPTYBOX
+    0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000,      // ICON_TARGET
+    0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_SMALL_FILL
+    0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000,      // ICON_TARGET_BIG_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380,      // ICON_TARGET_MOVE_FILL
+    0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380,      // ICON_CURSOR_MOVE_FILL
+    0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e,      // ICON_CURSOR_SCALE_FILL
+    0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000,      // ICON_CURSOR_SCALE_RIGHT_FILL
+    0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000,      // ICON_CURSOR_SCALE_LEFT_FILL
+    0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000,      // ICON_UNDO_FILL
+    0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000,      // ICON_REDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000,      // ICON_REREDO_FILL
+    0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000,      // ICON_MUTATE_FILL
+    0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020,      // ICON_ROTATE_FILL
+    0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000,      // ICON_REPEAT_FILL
+    0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000,      // ICON_SHUFFLE_FILL
+    0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000,      // ICON_EMPTYBOX_SMALL
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX
+    0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP
+    0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000,      // ICON_BOX_BOTTOM_RIGHT
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000,      // ICON_BOX_BOTTOM
+    0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000,      // ICON_BOX_BOTTOM_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_LEFT
+    0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_TOP_LEFT
+    0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000,      // ICON_BOX_CENTER
+    0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe,      // ICON_BOX_CIRCLE_MASK
+    0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff,      // ICON_POT
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe,      // ICON_ALPHA_MULTIPLY
+    0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe,      // ICON_ALPHA_CLEAR
+    0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe,      // ICON_DITHERING
+    0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0,      // ICON_MIPMAPS
+    0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000,      // ICON_BOX_GRID
+    0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248,      // ICON_GRID
+    0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000,      // ICON_BOX_CORNERS_SMALL
+    0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e,      // ICON_BOX_CORNERS_BIG
+    0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000,      // ICON_FOUR_BOXES
+    0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000,      // ICON_GRID_FILL
+    0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e,      // ICON_BOX_MULTISIZE
+    0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_SMALL
+    0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e,      // ICON_ZOOM_MEDIUM
+    0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e,      // ICON_ZOOM_BIG
+    0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e,      // ICON_ZOOM_ALL
+    0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000,      // ICON_ZOOM_CENTER
+    0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000,      // ICON_BOX_DOTS_SMALL
+    0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000,      // ICON_BOX_DOTS_BIG
+    0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe,      // ICON_BOX_CONCENTRIC
+    0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000,      // ICON_BOX_GRID_BIG
+    0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000,      // ICON_OK_TICK
+    0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000,      // ICON_CROSS
+    0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000,      // ICON_ARROW_LEFT
+    0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000,      // ICON_ARROW_RIGHT
+    0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN
+    0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP
+    0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000,      // ICON_ARROW_LEFT_FILL
+    0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000,      // ICON_ARROW_RIGHT_FILL
+    0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000,      // ICON_ARROW_DOWN_FILL
+    0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000,      // ICON_ARROW_UP_FILL
+    0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000,      // ICON_AUDIO
+    0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000,      // ICON_FX
+    0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000,      // ICON_WAVE
+    0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000,      // ICON_WAVE_SINUS
+    0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000,      // ICON_WAVE_SQUARE
+    0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000,      // ICON_WAVE_TRIANGULAR
+    0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000,      // ICON_CROSS_SMALL
+    0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000,      // ICON_PLAYER_PREVIOUS
+    0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000,      // ICON_PLAYER_PLAY_BACK
+    0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000,      // ICON_PLAYER_PLAY
+    0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000,      // ICON_PLAYER_PAUSE
+    0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000,      // ICON_PLAYER_STOP
+    0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000,      // ICON_PLAYER_NEXT
+    0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000,      // ICON_PLAYER_RECORD
+    0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000,      // ICON_MAGNET
+    0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_CLOSE
+    0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000,      // ICON_LOCK_OPEN
+    0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770,      // ICON_CLOCK
+    0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70,      // ICON_TOOLS
+    0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180,      // ICON_GEAR
+    0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180,      // ICON_GEAR_BIG
+    0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8,      // ICON_BIN
+    0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000,      // ICON_HAND_POINTER
+    0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000,      // ICON_LASER
+    0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000,      // ICON_COIN
+    0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000,      // ICON_EXPLOSION
+    0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000,      // ICON_1UP
+    0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240,      // ICON_PLAYER
+    0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000,      // ICON_PLAYER_JUMP
+    0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0,      // ICON_KEY
+    0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0,      // ICON_DEMON
+    0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000,      // ICON_TEXT_POPUP
+    0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000,      // ICON_GEAR_EX
+    0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK
+    0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000,      // ICON_CRACK_POINTS
+    0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808,      // ICON_STAR
+    0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8,      // ICON_DOOR
+    0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0,      // ICON_EXIT
+    0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000,      // ICON_MODE_2D
+    0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000,      // ICON_MODE_3D
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE
+    0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_TOP
+    0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe,      // ICON_CUBE_FACE_LEFT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe,      // ICON_CUBE_FACE_FRONT
+    0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe,      // ICON_CUBE_FACE_BOTTOM
+    0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe,      // ICON_CUBE_FACE_RIGHT
+    0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe,      // ICON_CUBE_FACE_BACK
+    0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000,      // ICON_CAMERA
+    0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000,      // ICON_SPECIAL
+    0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800,      // ICON_LINK_NET
+    0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00,      // ICON_LINK_BOXES
+    0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000,      // ICON_LINK_MULTI
+    0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00,      // ICON_LINK
+    0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00,      // ICON_LINK_BROKE
+    0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc,      // ICON_TEXT_NOTES
+    0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc,      // ICON_NOTEBOOK
+    0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000,      // ICON_SUITCASE
+    0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000,      // ICON_SUITCASE_ZIP
+    0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000,      // ICON_MAILBOX
+    0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000,      // ICON_MONITOR
+    0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000,      // ICON_PRINTER
+    0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA
+    0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000,      // ICON_PHOTO_CAMERA_FLASH
+    0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000,      // ICON_HOUSE
+    0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000,      // ICON_HEART
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000,      // ICON_CORNER
+    0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000,      // ICON_VERTICAL_BARS
+    0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000,      // ICON_VERTICAL_BARS_FILL
+    0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000,      // ICON_LIFE_BARS
+    0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc,      // ICON_INFO
+    0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002,      // ICON_CROSSLINE
+    0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000,      // ICON_HELP
+    0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc,      // ICON_FILETYPE_ALPHA
+    0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc,      // ICON_FILETYPE_HOME
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS_VISIBLE
+    0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_LAYERS
+    0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_WINDOW
+    0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000,      // ICON_HIDPI
+    0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc,      // ICON_FILETYPE_BINARY
+    0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000,      // ICON_HEX
+    0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180,      // ICON_SHIELD
+    0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8,      // ICON_FILE_NEW
+    0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000,      // ICON_FOLDER_ADD
+    0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420,      // ICON_ALARM
+    0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0,      // ICON_CPU
+    0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0,      // ICON_ROM
+    0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000,      // ICON_STEP_OVER
+    0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_INTO
+    0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180,      // ICON_STEP_OUT
+    0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000,      // ICON_RESTART
+    0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_ON
+    0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000,      // ICON_BREAKPOINT_OFF
+    0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000,      // ICON_BURGER_MENU
+    0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000,      // ICON_CASE_SENSITIVE
+    0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000,      // ICON_REG_EXP
+    0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000,      // ICON_FOLDER
+    0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc,      // ICON_FILE
+    0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0,      // ICON_SAND_TIMER
+    0x01800000, 0x02400240, 0x05a00420, 0x09900990, 0x11881188, 0x21842004, 0x40024182, 0x00003ffc,      // ICON_WARNING
+    0x7ffe0000, 0x4ff24002, 0x4c324ff2, 0x4f824c02, 0x41824f82, 0x41824002, 0x40024182, 0x00007ffe,      // ICON_HELP_BOX
+    0x7ffe0000, 0x41824002, 0x40024182, 0x41824182, 0x41824182, 0x41824182, 0x40024182, 0x00007ffe,      // ICON_INFO_BOX
+    0x01800000, 0x04200240, 0x10080810, 0x7bde2004, 0x0a500a50, 0x08500bd0, 0x08100850, 0x00000ff0,      // ICON_PRIORITY
+    0x01800000, 0x18180660, 0x80016006, 0x98196006, 0x99996666, 0x19986666, 0x01800660, 0x00000000,      // ICON_LAYERS_ISO
+    0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
+    0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
+    0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
+    0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060,      // ICON_LABEL
+    0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000,      // ICON_NAME_ID
+    0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000,      // ICON_SLICING
+    0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0,      // ICON_MANUAL_CONTROL
+    0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe,      // ICON_COLLISION
+    0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_ADD
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_ADD_FILL
+    0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_WARNING
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_WARNING_FILL
+    0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE
+    0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE_FILL
+    0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS
+    0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS_FILL
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0,      // ICON_UNION
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_INTERSECTION
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_DIFFERENCE
+    0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0,      // ICON_SPHERE
+    0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0,      // ICON_CYLINDER
+    0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0,      // ICON_CONE
+    0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000,      // ICON_ELLIPSOID
+    0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000,      // ICON_CAPSULE
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_250
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_251
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_252
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_253
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_254
+    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_255
+};
+
+// NOTE: A pointer to current icons array should be defined
+static unsigned int *guiIconsPtr = guiIcons;
+
+#endif      // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS
+
+#ifndef RAYGUI_ICON_SIZE
+    #define RAYGUI_ICON_SIZE             0
+#endif
+
+// WARNING: Those values define the total size of the style data array,
+// if changed, previous saved styles could become incompatible
+#define RAYGUI_MAX_CONTROLS             16      // Maximum number of controls
+#define RAYGUI_MAX_PROPS_BASE           16      // Maximum number of base properties
+#define RAYGUI_MAX_PROPS_EXTENDED        8      // Maximum number of extended properties
+
+//----------------------------------------------------------------------------------
+// Module Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Gui control property style color element
+typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static GuiState guiState = STATE_NORMAL;        // Gui global state, if !STATE_NORMAL, forces defined state
+
+static Font guiFont = { 0 };                    // Gui current font (WARNING: highly coupled to raylib)
+static bool guiLocked = false;                  // Gui lock state (no inputs processed)
+static float guiAlpha = 1.0f;                   // Gui controls transparency
+
+static unsigned int guiIconScale = 1;           // Gui icon default scale (if icons enabled)
+
+static bool guiTooltip = false;                 // Tooltip enabled/disabled
+static const char *guiTooltipPtr = NULL;        // Tooltip string pointer (string provided by user)
+
+static bool guiControlExclusiveMode = false;    // Gui control exclusive mode (no inputs processed except current control)
+static Rectangle guiControlExclusiveRec = { 0 }; // Gui control exclusive bounds rectangle, used as an unique identifier
+
+static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
+//static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
+static int autoCursorCounter = 0;               // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
+
+//----------------------------------------------------------------------------------
+// Style data array for all gui style properties (allocated on data segment by default)
+//
+// NOTE 1: First set of BASE properties are generic to all controls but could be individually
+// overwritten per control, first set of EXTENDED properties are generic to all controls and
+// can not be overwritten individually but custom EXTENDED properties can be used by control
+//
+// NOTE 2: A new style set could be loaded over this array using GuiLoadStyle(),
+// but default gui style could always be recovered with GuiLoadStyleDefault()
+//
+// guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+//----------------------------------------------------------------------------------
+static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)] = { 0 };
+
+static bool guiStyleLoaded = false;         // Style loaded flag for lazy style initialization
+
+//----------------------------------------------------------------------------------
+// Standalone Mode Functions Declaration
+//
+// NOTE: raygui depend on some raylib input and drawing functions
+// To use raygui as standalone library, below functions must be defined by the user
+//----------------------------------------------------------------------------------
+#if defined(RAYGUI_STANDALONE)
+
+#define KEY_RIGHT           262
+#define KEY_LEFT            263
+#define KEY_DOWN            264
+#define KEY_UP              265
+#define KEY_BACKSPACE       259
+#define KEY_ENTER           257
+
+#define MOUSE_LEFT_BUTTON     0
+
+// Input required functions
+//-------------------------------------------------------------------------------
+static Vector2 GetMousePosition(void);
+static float GetMouseWheelMove(void);
+static bool IsMouseButtonDown(int button);
+static bool IsMouseButtonPressed(int button);
+static bool IsMouseButtonReleased(int button);
+
+static bool IsKeyDown(int key);
+static bool IsKeyPressed(int key);
+static int GetCharPressed(void);         // -- GuiTextBox(), GuiValueBox()
+//-------------------------------------------------------------------------------
+
+// Drawing required functions
+//-------------------------------------------------------------------------------
+static void DrawRectangle(int x, int y, int width, int height, Color color);        // -- GuiDrawRectangle()
+static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker()
+//-------------------------------------------------------------------------------
+
+// Text required functions
+//-------------------------------------------------------------------------------
+static Font GetFontDefault(void);                            // -- GuiLoadStyleDefault()
+static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font
+
+static Texture2D LoadTextureFromImage(Image image);          // -- GuiLoadStyle(), required to load texture from embedded font atlas image
+static void SetShapesTexture(Texture2D tex, Rectangle rec);  // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization)
+
+static char *LoadFileText(const char *fileName);             // -- GuiLoadStyle(), required to load charset data
+static void UnloadFileText(char *text);                      // -- GuiLoadStyle(), required to unload charset data
+
+static const char *GetDirectoryPath(const char *filePath);   // -- GuiLoadStyle(), required to find charset/font file from text .rgs
+
+static int *LoadCodepoints(const char *text, int *count);    // -- GuiLoadStyle(), required to load required font codepoints list
+static void UnloadCodepoints(int *codepoints);               // -- GuiLoadStyle(), required to unload codepoints list
+
+static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle()
+//-------------------------------------------------------------------------------
+
+// raylib functions already implemented in raygui
+//-------------------------------------------------------------------------------
+static Color GetColor(int hexValue);                // Returns a Color struct from hexadecimal value
+static int ColorToInt(Color color);                 // Returns hexadecimal value for a Color
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec);   // Check if point is inside rectangle
+static const char *TextFormat(const char *text, ...);               // Formatting of text with variables to 'embed'
+static const char **TextSplit(const char *text, char delimiter, int *count);    // Split text into multiple strings
+static int TextToInteger(const char *text);         // Get integer value from text
+static float TextToFloat(const char *text);         // Get float value from text
+
+static int GetCodepointNext(const char *text, int *codepointSize);  // Get next codepoint in a UTF-8 encoded text
+static const char *CodepointToUTF8(int codepoint, int *byteSize);   // Encode codepoint into UTF-8 text (char array size returned as parameter)
+
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);  // Draw rectangle vertical gradient
+//-------------------------------------------------------------------------------
+
+#endif      // RAYGUI_STANDALONE
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize);    // Load style from memory (binary only)
+
+static Rectangle GetTextBounds(int control, Rectangle bounds);  // Get text bounds considering control bounds
+static const char *GetTextIcon(const char *text, int *iconId);  // Get text icon if provided and move text cursor
+
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint);     // Gui draw text using default font
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color);   // Gui draw rectangle using default raygui style
+
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow);   // Split controls text into multiple strings
+static Vector3 ConvertHSVtoRGB(Vector3 hsv);                    // Convert color data from HSV to RGB
+static Vector3 ConvertRGBtoHSV(Vector3 rgb);                    // Convert color data from RGB to HSV
+
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue);   // Scroll bar control, used by GuiScrollPanel()
+static void GuiTooltip(Rectangle controlRec);                   // Draw tooltip using control rec position
+
+static Color GuiFade(Color color, float alpha);         // Fade color by an alpha factor
+
+//----------------------------------------------------------------------------------
+// Gui Setup Functions Definition
+//----------------------------------------------------------------------------------
+// Enable gui global state
+// NOTE: We check for STATE_DISABLED to avoid messing custom global state setups
+void GuiEnable(void) { if (guiState == STATE_DISABLED) guiState = STATE_NORMAL; }
+
+// Disable gui global state
+// NOTE: We check for STATE_NORMAL to avoid messing custom global state setups
+void GuiDisable(void) { if (guiState == STATE_NORMAL) guiState = STATE_DISABLED; }
+
+// Lock gui global state
+void GuiLock(void) { guiLocked = true; }
+
+// Unlock gui global state
+void GuiUnlock(void) { guiLocked = false; }
+
+// Check if gui is locked (global state)
+bool GuiIsLocked(void) { return guiLocked; }
+
+// Set gui controls alpha global state
+void GuiSetAlpha(float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    guiAlpha = alpha;
+}
+
+// Set gui state (global state)
+void GuiSetState(int state) { guiState = (GuiState)state; }
+
+// Get gui state (global state)
+int GuiGetState(void) { return guiState; }
+
+// Set custom gui font
+// NOTE: Font loading/unloading is external to raygui
+void GuiSetFont(Font font)
+{
+    if (font.texture.id > 0)
+    {
+        // NOTE: If we try to setup a font but default style has not been
+        // lazily loaded before, it will be overwritten, so we need to force
+        // default style loading first
+        if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+        guiFont = font;
+    }
+}
+
+// Get custom gui font
+Font GuiGetFont(void)
+{
+    return guiFont;
+}
+
+// Set control style property value
+void GuiSetStyle(int control, int property, int value)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+
+    // Default properties are propagated to all controls
+    if ((control == 0) && (property < RAYGUI_MAX_PROPS_BASE))
+    {
+        for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) guiStyle[i*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property] = value;
+    }
+}
+
+// Get control style property value
+int GuiGetStyle(int control, int property)
+{
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+    return guiStyle[control*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED) + property];
+}
+
+//----------------------------------------------------------------------------------
+// Gui Controls Functions Definition
+//----------------------------------------------------------------------------------
+
+// Window Box control
+int GuiWindowBox(Rectangle bounds, const char *title)
+{
+    // Window title bar height (including borders)
+    // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox()
+    #if !defined(RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT)
+        #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT        24
+    #endif
+
+    #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+        #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT      18
+    #endif
+
+    int result = 0;
+    //GuiState state = guiState;
+
+    int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT;
+
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
+    if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
+
+    const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
+    Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
+    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+                                 statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
+
+    // Update control
+    //--------------------------------------------------------------------
+    // NOTE: Logic is directly managed by button
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiStatusBar(statusBar, title); // Draw window header as status bar
+    GuiPanel(windowPanel, NULL);    // Draw window base
+
+    // Draw window close button
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+    result = GuiButton(closeButtonRec, "x");
+#else
+    result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL));
+#endif
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Window close button clicked: result = 1
+}
+
+// Group Box control with text name
+int GuiGroupBox(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_GROUPBOX_LINE_THICK)
+        #define RAYGUI_GROUPBOX_LINE_THICK     1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+
+    GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Line control
+int GuiLine(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_LINE_MARGIN_TEXT)
+        #define RAYGUI_LINE_MARGIN_TEXT  12
+    #endif
+    #if !defined(RAYGUI_LINE_TEXT_PADDING)
+        #define RAYGUI_LINE_TEXT_PADDING  4
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR));
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text == NULL) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, bounds.width, 1 }, 0, BLANK, color);
+    else
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = bounds.height;
+        textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
+        textBounds.y = bounds.y;
+
+        // Draw line with embedded text label: "--- text --------------"
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height/2, RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+        GuiDrawText(text, textBounds, TEXT_ALIGN_LEFT, color);
+        GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color);
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Panel control
+int GuiPanel(Rectangle bounds, const char *text)
+{
+    #if !defined(RAYGUI_PANEL_BORDER_WIDTH)
+        #define RAYGUI_PANEL_BORDER_WIDTH   1
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if ((text != NULL) && (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f)) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+    }
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Tab Bar control
+// NOTE: Using GuiToggle() for the TABS
+int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
+{
+    #define RAYGUI_TABBAR_ITEM_WIDTH    148
+
+    int result = -1;
+    //GuiState state = guiState;
+
+    Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height };
+
+    if (*active < 0) *active = 0;
+    else if (*active > count - 1) *active = count - 1;
+
+    int offsetX = 0;    // Required in case tabs go out of screen
+    offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth();
+    if (offsetX < 0) offsetX = 0;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Draw control
+    //--------------------------------------------------------------------
+    for (int i = 0; i < count; i++)
+    {
+        tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX;
+
+        if (tabBounds.x < GetScreenWidth())
+        {
+            // Draw tabs as toggle controls
+            int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT);
+            int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+            GuiSetStyle(TOGGLE, TEXT_PADDING, 8);
+
+            if (i == (*active))
+            {
+                toggle = true;
+                GuiToggle(tabBounds, text[i], &toggle);
+            }
+            else
+            {
+                toggle = false;
+                GuiToggle(tabBounds, text[i], &toggle);
+                if (toggle) *active = i;
+            }
+
+            // Close tab with middle mouse button pressed
+            if (CheckCollisionPointRec(GetMousePosition(), tabBounds) && IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) result = i;
+
+            GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding);
+            GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment);
+
+            // Draw tab close button
+            // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds))
+            int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+            int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+            GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+#if defined(RAYGUI_NO_ICONS)
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i;
+#else
+            if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i;
+#endif
+            GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+            GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment);
+        }
+    }
+
+    // Draw tab-bar bottom line
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL)));
+    //--------------------------------------------------------------------
+
+    return result;     // Return as result the current TAB closing requested
+}
+
+// Scroll Panel control
+int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view)
+{
+    #define RAYGUI_MIN_SCROLLBAR_WIDTH     40
+    #define RAYGUI_MIN_SCROLLBAR_HEIGHT    40
+    #define RAYGUI_MIN_MOUSE_WHEEL_SPEED   20
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Rectangle temp = { 0 };
+    if (view == NULL) view = &temp;
+
+    Vector2 scrollPos = { 0.0f, 0.0f };
+    if (scroll != NULL) scrollPos = *scroll;
+
+    // Text will be drawn as a header bar (if provided)
+    Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT };
+    if (bounds.height < RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f) bounds.height = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT*2.0f;
+
+    if (text != NULL)
+    {
+        // Move panel bounds after the header bar
+        bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1;
+        bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1;
+    }
+
+    bool hasHorizontalScrollBar = (content.width > bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+    bool hasVerticalScrollBar = (content.height > bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH))? true : false;
+
+    // Recheck to account for the other scrollbar being visible
+    if (!hasHorizontalScrollBar) hasHorizontalScrollBar = (hasVerticalScrollBar && (content.width > (bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+    if (!hasVerticalScrollBar) hasVerticalScrollBar = (hasHorizontalScrollBar && (content.height > (bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH))))? true : false;
+
+    int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    int verticalScrollBarWidth =  hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0;
+    Rectangle horizontalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)horizontalScrollBarWidth
+    };
+    Rectangle verticalScrollBar = {
+        (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)),
+        (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH),
+        (float)verticalScrollBarWidth,
+        (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+    };
+
+    // Make sure scroll bars have a minimum width/height
+    if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH;
+    if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT;
+
+    // Calculate view area (area without the scrollbars)
+    *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)?
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } :
+                RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth };
+
+    // Clip view area to the actual content size
+    if (view->width > content.width) view->width = content.width;
+    if (view->height > content.height) view->height = content.height;
+
+    float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f;
+    float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+#if defined(SUPPORT_SCROLLBAR_KEY_INPUT)
+            if (hasHorizontalScrollBar)
+            {
+                if (IsKeyDown(KEY_RIGHT)) scrollPos.x -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_LEFT)) scrollPos.x += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+
+            if (hasVerticalScrollBar)
+            {
+                if (IsKeyDown(KEY_DOWN)) scrollPos.y -= GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                if (IsKeyDown(KEY_UP)) scrollPos.y += GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+            }
+#endif
+            float wheelMove = GetMouseWheelMove();
+
+            // Set scrolling speed with mouse wheel based on ratio between bounds and content
+            Vector2 mouseWheelSpeed = { content.width/bounds.width, content.height/bounds.height };
+            if (mouseWheelSpeed.x < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.x = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+            if (mouseWheelSpeed.y < RAYGUI_MIN_MOUSE_WHEEL_SPEED) mouseWheelSpeed.y = RAYGUI_MIN_MOUSE_WHEEL_SPEED;
+
+            // Horizontal and vertical scrolling with mouse wheel
+            if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed.x;
+            else scrollPos.y += wheelMove*mouseWheelSpeed.y; // Vertical scroll
+        }
+    }
+
+    // Normalize scroll values
+    if (scrollPos.x > -horizontalMin) scrollPos.x = -horizontalMin;
+    if (scrollPos.x < -horizontalMax) scrollPos.x = -horizontalMax;
+    if (scrollPos.y > -verticalMin) scrollPos.y = -verticalMin;
+    if (scrollPos.y < -verticalMax) scrollPos.y = -verticalMax;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
+
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));        // Draw background
+
+    // Save size of the scrollbar slider
+    const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+
+    // Draw horizontal scrollbar if visible
+    if (hasHorizontalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content width and the widget width
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)/(int)content.width)*((int)bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth)));
+        scrollPos.x = (float)-GuiScrollBar(horizontalScrollBar, (int)-scrollPos.x, (int)horizontalMin, (int)horizontalMax);
+    }
+    else scrollPos.x = 0.0f;
+
+    // Draw vertical scrollbar if visible
+    if (hasVerticalScrollBar)
+    {
+        // Change scrollbar slider size to show the diff in size between the content height and the widget height
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)(((bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)/(int)content.height)*((int)bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth)));
+        scrollPos.y = (float)-GuiScrollBar(verticalScrollBar, (int)-scrollPos.y, (int)verticalMin, (int)verticalMax);
+    }
+    else scrollPos.y = 0.0f;
+
+    // Draw detail corner rectangle if both scroll bars are visible
+    if (hasHorizontalScrollBar && hasVerticalScrollBar)
+    {
+        Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 };
+        GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))));
+    }
+
+    // Draw scrollbar lines depending on current state
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK);
+
+    // Set scrollbar slider size back to the way it was before
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider);
+    //--------------------------------------------------------------------
+
+    if (scroll != NULL) *scroll = scrollPos;
+
+    return result;
+}
+
+// Label control
+int GuiLabel(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    //...
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Button control, returns true when clicked
+int GuiButton(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))));
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //------------------------------------------------------------------
+
+    return result;      // Button pressed: result = 1
+}
+
+// Label button control
+int GuiLabelButton(Rectangle bounds, const char *text)
+{
+    GuiState state = guiState;
+    bool pressed = false;
+
+    // NOTE: We force bounds.width to be all text
+    float textWidth = (float)GuiGetTextWidth(text);
+    if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return pressed;
+}
+
+// Toggle Button control
+int GuiToggle(Rectangle bounds, const char *text, bool *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (active == NULL) active = &temp;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check toggle button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_NORMAL;
+                *active = !(*active);
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_NORMAL)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))));
+    }
+    else
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3)));
+        GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)));
+    }
+
+    if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Toggle Group control
+int GuiToggleGroup(Rectangle bounds, const char *text, int *active)
+{
+    #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS)
+        #define RAYGUI_TOGGLEGROUP_MAX_ITEMS    32
+    #endif
+
+    int result = 0;
+    float initBoundsX = bounds.x;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 };
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, rows);
+
+    int prevRow = rows[0];
+
+    for (int i = 0; i < itemCount; i++)
+    {
+        if (prevRow != rows[i])
+        {
+            bounds.x = initBoundsX;
+            bounds.y += (bounds.height + GuiGetStyle(TOGGLE, GROUP_PADDING));
+            prevRow = rows[i];
+        }
+
+        if (i == (*active))
+        {
+            toggle = true;
+            GuiToggle(bounds, items[i], &toggle);
+        }
+        else
+        {
+            toggle = false;
+            GuiToggle(bounds, items[i], &toggle);
+            if (toggle) *active = i;
+        }
+
+        bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING));
+    }
+
+    return result;
+}
+
+// Toggle Slider control extended
+int GuiToggleSlider(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    //bool toggle = false;    // Required for individual toggles
+
+    // Get substrings items from text (items pointers)
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle slider = {
+        0,      // Calculated later depending on the active toggle
+        bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+        (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount,
+        bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                (*active)++;
+                result = 1;
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED;
+    }
+
+    if (*active >= itemCount) *active = 0;
+    slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width;
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))),
+        GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL)));
+
+    // Draw internal slider
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+
+    // Draw text in slider
+    if (text != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(text);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Check Box control, returns 1 when state changed
+int GuiCheckBox(Rectangle bounds, const char *text, bool *checked)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    bool temp = false;
+    if (checked == NULL) checked = &temp;
+
+    Rectangle textBounds = { 0 };
+
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(CHECKBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        Rectangle totalBounds = {
+            (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT)? textBounds.x : bounds.x,
+            bounds.y,
+            bounds.width + textBounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING),
+            bounds.height,
+        };
+
+        // Check checkbox state
+        if (CheckCollisionPointRec(mousePoint, totalBounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+
+            if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+            {
+                *checked = !(*checked);
+                result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK);
+
+    if (*checked)
+    {
+        Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING),
+                            bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)),
+                            bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) };
+        GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)));
+    }
+
+    GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Combo Box control
+int GuiComboBox(Rectangle bounds, const char *text, int *active)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING));
+
+    Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING),
+                           (float)bounds.y, (float)GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH), (float)bounds.height };
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    if (*active < 0) *active = 0;
+    else if (*active > (itemCount - 1)) *active = itemCount - 1;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (CheckCollisionPointRec(mousePoint, bounds) ||
+            CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                *active += 1;
+                if (*active >= itemCount) *active = 0;      // Cyclic combobox
+            }
+
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw combo box main
+    GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))));
+    GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))));
+
+    // Draw selector using a custom button
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 1);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount));
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dropdown Box control
+// NOTE: Returns mouse click
+int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int temp = 0;
+    if (active == NULL) active = &temp;
+
+    int itemSelected = *active;
+    int itemFocused = -1;
+
+    int direction = 0; // Dropdown box open direction: down (default)
+    if (GuiGetStyle(DROPDOWNBOX, DROPDOWN_ROLL_UP) == 1) direction = 1; // Up
+
+    // Get substrings items from text (items pointers, lengths and count)
+    int itemCount = 0;
+    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    Rectangle boundsOpen = bounds;
+    boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+    if (direction == 1) boundsOpen.y -= itemCount*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)) + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING);
+
+    Rectangle itemBounds = bounds;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            // Check if mouse has been pressed or released outside limits
+            if (!CheckCollisionPointRec(mousePoint, boundsOpen))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+
+            // Check if already selected item has been pressed again
+            if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+
+            // Check focused and selected item
+            for (int i = 0; i < itemCount; i++)
+            {
+                // Update item rectangle y position for next item
+                if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+                else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = i;
+                    if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON))
+                    {
+                        itemSelected = i;
+                        result = 1;         // Item selected
+                    }
+                    break;
+                }
+            }
+
+            itemBounds = bounds;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    result = 1;
+                    state = STATE_PRESSED;
+                }
+                else state = STATE_FOCUSED;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (editMode) GuiPanel(boundsOpen, NULL);
+
+    GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)));
+    GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)));
+
+    if (editMode)
+    {
+        // Draw visible items
+        for (int i = 0; i < itemCount; i++)
+        {
+            // Update item rectangle y position for next item
+            if (direction == 0) itemBounds.y += (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+            else itemBounds.y -= (bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING));
+
+            if (i == itemSelected)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)));
+            }
+            else if (i == itemFocused)
+            {
+                GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)));
+                GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)));
+            }
+            else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)));
+        }
+    }
+
+    if (!GuiGetStyle(DROPDOWNBOX, DROPDOWN_ARROW_HIDDEN))
+    {
+        // Draw arrows (using icon if available)
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(direction? "#121#" : "#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));   // ICON_ARROW_DOWN_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    *active = itemSelected;
+
+    // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item...
+    return result;   // Mouse click: result = 1
+}
+
+// Text Box control
+// NOTE: Returns true on ENTER pressed (useful for data validation)
+int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  20        // Frames to wait for autocursor movement
+    #endif
+    #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY      1        // Frames delay for autocursor movement
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    bool multiline = false;     // TODO: Consider multiline text input
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
+
+    Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
+    int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
+    int thisCursorIndex = textBoxCursorIndex;
+    if (thisCursorIndex > textLength) thisCursorIndex = textLength;
+    int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
+    int textIndexOffset = 0;    // Text index offset to start drawing in the box
+
+    // Cursor rectangle
+    // NOTE: Position X value should be updated
+    Rectangle cursor = {
+        textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING),
+        textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE),
+        2,
+        (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2
+    };
+
+    if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+    if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH);
+
+    // Mouse cursor rectangle
+    // NOTE: Initialized outside of screen
+    Rectangle mouseCursor = cursor;
+    mouseCursor.x = -1;
+    mouseCursor.width = 1;
+
+    // Blink-cursor frame counter
+    //if (!autoCursorMode) blinkCursorFrameCounter++;
+    //else blinkCursorFrameCounter = 0;
+
+    // Update control
+    //--------------------------------------------------------------------
+    // WARNING: Text editing is only supported under certain conditions:
+    if ((state != STATE_DISABLED) &&                // Control not disabled
+        !GuiGetStyle(TEXTBOX, TEXT_READONLY) &&     // TextBox not on read-only mode
+        !guiLocked &&                               // Gui not locked
+        !guiControlExclusiveMode &&                       // No gui slider on dragging
+        (wrapMode == TEXT_WRAP_NONE))               // No wrap mode
+    {
+        Vector2 mousePosition = GetMousePosition();
+
+        if (editMode)
+        {
+            // GLOBAL: Auto-cursor movement logic
+            // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+            if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+            else autoCursorCounter = 0;
+
+            bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
+            state = STATE_PRESSED;
+
+            if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+
+            // If text does not fit in the textbox and current cursor position is out of bounds,
+            // we add an index offset to text for drawing only what requires depending on cursor
+            while (textWidth >= textBounds.width)
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textIndexOffset, &nextCodepointSize);
+
+                textIndexOffset += nextCodepointSize;
+
+                textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
+            }
+
+            int codepoint = GetCharPressed();       // Get Unicode codepoint
+            if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n';
+
+            // Encode codepoint as UTF-8
+            int codepointSize = 0;
+            const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
+
+            // Handle text paste action
+            if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                const char *pasteText = GetClipboardText();
+                if (pasteText != NULL)
+                {
+                    int pasteLength = 0;
+                    int pasteCodepoint;
+                    int pasteCodepointSize;
+
+                    // Count how many codepoints to copy, stopping at the first unwanted control character
+                    while (true)
+                    {
+                        pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+                        if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+                        if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+                        pasteLength += pasteCodepointSize;
+                    }
+
+                    if (pasteLength > 0)
+                    {
+                        // Move forward data from cursor position
+                        for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+                        // Paste data in at cursor
+                        for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+                        textBoxCursorIndex += pasteLength;
+                        textLength += pasteLength;
+                        text[textLength] = '\0';
+                    }
+                }
+            }
+            else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            {
+                // Adding codepoint to text, at current cursor position
+
+                // Move forward data from cursor position
+                for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
+
+                // Add new codepoint in current cursor position
+                for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i];
+
+                textBoxCursorIndex += codepointSize;
+                textLength += codepointSize;
+
+                // Make sure text last character is EOL
+                text[textLength] = '\0';
+            }
+
+            // Move cursor to start
+            if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0;
+
+            // Move cursor to end
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
+
+            // Delete related codepoints from text, after current cursor position
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+                        break;
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to delete (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+            }
+
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, after current cursor position
+
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
+
+                textLength -= nextCodepointSize;
+            }
+
+            // Delete related codepoints from text, before current cursor position
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
+
+                // Check whitespace to delete (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+                textBoxCursorIndex -= accCodepointSize;
+            }
+
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, before current cursor position
+
+                int prevCodepointSize = 0;
+
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
+
+                textLength -= prevCodepointSize;
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+
+            // Move cursor position with keys
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
+
+                // Check whitespace to skip (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
+            {
+                int prevCodepointSize = 0;
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+            else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
+                {
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                // Check whitespace to skip (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                textBoxCursorIndex = offset;
+            }
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+
+                textBoxCursorIndex += nextCodepointSize;
+            }
+
+            // Move cursor position with mouse
+            if (CheckCollisionPointRec(mousePosition, textBounds))     // Mouse hover text
+            {
+                float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize;
+                int codepointIndex = 0;
+                float glyphWidth = 0.0f;
+                float widthToMouseX = 0;
+                int mouseCursorIndex = 0;
+
+                for (int i = textIndexOffset; i < textLength; i += codepointSize)
+                {
+                    codepoint = GetCodepointNext(&text[i], &codepointSize);
+                    codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                    if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                    else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                    if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2)))
+                    {
+                        mouseCursor.x = textBounds.x + widthToMouseX;
+                        mouseCursorIndex = i;
+                        break;
+                    }
+
+                    widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                }
+
+                // Check if mouse cursor is at the last position
+                int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
+                if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
+                {
+                    mouseCursor.x = textBounds.x + textEndWidth;
+                    mouseCursorIndex = textLength;
+                }
+
+                // Place cursor at required index on mouse click
+                if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    cursor.x = mouseCursor.x;
+                    textBoxCursorIndex = mouseCursorIndex;
+                }
+            }
+            else mouseCursor.x = -1;
+
+            // Recalculate cursor position.y depending on textBoxCursorIndex
+            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+            //if (multiline) cursor.y = GetTextLines()
+
+            // Finish text editing on ENTER or mouse click outside bounds
+            if ((!multiline && IsKeyPressed(KEY_ENTER)) ||
+                (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                textBoxCursorIndex = 0;     // GLOBAL: Reset the shared cursor index
+                autoCursorCounter = 0;      // GLOBAL: Reset counter for repeated keystrokes
+                result = 1;
+            }
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePosition, bounds))
+            {
+                state = STATE_FOCUSED;
+
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                {
+                    textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
+                    autoCursorCounter = 0;             // GLOBAL: Reset counter for repeated keystrokes
+                    result = 1;
+                }
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_PRESSED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)));
+    }
+    else if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)));
+    }
+    else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK);
+
+    // Draw text considering index offset if required
+    // NOTE: Text index offset depends on cursor position
+    GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))));
+
+    // Draw cursor
+    if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY))
+    {
+        //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0))
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+
+        // Draw mouse position cursor (if required)
+        if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)));
+    }
+    else if (state == STATE_FOCUSED) GuiTooltip(bounds);
+    //--------------------------------------------------------------------
+
+    return result;      // Mouse button pressed: result = 1
+}
+
+/*
+// Text Box control with multiple lines and word-wrap
+// NOTE: This text-box is readonly, no editing supported by default
+bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode)
+{
+    bool pressed = false;
+
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 1);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD);   // WARNING: If wrap mode enabled, text editing is not supported
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP);
+
+    // TODO: Implement methods to calculate cursor position properly
+    pressed = GuiTextBox(bounds, text, textSize, editMode);
+
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);
+    GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE);
+    GuiSetStyle(TEXTBOX, TEXT_READONLY, 0);
+
+    return pressed;
+}
+*/
+
+// Spinner control, returns selected value
+int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    int result = 1;
+    GuiState state = guiState;
+
+    int tempValue = *value;
+
+    Rectangle valueBoxBounds = {
+        bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+        bounds.y,
+        bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+        (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check spinner state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+
+#if defined(RAYGUI_NO_ICONS)
+    if (GuiButton(leftButtonBound, "<")) tempValue--;
+    if (GuiButton(rightButtonBound, ">")) tempValue++;
+#else
+    if (GuiButton(leftButtonBound, GuiIconText(ICON_ARROW_LEFT_FILL, NULL))) tempValue--;
+    if (GuiButton(rightButtonBound, GuiIconText(ICON_ARROW_RIGHT_FILL, NULL))) tempValue++;
+#endif
+
+    if (!editMode)
+    {
+        if (tempValue < minValue) tempValue = minValue;
+        if (tempValue > maxValue) tempValue = maxValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
+
+    // Draw value selector custom buttons
+    // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
+    int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
+    int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    *value = tempValue;
+    return result;
+}
+
+// Value Box control, updates input text with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+    snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Add new digit to text value
+            if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
+            {
+                int key = GetCharPressed();
+                
+                // Only allow keys in range [48..57]
+                if ((key >= 48) && (key <= 57))
+                {
+                    textValue[keyCount] = (char)key;
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Delete text
+            if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
+            {
+                keyCount--;
+                textValue[keyCount] = '\0';
+                valueHasChanged = true;
+            }
+
+            if (valueHasChanged) *value = TextToInteger(textValue);
+
+            // NOTE: We are not clamp values until user input finishes
+            //if (*value > maxValue) *value = maxValue;
+            //else if (*value < minValue) *value = minValue;
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
+            {
+                if (*value > maxValue) *value = maxValue;
+                else if (*value < minValue) *value = minValue;
+
+                result = 1;
+            }
+        }
+        else
+        {
+            if (*value > maxValue) *value = maxValue;
+            else if (*value < minValue) *value = minValue;
+
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor rectangle
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+            bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+            2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+        if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Floating point Value Box control, updates input val_str with numbers
+// NOTE: Requires static variables: frameCounter
+int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
+{
+    #if !defined(RAYGUI_VALUEBOX_MAX_CHARS)
+        #define RAYGUI_VALUEBOX_MAX_CHARS  32
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
+    //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
+
+    Rectangle textBounds = { 0 };
+    if (text != NULL)
+    {
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        bool valueHasChanged = false;
+
+        if (editMode)
+        {
+            state = STATE_PRESSED;
+
+            int keyCount = (int)strlen(textValue);
+
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
+            // Only allow keys in range [48..57]
+            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            {
+                if (GuiGetTextWidth(textValue) < bounds.width)
+                {
+                    int key = GetCharPressed();
+                    if (((key >= 48) && (key <= 57)) ||
+                        (key == '.') ||
+                        ((keyCount == 0) && (key == '+')) ||  // NOTE: Sign can only be in first position
+                        ((keyCount == 0) && (key == '-')))
+                    {
+                        textValue[keyCount] = (char)key;
+                        keyCount++;
+
+                        valueHasChanged = true;
+                    }
+                }
+            }
+
+            // Pressed backspace
+            if (IsKeyPressed(KEY_BACKSPACE))
+            {
+                if (keyCount > 0)
+                {
+                    keyCount--;
+                    textValue[keyCount] = '\0';
+                    valueHasChanged = true;
+                }
+            }
+
+            if (valueHasChanged) *value = TextToFloat(textValue);
+
+            if ((IsKeyPressed(KEY_ENTER) || IsKeyPressed(KEY_KP_ENTER)) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1;
+        }
+        else
+        {
+            if (CheckCollisionPointRec(mousePoint, bounds))
+            {
+                state = STATE_FOCUSED;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1;
+            }
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    Color baseColor = BLANK;
+    if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED));
+    else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
+    GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
+
+    // Draw cursor
+    if (editMode)
+    {
+        // NOTE: ValueBox internal text is always centered
+        Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+                            bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
+                            bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
+        GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
+    }
+
+    // Draw text label if provided
+    GuiDrawText(text, textBounds,
+                (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT,
+                GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider control with pro parameters
+// NOTE: Other GuiSlider*() controls use this one
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+    float oldValue = *value;
+
+    int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
+    Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
+                         0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // Get equivalent value and slider position from mousePosition.x
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
+                }
+            }
+            else state = STATE_FOCUSED;
+        }
+
+        if (*value > maxValue) *value = maxValue;
+        else if (*value < minValue) *value = minValue;
+    }
+
+    // Control value change check
+    if (oldValue == *value) result = 0;
+    else result = 1;
+
+    // Slider bar limits check
+    float sliderValue = (((*value - minValue)/(maxValue - minValue))*(bounds.width - sliderWidth - 2*GuiGetStyle(SLIDER, BORDER_WIDTH)));
+    if (sliderWidth > 0)        // Slider
+    {
+        slider.x += sliderValue;
+        slider.width = (float)sliderWidth;
+        if (slider.x <= (bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH))) slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH);
+        else if ((slider.x + slider.width) >= (bounds.x + bounds.width)) slider.x = bounds.x + bounds.width - slider.width - GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    else if (sliderWidth == 0)  // SliderBar
+    {
+        slider.x += GuiGetStyle(SLIDER, BORDER_WIDTH);
+        slider.width = sliderValue;
+        if (slider.width > bounds.width) slider.width = bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)?  BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+
+    // Draw slider internal bar (depends on state)
+    if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
+    else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
+    else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+    else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Slider Bar control extended, returns selected value
+int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+    result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+    return result;
+}
+
+// Progress Bar control extended, shows current progress value
+int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    float temp = (maxValue - minValue)/2.0f;
+    if (value == NULL) value = &temp;
+
+    // Progress bar
+    Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
+                           bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
+                           bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if (*value > maxValue) *value = maxValue;
+
+    // WARNING: Working with floats could lead to rounding issues
+    if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED)
+    {
+        GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK);
+    }
+    else
+    {
+        if (*value > minValue)
+        {
+            // Draw progress bar with colored border, more visual
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        }
+        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+
+        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        else
+        {
+            // Draw borders not yet reached by value
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+        }
+
+        // Draw slider internal progress bar (depends on state)
+        GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)));
+    }
+
+    // Draw left/right text if provided
+    if (textLeft != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+
+    if (textRight != NULL)
+    {
+        Rectangle textBounds = { 0 };
+        textBounds.width = (float)GuiGetTextWidth(textRight);
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
+        textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    }
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Status Bar control
+int GuiStatusBar(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3))));
+    GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3))));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Dummy rectangle control, intended for placeholding
+int GuiDummyRec(Rectangle bounds, const char *text)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check button state
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED;
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)));
+    GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)));
+    //------------------------------------------------------------------
+
+    return result;
+}
+
+// List View control
+int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active)
+{
+    int result = 0;
+    int itemCount = 0;
+    const char **items = NULL;
+
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
+    result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL);
+
+    return result;
+}
+
+// List View control with extended parameters
+int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus)
+{
+    int result = 0;
+    GuiState state = guiState;
+
+    int itemFocused = (focus == NULL)? -1 : *focus;
+    int itemSelected = (active == NULL)? -1 : *active;
+
+    // Check if we need a scroll bar
+    bool useScrollBar = false;
+    if ((GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING))*count > bounds.height) useScrollBar = true;
+
+    // Define base item rectangle [0]
+    Rectangle itemBounds = { 0 };
+    itemBounds.x = bounds.x + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING);
+    itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.width = bounds.width - 2*GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) - GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    itemBounds.height = (float)GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT);
+    if (useScrollBar) itemBounds.width -= GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH);
+
+    // Get items on the list
+    int visibleItems = (int)bounds.height/(GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    if (visibleItems > count) visibleItems = count;
+
+    int startIndex = (scrollIndex == NULL)? 0 : *scrollIndex;
+    if ((startIndex < 0) || (startIndex > (count - visibleItems))) startIndex = 0;
+    int endIndex = startIndex + visibleItems;
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        // Check mouse inside list view
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Check focused and selected item
+            for (int i = 0; i < visibleItems; i++)
+            {
+                if (CheckCollisionPointRec(mousePoint, itemBounds))
+                {
+                    itemFocused = startIndex + i;
+                    if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+                    {
+                        if (itemSelected == (startIndex + i)) itemSelected = -1;
+                        else itemSelected = startIndex + i;
+                    }
+                    break;
+                }
+
+                // Update item rectangle y position for next item
+                itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+            }
+
+            if (useScrollBar)
+            {
+                int wheelMove = (int)GetMouseWheelMove();
+                startIndex -= wheelMove;
+
+                if (startIndex < 0) startIndex = 0;
+                else if (startIndex > (count - visibleItems)) startIndex = count - visibleItems;
+
+                endIndex = startIndex + visibleItems;
+                if (endIndex > count) endIndex = count;
+            }
+        }
+        else itemFocused = -1;
+
+        // Reset item rectangle y to [0]
+        itemBounds.y = bounds.y + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING) + GuiGetStyle(DEFAULT, BORDER_WIDTH);
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+
+    // Draw visible items
+    for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
+    {
+        if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+
+        if (state == STATE_DISABLED)
+        {
+            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+
+            GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
+        }
+        else
+        {
+            if (((startIndex + i) == itemSelected) && (active != NULL))
+            {
+                // Draw item selected
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
+            }
+            else if (((startIndex + i) == itemFocused)) // && (focus != NULL))  // NOTE: We want items focused, despite not returned!
+            {
+                // Draw item focused
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
+            }
+            else
+            {
+                // Draw item normal (no rectangle)
+                GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
+            }
+        }
+
+        // Update item rectangle y position for next item
+        itemBounds.y += (GuiGetStyle(LISTVIEW, LIST_ITEMS_HEIGHT) + GuiGetStyle(LISTVIEW, LIST_ITEMS_SPACING));
+    }
+
+    if (useScrollBar)
+    {
+        Rectangle scrollBarBounds = {
+            bounds.x + bounds.width - GuiGetStyle(LISTVIEW, BORDER_WIDTH) - GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.y + GuiGetStyle(LISTVIEW, BORDER_WIDTH), (float)GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH),
+            bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH)
+        };
+
+        // Calculate percentage of visible items and apply same percentage to scrollbar
+        float percentVisible = (float)(endIndex - startIndex)/count;
+        float sliderSize = bounds.height*percentVisible;
+
+        int prevSliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);   // Save default slider size
+        int prevScrollSpeed = GuiGetStyle(SCROLLBAR, SCROLL_SPEED); // Save default scroll speed
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, (int)sliderSize);            // Change slider size
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, count - visibleItems); // Change scroll speed
+
+        startIndex = GuiScrollBar(scrollBarBounds, startIndex, 0, count - visibleItems);
+
+        GuiSetStyle(SCROLLBAR, SCROLL_SPEED, prevScrollSpeed); // Reset scroll speed to default
+        GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, prevSliderSize); // Reset slider size to default
+    }
+    //--------------------------------------------------------------------
+
+    if (active != NULL) *active = itemSelected;
+    if (focus != NULL) *focus = itemFocused;
+    if (scrollIndex != NULL) *scrollIndex = startIndex;
+
+    return result;
+}
+
+// Color Panel control - Color (RGBA) variant
+int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
+    Vector3 hsv = ConvertRGBtoHSV(vcolor);
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
+
+    GuiColorPanelHSV(bounds, text, &hsv);
+
+    // Check if the hsv was changed, only then change the color
+    // This is required, because the Color->HSV->Color conversion has precision errors
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+    // Otherwise GuiColorPanel would often modify it's color without user input
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
+    if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
+    {
+        Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+        // NOTE: Vector3ToColor() only available on raylib 1.8.1
+        *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x),
+                            (unsigned char)(255.0f*rgb.y),
+                            (unsigned char)(255.0f*rgb.z),
+                            color->a };
+    }
+    return result;
+}
+
+// Color Bar Alpha control
+// NOTE: Returns alpha value normalized [0..1]
+int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+{
+    #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE)
+        #define RAYGUI_COLORBARALPHA_CHECKED_SIZE   10
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+        (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+        (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+        (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                    if (*alpha <= 0.0f) *alpha = 0.0f;
+                    if (*alpha >= 1.0f) *alpha = 1.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *alpha = (mousePoint.x - bounds.x)/bounds.width;
+                if (*alpha <= 0.0f) *alpha = 0.0f;
+                if (*alpha >= 1.0f) *alpha = 1.0f;
+                //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    // Draw alpha bar: checked background
+    if (state != STATE_DISABLED)
+    {
+        int checksX = (int)bounds.width/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+        int checksY = (int)bounds.height/RAYGUI_COLORBARALPHA_CHECKED_SIZE;
+
+        for (int x = 0; x < checksX; x++)
+        {
+            for (int y = 0; y < checksY; y++)
+            {
+                Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE };
+                GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f));
+            }
+        }
+
+        DrawRectangleGradientEx(bounds, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, RAYGUI_CLITERAL(Color){ 255, 255, 255, 0 }, Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw alpha bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Bar Hue control
+// Returns hue value normalized [0..1]
+// NOTE: Other similar bars (for reference):
+//      Color GuiColorBarSat() [WHITE->color]
+//      Color GuiColorBarValue() [BLACK->color], HSV/HSL
+//      float GuiColorBarLuminance() [BLACK->WHITE]
+int GuiColorBarHue(Rectangle bounds, const char *text, float *hue)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                    if (*hue <= 0.0f) *hue = 0.0f;
+                    if (*hue >= 359.0f) *hue = 359.0f;
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                *hue = (mousePoint.y - bounds.y)*360/bounds.height;
+                if (*hue <= 0.0f) *hue = 0.0f;
+                if (*hue >= 359.0f) *hue = 359.0f;
+
+            }
+            else state = STATE_FOCUSED;
+
+            /*if (IsKeyDown(KEY_UP))
+            {
+                hue -= 2.0f;
+                if (hue <= 0.0f) hue = 0.0f;
+            }
+            else if (IsKeyDown(KEY_DOWN))
+            {
+                hue += 2.0f;
+                if (hue >= 360.0f) hue = 360.0f;
+            }*/
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        // Draw hue bar:color bars
+        // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha));
+        DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha));
+    }
+    else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha));
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+
+    // Draw hue bar: selector
+    GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)));
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Color Picker control
+// NOTE: It's divided in multiple controls:
+//      Color GuiColorPanel(Rectangle bounds, Color color)
+//      float GuiColorBarAlpha(Rectangle bounds, float alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanel() size
+// NOTE: this picker converts RGB to HSV, which can cause the Hue control to jump. If you have this problem, consider using the HSV variant instead
+int GuiColorPicker(Rectangle bounds, const char *text, Color *color)
+{
+    int result = 0;
+
+    Color temp = { 200, 0, 0, 255 };
+    if (color == NULL) color = &temp;
+
+    GuiColorPanel(bounds, NULL, color);
+
+    Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+    //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
+
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
+    Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
+
+    GuiColorBarHue(boundsHue, NULL, &hsv.x);
+
+    //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f);
+    Vector3 rgb = ConvertHSVtoRGB(hsv);
+
+    *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a };
+
+    return result;
+}
+
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
+// NOTE: It's divided in multiple controls:
+//      int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+//      int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
+//      float GuiColorBarHue(Rectangle bounds, float value)
+// NOTE: bounds define GuiColorPanelHSV() size
+int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+
+    Vector3 tempHsv = { 0 };
+
+    if (colorHsv == NULL)
+    {
+        const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f };
+        tempHsv = ConvertRGBtoHSV(tempColor);
+        colorHsv = &tempHsv;
+    }
+
+    GuiColorPanelHSV(bounds, NULL, colorHsv);
+
+    const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
+
+    GuiColorBarHue(boundsHue, NULL, &colorHsv->x);
+
+    return result;
+}
+
+// Color Panel control - HSV variant
+int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
+{
+    int result = 0;
+    GuiState state = guiState;
+    Vector2 pickerSelector = { 0 };
+
+    const Color colWhite = { 255, 255, 255, 255 };
+    const Color colBlack = { 0, 0, 0, 255 };
+
+    pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width;            // HSV: Saturation
+    pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height;  // HSV: Value
+
+    Vector3 maxHue = { colorHsv->x, 1.0f, 1.0f };
+    Vector3 rgbHue = ConvertHSVtoRGB(maxHue);
+    Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x),
+                      (unsigned char)(255.0f*rgbHue.y),
+                      (unsigned char)(255.0f*rgbHue.z), 255 };
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    pickerSelector = mousePoint;
+
+                    if (pickerSelector.x < bounds.x) pickerSelector.x = bounds.x;
+                    if (pickerSelector.x > bounds.x + bounds.width) pickerSelector.x = bounds.x + bounds.width;
+                    if (pickerSelector.y < bounds.y) pickerSelector.y = bounds.y;
+                    if (pickerSelector.y > bounds.y + bounds.height) pickerSelector.y = bounds.y + bounds.height;
+
+                    // Calculate color from picker
+                    Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                    colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                    colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                    colorHsv->y = colorPick.x;
+                    colorHsv->z = 1.0f - colorPick.y;
+
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON))
+            {
+                state = STATE_PRESSED;
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds;
+                pickerSelector = mousePoint;
+
+                // Calculate color from picker
+                Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y };
+
+                colorPick.x /= (float)bounds.width;     // Get normalized value on x
+                colorPick.y /= (float)bounds.height;    // Get normalized value on y
+
+                colorHsv->y = colorPick.x;
+                colorHsv->z = 1.0f - colorPick.y;
+            }
+            else state = STATE_FOCUSED;
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state != STATE_DISABLED)
+    {
+        DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha));
+        DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0));
+
+        // Draw color picker: selector
+        Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) };
+        GuiDrawRectangle(selector, 0, BLANK, colWhite);
+    }
+    else
+    {
+        DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha));
+    }
+
+    GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Message Box control
+int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons)
+{
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_HEIGHT)
+        #define RAYGUI_MESSAGEBOX_BUTTON_HEIGHT    24
+    #endif
+    #if !defined(RAYGUI_MESSAGEBOX_BUTTON_PADDING)
+        #define RAYGUI_MESSAGEBOX_BUTTON_PADDING   12
+    #endif
+
+    int result = -1;    // Returns clicked button from buttons list, 0 refers to closed window button
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    //int textWidth = GuiGetTextWidth(message) + 2;
+
+    Rectangle textBounds = { 0 };
+    textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
+    textBounds.width = bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*2;
+    textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiLabel(textBounds, message);
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+
+    prevTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;
+}
+
+// Text Input Box control, ask for text
+int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive)
+{
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT      24
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING)
+        #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING     12
+    #endif
+    #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT)
+        #define RAYGUI_TEXTINPUTBOX_HEIGHT             26
+    #endif
+
+    // Used to enable text edit mode
+    // WARNING: No more than one GuiTextInputBox() should be open at the same time
+    static bool textEditMode = false;
+
+    int result = -1;
+
+    int buttonCount = 0;
+    const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL);
+    Rectangle buttonBounds = { 0 };
+    buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    buttonBounds.width = (bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
+    buttonBounds.height = RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT;
+
+    int messageInputHeight = (int)bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - 2*RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+
+    Rectangle textBounds = { 0 };
+    if (message != NULL)
+    {
+        int textSize = GuiGetTextWidth(message) + 2;
+
+        textBounds.x = bounds.x + bounds.width/2 - textSize/2;
+        textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
+        textBounds.width = (float)textSize;
+        textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+    }
+
+    Rectangle textBoxBounds = { 0 };
+    textBoxBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    textBoxBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - RAYGUI_TEXTINPUTBOX_HEIGHT/2;
+    if (message == NULL) textBoxBounds.y = bounds.y + 24 + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING;
+    else textBoxBounds.y += (messageInputHeight/2 + messageInputHeight/4);
+    textBoxBounds.width = bounds.width - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING*2;
+    textBoxBounds.height = RAYGUI_TEXTINPUTBOX_HEIGHT;
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (GuiWindowBox(bounds, title)) result = 0;
+
+    // Draw message if available
+    if (message != NULL)
+    {
+        int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(textBounds, message);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, prevTextAlignment);
+    }
+
+    if (secretViewActive != NULL)
+    {
+        static char stars[] = "****************";
+        if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height },
+            ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+
+        GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive);
+    }
+    else
+    {
+        if (GuiTextBox(textBoxBounds, text, textMaxSize, textEditMode)) textEditMode = !textEditMode;
+    }
+
+    int prevBtnTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    for (int i = 0; i < buttonCount; i++)
+    {
+        if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1;
+        buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING);
+    }
+
+    if (result >= 0) textEditMode = false;
+
+    GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment);
+    //--------------------------------------------------------------------
+
+    return result;      // Result is the pressed button index
+}
+
+// Grid control
+// NOTE: Returns grid mouse-hover selected cell
+// About drawing lines at subpixel spacing, simple put, not easy solution:
+// https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster
+int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell)
+{
+    // Grid lines alpha amount
+    #if !defined(RAYGUI_GRID_ALPHA)
+        #define RAYGUI_GRID_ALPHA    0.15f
+    #endif
+
+    int result = 0;
+    GuiState state = guiState;
+
+    Vector2 mousePoint = GetMousePosition();
+    Vector2 currentMouseCell = { -1, -1 };
+
+    float spaceWidth = spacing/(float)subdivs;
+    int linesV = (int)(bounds.width/spaceWidth) + 1;
+    int linesH = (int)(bounds.height/spaceWidth) + 1;
+
+    int color = GuiGetStyle(DEFAULT, LINE_COLOR);
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
+    {
+        if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            // NOTE: Cell values must be the upper left of the cell the mouse is in
+            currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing);
+            currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing);
+        }
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    if (state == STATE_DISABLED) color = GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED);
+
+    if (subdivs > 0)
+    {
+        // Draw vertical grid lines
+        for (int i = 0; i < linesV; i++)
+        {
+            Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height + 1 };
+            GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+
+        // Draw horizontal grid lines
+        for (int i = 0; i < linesH; i++)
+        {
+            Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width + 1, 1 };
+            GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(color), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(color), RAYGUI_GRID_ALPHA));
+        }
+    }
+
+    if (mouseCell != NULL) *mouseCell = currentMouseCell;
+    return result;
+}
+
+//----------------------------------------------------------------------------------
+// Tooltip management functions
+// NOTE: Tooltips requires some global variables: tooltipPtr
+//----------------------------------------------------------------------------------
+// Enable gui tooltips (global state)
+void GuiEnableTooltip(void) { guiTooltip = true; }
+
+// Disable gui tooltips (global state)
+void GuiDisableTooltip(void) { guiTooltip = false; }
+
+// Set tooltip string
+void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; }
+
+//----------------------------------------------------------------------------------
+// Styles loading functions
+//----------------------------------------------------------------------------------
+
+// Load raygui style file (.rgs)
+// NOTE: By default a binary file is expected, that file could contain a custom font,
+// in that case, custom font image atlas is GRAY+ALPHA and pixel data can be compressed (DEFLATE)
+void GuiLoadStyle(const char *fileName)
+{
+    #define MAX_LINE_BUFFER_SIZE    256
+
+    bool tryBinary = false;
+    if (!guiStyleLoaded) GuiLoadStyleDefault();
+
+    // Try reading the files as text file first
+    FILE *rgsFile = fopen(fileName, "rt");
+
+    if (rgsFile != NULL)
+    {
+        char buffer[MAX_LINE_BUFFER_SIZE] = { 0 };
+        fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+
+        if (buffer[0] == '#')
+        {
+            int controlId = 0;
+            int propertyId = 0;
+            unsigned int propertyValue = 0;
+
+            while (!feof(rgsFile))
+            {
+                switch (buffer[0])
+                {
+                    case 'p':
+                    {
+                        // Style property: p <control_id> <property_id> <property_value> <property_name>
+
+                        sscanf(buffer, "p %d %d 0x%x", &controlId, &propertyId, &propertyValue);
+                        GuiSetStyle(controlId, propertyId, (int)propertyValue);
+
+                    } break;
+                    case 'f':
+                    {
+                        // Style font: f <gen_font_size> <charmap_file> <font_file>
+
+                        int fontSize = 0;
+                        char charmapFileName[256] = { 0 };
+                        char fontFileName[256] = { 0 };
+                        sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName);
+
+                        Font font = { 0 };
+                        int *codepoints = NULL;
+                        int codepointCount = 0;
+
+                        if (charmapFileName[0] != '0')
+                        {
+                            // Load text data from file
+                            // NOTE: Expected an UTF-8 array of codepoints, no separation
+                            char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName));
+                            codepoints = LoadCodepoints(textData, &codepointCount);
+                            UnloadFileText(textData);
+                        }
+
+                        if (fontFileName[0] != '\0')
+                        {
+                            // In case a font is already loaded and it is not default internal font, unload it
+                            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+
+                            if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount);
+                            else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0);   // Default to 95 standard codepoints
+                        }
+
+                        // If font texture not properly loaded, revert to default font and size/spacing
+                        if (font.texture.id == 0)
+                        {
+                            font = GetFontDefault();
+                            GuiSetStyle(DEFAULT, TEXT_SIZE, 10);
+                            GuiSetStyle(DEFAULT, TEXT_SPACING, 1);
+                        }
+
+                        UnloadCodepoints(codepoints);
+
+                        if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font);
+
+                    } break;
+                    default: break;
+                }
+
+                fgets(buffer, MAX_LINE_BUFFER_SIZE, rgsFile);
+            }
+        }
+        else tryBinary = true;
+
+        fclose(rgsFile);
+    }
+
+    if (tryBinary)
+    {
+        rgsFile = fopen(fileName, "rb");
+
+        if (rgsFile != NULL)
+        {
+            fseek(rgsFile, 0, SEEK_END);
+            int fileDataSize = ftell(rgsFile);
+            fseek(rgsFile, 0, SEEK_SET);
+
+            if (fileDataSize > 0)
+            {
+                unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
+                fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
+
+                GuiLoadStyleFromMemory(fileData, fileDataSize);
+
+                RAYGUI_FREE(fileData);
+            }
+
+            fclose(rgsFile);
+        }
+    }
+}
+
+// Load style default over global style
+void GuiLoadStyleDefault(void)
+{
+    // We set this variable first to avoid cyclic function calls
+    // when calling GuiSetStyle() and GuiGetStyle()
+    guiStyleLoaded = true;
+
+    // Initialize default LIGHT style property values
+    // WARNING: Default value are applied to all controls on set but
+    // they can be overwritten later on for every custom control
+    GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_FOCUSED, 0x5bb2d9ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_FOCUSED, 0xc9effeff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_FOCUSED, 0x6c9bbcff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_PRESSED, 0x0492c7ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_PRESSED, 0x97e8ffff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_PRESSED, 0x368bafff);
+    GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff);
+    GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff);
+    GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff);
+    GuiSetStyle(DEFAULT, BORDER_WIDTH, 1);
+    GuiSetStyle(DEFAULT, TEXT_PADDING, 0);
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+
+    // Initialize default extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(DEFAULT, TEXT_SIZE, 10);                // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, TEXT_SPACING, 1);              // DEFAULT, shared by all controls
+    GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff);       // DEFAULT specific property
+    GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property
+    GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15);        // DEFAULT, 15 pixels between lines
+    GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE);   // DEFAULT, text aligned vertically to middle of text-bounds
+
+    // Initialize control-specific property values
+    // NOTE: Those properties are in default list but require specific values by control type
+    GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(BUTTON, BORDER_WIDTH, 2);
+    GuiSetStyle(SLIDER, TEXT_PADDING, 4);
+    GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_PADDING, 4);
+    GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT);
+    GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0);
+    GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+    GuiSetStyle(TEXTBOX, TEXT_PADDING, 4);
+    GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
+    GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+    GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
+    GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
+
+    // Initialize extended property values
+    // NOTE: By default, extended property values are initialized to 0
+    GuiSetStyle(TOGGLE, GROUP_PADDING, 2);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 16);
+    GuiSetStyle(SLIDER, SLIDER_PADDING, 1);
+    GuiSetStyle(PROGRESSBAR, PROGRESS_PADDING, 1);
+    GuiSetStyle(CHECKBOX, CHECK_PADDING, 1);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_WIDTH, 32);
+    GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
+    GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
+    GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
+    GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
+    GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16);
+    GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0);
+    GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
+    GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
+    GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_WIDTH, 16);
+    GuiSetStyle(COLORPICKER, HUEBAR_PADDING, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8);
+    GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2);
+
+    if (guiFont.texture.id != GetFontDefault().texture.id)
+    {
+        // Unload previous font texture
+        UnloadTexture(guiFont.texture);
+        RAYGUI_FREE(guiFont.recs);
+        RAYGUI_FREE(guiFont.glyphs);
+        guiFont.recs = NULL;
+        guiFont.glyphs = NULL;
+
+        // Setup default raylib font
+        guiFont = GetFontDefault();
+
+        // NOTE: Default raylib font character 95 is a white square
+        Rectangle whiteChar = guiFont.recs[95];
+
+        // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering
+        SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 });
+    }
+}
+
+// Get text with icon id prepended
+// NOTE: Useful to add icons by name id (enum) instead of
+// a number that can change between ricon versions
+const char *GuiIconText(int iconId, const char *text)
+{
+#if defined(RAYGUI_NO_ICONS)
+    return NULL;
+#else
+    static char buffer[1024] = { 0 };
+    static char iconBuffer[16] = { 0 };
+
+    if (text != NULL)
+    {
+        memset(buffer, 0, 1024);
+        snprintf(buffer, 1024, "#%03i#", iconId);
+
+        for (int i = 5; i < 1024; i++)
+        {
+            buffer[i] = text[i - 5];
+            if (text[i - 5] == '\0') break;
+        }
+
+        return buffer;
+    }
+    else
+    {
+        snprintf(iconBuffer, 16, "#%03i#", iconId);
+
+        return iconBuffer;
+    }
+#endif
+}
+
+#if !defined(RAYGUI_NO_ICONS)
+// Get full icons data pointer
+unsigned int *GuiGetIcons(void) { return guiIconsPtr; }
+
+// Load raygui icons file (.rgi)
+// NOTE: In case nameIds are required, they can be requested with loadIconsName,
+// they are returned as a guiIconsName[iconCount][RAYGUI_ICON_MAX_NAME_LENGTH],
+// WARNING: guiIconsName[]][] memory should be manually freed!
+char **GuiLoadIcons(const char *fileName, bool loadIconsName)
+{
+    // Style File Structure (.rgi)
+    // ------------------------------------------------------
+    // Offset  | Size    | Type       | Description
+    // ------------------------------------------------------
+    // 0       | 4       | char       | Signature: "rGI "
+    // 4       | 2       | short      | Version: 100
+    // 6       | 2       | short      | reserved
+
+    // 8       | 2       | short      | Num icons (N)
+    // 10      | 2       | short      | Icons size (Options: 16, 32, 64) (S)
+
+    // Icons name id (32 bytes per name id)
+    // foreach (icon)
+    // {
+    //   12+32*i  | 32   | char       | Icon NameId
+    // }
+
+    // Icons data: One bit per pixel, stored as unsigned int array (depends on icon size)
+    // S*S pixels/32bit per unsigned int = K unsigned int per icon
+    // foreach (icon)
+    // {
+    //   ...   | K       | unsigned int | Icon Data
+    // }
+
+    FILE *rgiFile = fopen(fileName, "rb");
+
+    char **guiIconsName = NULL;
+
+    if (rgiFile != NULL)
+    {
+        char signature[5] = { 0 };
+        short version = 0;
+        short reserved = 0;
+        short iconCount = 0;
+        short iconSize = 0;
+
+        fread(signature, 1, 4, rgiFile);
+        fread(&version, sizeof(short), 1, rgiFile);
+        fread(&reserved, sizeof(short), 1, rgiFile);
+        fread(&iconCount, sizeof(short), 1, rgiFile);
+        fread(&iconSize, sizeof(short), 1, rgiFile);
+
+        if ((signature[0] == 'r') &&
+            (signature[1] == 'G') &&
+            (signature[2] == 'I') &&
+            (signature[3] == ' '))
+        {
+            if (loadIconsName)
+            {
+                guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+                for (int i = 0; i < iconCount; i++)
+                {
+                    guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                    fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
+                }
+            }
+            else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
+
+            // Read icons data directly over internal icons array
+            fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
+        }
+
+        fclose(rgiFile);
+    }
+
+    return guiIconsName;
+}
+
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+    char **guiIconsName = NULL;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    short iconCount = 0;
+    short iconSize = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+    memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'I') &&
+        (signature[3] == ' '))
+    {
+        if (loadIconsName)
+        {
+            guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+            for (int i = 0; i < iconCount; i++)
+            {
+                guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+                fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+            }
+        }
+        else
+        {
+            // Skip icon name data if not required
+            fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+        }
+
+        int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+        guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+        memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+    }
+
+    return guiIconsName;
+}
+
+// Draw selected icon using rectangles pixel-by-pixel
+void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
+{
+    #define BIT_CHECK(a,b) ((a) & (1u<<(b)))
+
+    for (int i = 0, y = 0; i < RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32; i++)
+    {
+        for (int k = 0; k < 32; k++)
+        {
+            if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k))
+            {
+            #if !defined(RAYGUI_STANDALONE)
+                GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color);
+            #endif
+            }
+
+            if ((k == 15) || (k == 31)) y++;
+        }
+    }
+}
+
+// Set icon drawing size
+void GuiSetIconScale(int scale)
+{
+    if (scale >= 1) guiIconScale = scale;
+}
+
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    Vector2 textSize = { 0 };
+    int textIconOffset = 0;
+
+    if ((text != NULL) && (text[0] != '\0'))
+    {
+        if (text[0] == '#')
+        {
+            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+            {
+                if (text[i] == '#')
+                {
+                    textIconOffset = i;
+                    break;
+                }
+            }
+        }
+
+        text += textIconOffset;
+
+        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+        // Custom MeasureText() implementation
+        if ((guiFont.texture.id > 0) && (text != NULL))
+        {
+            // Get size in bytes of text, considering end of line and line break
+            int size = 0;
+            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+            {
+                if ((text[i] != '\0') && (text[i] != '\n')) size++;
+                else break;
+            }
+
+            float scaleFactor = fontSize/(float)guiFont.baseSize;
+            textSize.y = (float)guiFont.baseSize*scaleFactor;
+            float glyphWidth = 0.0f;
+
+            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+            {
+                int codepoint = GetCodepointNext(&text[i], &codepointSize);
+                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+    }
+
+    return (int)textSize.x;
+}
+
+#endif      // !RAYGUI_NO_ICONS
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Load style from memory
+// WARNING: Binary files only
+static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    int propertyCount = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'S') &&
+        (signature[3] == ' '))
+    {
+        short controlId = 0;
+        short propertyId = 0;
+        unsigned int propertyValue = 0;
+
+        for (int i = 0; i < propertyCount; i++)
+        {
+            memcpy(&controlId, fileDataPtr, sizeof(short));
+            memcpy(&propertyId, fileDataPtr + 2, sizeof(short));
+            memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int));
+            fileDataPtr += 8;
+
+            if (controlId == 0) // DEFAULT control
+            {
+                // If a DEFAULT property is loaded, it is propagated to all controls
+                // NOTE: All DEFAULT properties should be defined first in the file
+                GuiSetStyle(0, (int)propertyId, propertyValue);
+
+                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue);
+            }
+            else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
+        }
+
+        // Font loading is highly dependant on raylib API to load font data and image
+
+#if !defined(RAYGUI_STANDALONE)
+        // Load custom font if available
+        int fontDataSize = 0;
+        memcpy(&fontDataSize, fileDataPtr, sizeof(int));
+        fileDataPtr += 4;
+
+        if (fontDataSize > 0)
+        {
+            Font font = { 0 };
+            int fontType = 0;   // 0-Normal, 1-SDF
+
+            memcpy(&font.baseSize, fileDataPtr, sizeof(int));
+            memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int));
+            memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            // Load font white rectangle
+            Rectangle fontWhiteRec = { 0 };
+            memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle));
+            fileDataPtr += 16;
+
+            // Load font image parameters
+            int fontImageUncompSize = 0;
+            int fontImageCompSize = 0;
+            memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int));
+            memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int));
+            fileDataPtr += 8;
+
+            Image imFont = { 0 };
+            imFont.mipmaps = 1;
+            memcpy(&imFont.width, fileDataPtr, sizeof(int));
+            memcpy(&imFont.height, fileDataPtr + 4, sizeof(int));
+            memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int));
+            fileDataPtr += 12;
+
+            if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize))
+            {
+                // Compressed font atlas image data (DEFLATE), it requires DecompressData()
+                int dataUncompSize = 0;
+                unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
+                memcpy(compData, fileDataPtr, fontImageCompSize);
+                fileDataPtr += fontImageCompSize;
+
+                imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize);
+
+                // Security check, dataUncompSize must match the provided fontImageUncompSize
+                if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted");
+
+                RAYGUI_FREE(compData);
+            }
+            else
+            {
+                // Font atlas image data is not compressed
+                imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
+                memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
+                fileDataPtr += fontImageUncompSize;
+            }
+
+            if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture);
+            font.texture = LoadTextureFromImage(imFont);
+
+            RAYGUI_FREE(imFont.data);
+
+            // Validate font atlas texture was loaded correctly
+            if (font.texture.id != 0)
+            {
+                // Load font recs data
+                int recsDataSize = font.glyphCount*sizeof(Rectangle);
+                int recsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed recs data
+                    memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
+                {
+                    // Recs data is compressed, uncompress it
+                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
+                    fileDataPtr += recsDataCompressedSize;
+
+                    int recsDataUncompSize = 0;
+                    font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted");
+
+                    RAYGUI_FREE(recsDataCompressed);
+                }
+                else
+                {
+                    // Recs data is uncompressed
+                    font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle));
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle));
+                        fileDataPtr += sizeof(Rectangle);
+                    }
+                }
+
+                // Load font glyphs info data
+                int glyphsDataSize = font.glyphCount*16;    // 16 bytes data per glyph
+                int glyphsDataCompressedSize = 0;
+
+                // WARNING: Version 400 adds the compression size parameter
+                if (version >= 400)
+                {
+                    // RGS files version 400 support compressed glyphs data
+                    memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int));
+                    fileDataPtr += sizeof(int);
+                }
+
+                // Allocate required glyphs space to fill with data
+                font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo));
+
+                if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
+                {
+                    // Glyphs data is compressed, uncompress it
+                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
+
+                    memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
+                    fileDataPtr += glyphsDataCompressedSize;
+
+                    int glyphsDataUncompSize = 0;
+                    unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize);
+
+                    // Security check, data uncompressed size must match the expected original data size
+                    if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted");
+
+                    unsigned char *glyphsDataUncompPtr = glyphsDataUncomp;
+
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int));
+                        glyphsDataUncompPtr += 16;
+                    }
+
+                    RAYGUI_FREE(glypsDataCompressed);
+                    RAYGUI_FREE(glyphsDataUncomp);
+                }
+                else
+                {
+                    // Glyphs data is uncompressed
+                    for (int i = 0; i < font.glyphCount; i++)
+                    {
+                        memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int));
+                        memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int));
+                        memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int));
+                        fileDataPtr += 16;
+                    }
+                }
+            }
+            else font = GetFontDefault();   // Fallback in case of errors loading font atlas texture
+
+            GuiSetFont(font);
+
+            // Set font texture source rectangle to be used as white texture to draw shapes
+            // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call
+            if ((fontWhiteRec.x > 0) &&
+                (fontWhiteRec.y > 0) &&
+                (fontWhiteRec.width > 0) &&
+                (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec);
+        }
+#endif
+    }
+}
+
+// Get text bounds considering control bounds
+static Rectangle GetTextBounds(int control, Rectangle bounds)
+{
+    Rectangle textBounds = bounds;
+
+    textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH);
+    textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING);
+    textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);
+    textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING);    // NOTE: Text is processed line per line!
+
+    // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds
+    switch (control)
+    {
+        case COMBOBOX:
+        case DROPDOWNBOX:
+        case LISTVIEW:
+            // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW
+        case SLIDER:
+        case CHECKBOX:
+        case VALUEBOX:
+        case CONTROL11:
+            // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
+        default:
+        {
+            // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText()
+            if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING);
+            else textBounds.x += GuiGetStyle(control, TEXT_PADDING);
+        }
+        break;
+    }
+
+    return textBounds;
+}
+
+// Get text icon if provided and move text cursor
+// NOTE: We support up to 999 values for iconId
+static const char *GetTextIcon(const char *text, int *iconId)
+{
+#if !defined(RAYGUI_NO_ICONS)
+    *iconId = -1;
+    if (text[0] == '#')     // Maybe we have an icon!
+    {
+        char iconValue[4] = { 0 };  // Maximum length for icon value: 3 digits + '\0'
+
+        int pos = 1;
+        while ((pos < 4) && (text[pos] >= '0') && (text[pos] <= '9'))
+        {
+            iconValue[pos - 1] = text[pos];
+            pos++;
+        }
+
+        if (text[pos] == '#')
+        {
+            *iconId = TextToInteger(iconValue);
+
+            // Move text pointer after icon
+            // WARNING: If only icon provided, it could point to EOL character: '\0'
+            if (*iconId >= 0) text += (pos + 1);
+        }
+    }
+#endif
+
+    return text;
+}
+
+// Get text divided into lines (by line-breaks '\n')
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
+{
+    #define RAYGUI_MAX_TEXT_LINES   128
+
+    static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 };
+    for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL;    // Init NULL pointers to substrings
+
+    int textSize = (int)strlen(text);
+
+    lines[0] = text;
+    *count = 1;
+
+    for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
+    {
+        if (text[i] == '\n')
+        {
+            k++;
+            lines[k] = &text[i + 1]; // WARNING: next value is valid?
+            *count += 1;
+        }
+    }
+
+    return lines;
+}
+
+// Get text width to next space for provided string
+static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex)
+{
+    float width = 0;
+    int codepointByteCount = 0;
+    int codepoint = 0;
+    int index = 0;
+    float glyphWidth = 0;
+    float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+    for (int i = 0; text[i] != '\0'; i++)
+    {
+        if (text[i] != ' ')
+        {
+            codepoint = GetCodepoint(&text[i], &codepointByteCount);
+            index = GetGlyphIndex(guiFont, codepoint);
+            glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor;
+            width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+        }
+        else
+        {
+            *nextSpaceIndex = i;
+            break;
+        }
+    }
+
+    return width;
+}
+
+// Gui draw text using default font
+static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint)
+{
+    #define TEXT_VALIGN_PIXEL_OFFSET(h)  ((int)h%2)     // Vertical alignment for pixel perfect
+
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    if ((text == NULL) || (text[0] == '\0')) return;    // Security check
+
+    // PROCEDURE:
+    //   - Text is processed line per line
+    //   - For every line, horizontal alignment is defined
+    //   - For all text, vertical alignment is defined (multiline text only)
+    //   - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only)
+
+    // Get text lines (using '\n' as delimiter) to be processed individually
+    // WARNING: We can't use GuiTextSplit() function because it can be already used
+    // before the GuiDrawText() call and its buffer is static, it would be overriden :(
+    int lineCount = 0;
+    const char **lines = GetTextLines(text, &lineCount);
+
+    // Text style variables
+    //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT);
+    int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL);
+    int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);    // Wrap-mode only available in read-only mode, no for text editing
+
+    // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap
+    float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2);
+    float posOffsetY = 0.0f;
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        int iconId = 0;
+        lines[i] = GetTextIcon(lines[i], &iconId);      // Check text for icon and move cursor
+
+        // Get text position depending on alignment and iconId
+        //---------------------------------------------------------------------------------
+        Vector2 textBoundsPosition = { textBounds.x, textBounds.y };
+        float textBoundsWidthOffset = 0.0f;
+
+        // NOTE: We get text size after icon has been processed
+        // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+        int textSizeX = GuiGetTextWidth(lines[i]);
+
+        // If text requires an icon, add size to measure
+        if (iconId >= 0)
+        {
+            textSizeX += RAYGUI_ICON_SIZE*guiIconScale;
+
+            // WARNING: If only icon provided, text could be pointing to EOF character: '\0'
+#if !defined(RAYGUI_NO_ICONS)
+            if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING;
+#endif
+        }
+
+        // Check guiTextAlign global variables
+        switch (alignment)
+        {
+            case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break;
+            case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x +  textBounds.width/2 - textSizeX/2; break;
+            case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break;
+            default: break;
+        }
+
+        if (textSizeX > textBounds.width && (lines[i] != NULL) && (lines[i][0] != '\0')) textBoundsPosition.x = textBounds.x;
+
+        switch (alignmentVertical)
+        {
+            // Only valid in case of wordWrap = 0;
+            case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break;
+            case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break;
+            default: break;
+        }
+
+        // NOTE: Make sure we get pixel-perfect coordinates,
+        // In case of decimals we got weird text positioning
+        textBoundsPosition.x = (float)((int)textBoundsPosition.x);
+        textBoundsPosition.y = (float)((int)textBoundsPosition.y);
+        //---------------------------------------------------------------------------------
+
+        // Draw text (with icon if available)
+        //---------------------------------------------------------------------------------
+#if !defined(RAYGUI_NO_ICONS)
+        if (iconId >= 0)
+        {
+            // NOTE: We consider icon height, probably different than text size
+            GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint);
+            textBoundsPosition.x += (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+            textBoundsWidthOffset = (float)(RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING);
+        }
+#endif
+        // Get size in bytes of text,
+        // considering end of line and line break
+        int lineSize = 0;
+        for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ }
+        float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize;
+
+        int lastSpaceIndex = 0;
+        bool tempWrapCharMode = false;
+
+        int textOffsetY = 0;
+        float textOffsetX = 0.0f;
+        float glyphWidth = 0;
+
+        int ellipsisWidth = GuiGetTextWidth("...");
+        bool textOverflow = false;
+        for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
+        {
+            int codepoint = GetCodepointNext(&lines[i][c], &codepointSize);
+            int index = GetGlyphIndex(guiFont, codepoint);
+
+            // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+            // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+            if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size
+
+            // Get glyph width to check if it goes out of bounds
+            if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor);
+            else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor;
+
+            // Wrap mode text measuring, to validate if
+            // it can be drawn or a new line is required
+            if (wrapMode == TEXT_WRAP_CHAR)
+            {
+                // Jump to next line if current character reach end of the box limits
+                if ((textOffsetX + glyphWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+
+                    if (tempWrapCharMode)   // Wrap at char level when too long words
+                    {
+                        wrapMode = TEXT_WRAP_WORD;
+                        tempWrapCharMode = false;
+                    }
+                }
+            }
+            else if (wrapMode == TEXT_WRAP_WORD)
+            {
+                if (codepoint == 32) lastSpaceIndex = c;
+
+                // Get width to next space in line
+                int nextSpaceIndex = 0;
+                float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex);
+
+                int nextSpaceIndex2 = 0;
+                float nextWordSize = GetNextSpaceWidth(lines[i] + lastSpaceIndex + 1, &nextSpaceIndex2);
+
+                if (nextWordSize > textBounds.width - textBoundsWidthOffset)
+                {
+                    // Considering the case the next word is longer than bounds
+                    tempWrapCharMode = true;
+                    wrapMode = TEXT_WRAP_CHAR;
+                }
+                else if ((textOffsetX + nextSpaceWidth) > textBounds.width - textBoundsWidthOffset)
+                {
+                    textOffsetX = 0.0f;
+                    textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+                }
+            }
+
+            if (codepoint == '\n') break;   // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint
+            else
+            {
+                // TODO: There are multiple types of spaces in Unicode,
+                // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html
+                if ((codepoint != ' ') && (codepoint != '\t'))      // Do not draw codepoints with no glyph
+                {
+                    if (wrapMode == TEXT_WRAP_NONE)
+                    {
+                        // Draw only required text glyphs fitting the textBounds.width
+                        if (textSizeX > textBounds.width)
+                        {
+                            if (textOffsetX <= (textBounds.width - glyphWidth - textBoundsWidthOffset - ellipsisWidth))
+                            {
+                                DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                            }
+                            else if (!textOverflow)
+                            {
+                                textOverflow = true;
+
+                                for (int j = 0; j < ellipsisWidth; j += ellipsisWidth/3)
+                                {
+                                    DrawTextCodepoint(guiFont, '.', RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX + j, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                                }
+                            }
+                        }
+                        else
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                    else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD))
+                    {
+                        // Draw only glyphs inside the bounds
+                        if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE)))
+                        {
+                            DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha));
+                        }
+                    }
+                }
+
+                if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+                else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING);
+        else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING));
+        //---------------------------------------------------------------------------------
+    }
+
+#if defined(RAYGUI_DEBUG_TEXT_BOUNDS)
+    GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f));
+#endif
+}
+
+// Gui draw rectangle using default raygui plain style with borders
+static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color)
+{
+    if (color.a > 0)
+    {
+        // Draw rectangle filled with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha));
+    }
+
+    if (borderWidth > 0)
+    {
+        // Draw rectangle border lines with color
+        DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha));
+        DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha));
+    }
+
+#if defined(RAYGUI_DEBUG_RECS_BOUNDS)
+    DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f));
+#endif
+}
+
+// Draw tooltip using control bounds
+static void GuiTooltip(Rectangle controlRec)
+{
+    if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiControlExclusiveMode)
+    {
+        Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+
+        if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
+
+        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
+
+        int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
+        int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
+        GuiSetStyle(LABEL, TEXT_PADDING, 0);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
+        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
+        GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
+        GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
+    }
+}
+
+// Split controls text into multiple strings
+// Also check for multiple columns (required by GuiToggleGroup())
+static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+    // NOTE: Those definitions could be externally provided if required
+
+    // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user
+    // textRow is an externally provided array of integers that stores row number for every splitted string
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE     1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };   // String pointers array (points to buffer data)
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };         // Buffer data (text input copy with '\0' added)
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 1;
+
+    if (textRow != NULL) textRow[0] = 0;
+
+    // Count how many substrings we have on text and point to every one
+    for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+    {
+        buffer[i] = text[i];
+        if (buffer[i] == '\0') break;
+        else if ((buffer[i] == delimiter) || (buffer[i] == '\n'))
+        {
+            result[counter] = buffer + i + 1;
+
+            if (textRow != NULL)
+            {
+                if (buffer[i] == '\n') textRow[counter] = textRow[counter - 1] + 1;
+                else textRow[counter] = textRow[counter - 1];
+            }
+
+            buffer[i] = '\0';   // Set an end of string at this point
+
+            counter++;
+            if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+        }
+    }
+
+    *count = counter;
+
+    return result;
+}
+
+// Convert color data from RGB to HSV
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertRGBtoHSV(Vector3 rgb)
+{
+    Vector3 hsv = { 0 };
+    float min = 0.0f;
+    float max = 0.0f;
+    float delta = 0.0f;
+
+    min = (rgb.x < rgb.y)? rgb.x : rgb.y;
+    min = (min < rgb.z)? min  : rgb.z;
+
+    max = (rgb.x > rgb.y)? rgb.x : rgb.y;
+    max = (max > rgb.z)? max  : rgb.z;
+
+    hsv.z = max;            // Value
+    delta = max - min;
+
+    if (delta < 0.00001f)
+    {
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    if (max > 0.0f)
+    {
+        // NOTE: If max is 0, this divide would cause a crash
+        hsv.y = (delta/max);    // Saturation
+    }
+    else
+    {
+        // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
+        hsv.y = 0.0f;
+        hsv.x = 0.0f;           // Undefined, maybe NAN?
+        return hsv;
+    }
+
+    // NOTE: Comparing float values could not work properly
+    if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta;    // Between yellow & magenta
+    else
+    {
+        if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta;  // Between cyan & yellow
+        else hsv.x = 4.0f + (rgb.x - rgb.y)/delta;      // Between magenta & cyan
+    }
+
+    hsv.x *= 60.0f;     // Convert to degrees
+
+    if (hsv.x < 0.0f) hsv.x += 360.0f;
+
+    return hsv;
+}
+
+// Convert color data from HSV to RGB
+// NOTE: Color data should be passed normalized
+static Vector3 ConvertHSVtoRGB(Vector3 hsv)
+{
+    Vector3 rgb = { 0 };
+    float hh = 0.0f, p = 0.0f, q = 0.0f, t = 0.0f, ff = 0.0f;
+    long i = 0;
+
+    // NOTE: Comparing float values could not work properly
+    if (hsv.y <= 0.0f)
+    {
+        rgb.x = hsv.z;
+        rgb.y = hsv.z;
+        rgb.z = hsv.z;
+        return rgb;
+    }
+
+    hh = hsv.x;
+    if (hh >= 360.0f) hh = 0.0f;
+    hh /= 60.0f;
+
+    i = (long)hh;
+    ff = hh - i;
+    p = hsv.z*(1.0f - hsv.y);
+    q = hsv.z*(1.0f - (hsv.y*ff));
+    t = hsv.z*(1.0f - (hsv.y*(1.0f - ff)));
+
+    switch (i)
+    {
+        case 0:
+        {
+            rgb.x = hsv.z;
+            rgb.y = t;
+            rgb.z = p;
+        } break;
+        case 1:
+        {
+            rgb.x = q;
+            rgb.y = hsv.z;
+            rgb.z = p;
+        } break;
+        case 2:
+        {
+            rgb.x = p;
+            rgb.y = hsv.z;
+            rgb.z = t;
+        } break;
+        case 3:
+        {
+            rgb.x = p;
+            rgb.y = q;
+            rgb.z = hsv.z;
+        } break;
+        case 4:
+        {
+            rgb.x = t;
+            rgb.y = p;
+            rgb.z = hsv.z;
+        } break;
+        case 5:
+        default:
+        {
+            rgb.x = hsv.z;
+            rgb.y = p;
+            rgb.z = q;
+        } break;
+    }
+
+    return rgb;
+}
+
+// Scroll bar control (used by GuiScrollPanel())
+static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue)
+{
+    GuiState state = guiState;
+
+    // Is the scrollbar horizontal or vertical?
+    bool isVertical = (bounds.width > bounds.height)? false : true;
+
+    // The size (width or height depending on scrollbar type) of the spinner buttons
+    const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)?
+        (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) :
+        (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0;
+
+    // Arrow buttons [<] [>] [∧] [∨]
+    Rectangle arrowUpLeft = { 0 };
+    Rectangle arrowDownRight = { 0 };
+
+    // Actual area of the scrollbar excluding the arrow buttons
+    Rectangle scrollbar = { 0 };
+
+    // Slider bar that moves     --[///]-----
+    Rectangle slider = { 0 };
+
+    // Normalize value
+    if (value > maxValue) value = maxValue;
+    if (value < minValue) value = minValue;
+
+    int valueRange = maxValue - minValue;
+    if (valueRange <= 0) valueRange = 1;
+
+    int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE);
+    if (sliderSize < 1) sliderSize = 1;  // TODO: Consider a minimum slider size
+
+    // Calculate rectangles for all of the components
+    arrowUpLeft = RAYGUI_CLITERAL(Rectangle){
+        (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH),
+        (float)spinnerSize, (float)spinnerSize };
+
+    if (isVertical)
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)),
+            bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)),
+            (float)sliderSize };
+    }
+    else    // horizontal
+    {
+        arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize };
+        scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) };
+
+        // Make sure the slider won't get outside of the scrollbar
+        sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize;
+        slider = RAYGUI_CLITERAL(Rectangle){
+            scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)),
+            bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING),
+            (float)sliderSize,
+            bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) };
+    }
+
+    // Update control
+    //--------------------------------------------------------------------
+    if ((state != STATE_DISABLED) && !guiLocked)
+    {
+        Vector2 mousePoint = GetMousePosition();
+
+        if (guiControlExclusiveMode) // Allows to keep dragging outside of bounds
+        {
+            if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) &&
+                !CheckCollisionPointRec(mousePoint, arrowUpLeft) &&
+                !CheckCollisionPointRec(mousePoint, arrowDownRight))
+            {
+                if (CHECK_BOUNDS_ID(bounds, guiControlExclusiveRec))
+                {
+                    state = STATE_PRESSED;
+
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+            }
+            else
+            {
+                guiControlExclusiveMode = false;
+                guiControlExclusiveRec = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 };
+            }
+        }
+        else if (CheckCollisionPointRec(mousePoint, bounds))
+        {
+            state = STATE_FOCUSED;
+
+            // Handle mouse wheel
+            int wheel = (int)GetMouseWheelMove();
+            if (wheel != 0) value += wheel;
+
+            // Handle mouse button down
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
+            {
+                guiControlExclusiveMode = true;
+                guiControlExclusiveRec = bounds; // Store bounds as an identifier when dragging starts
+
+                // Check arrows click
+                if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED);
+                else if (!CheckCollisionPointRec(mousePoint, slider))
+                {
+                    // If click on scrollbar position but not on slider, place slider directly on that position
+                    if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue);
+                    else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue);
+                }
+
+                state = STATE_PRESSED;
+            }
+
+            // Keyboard control on mouse hover scrollbar
+            /*
+            if (isVertical)
+            {
+                if (IsKeyDown(KEY_DOWN)) value += 5;
+                else if (IsKeyDown(KEY_UP)) value -= 5;
+            }
+            else
+            {
+                if (IsKeyDown(KEY_RIGHT)) value += 5;
+                else if (IsKeyDown(KEY_LEFT)) value -= 5;
+            }
+            */
+        }
+
+        // Normalize value
+        if (value > maxValue) value = maxValue;
+        if (value < minValue) value = minValue;
+    }
+    //--------------------------------------------------------------------
+
+    // Draw control
+    //--------------------------------------------------------------------
+    GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)));   // Draw the background
+
+    GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)));     // Draw the scrollbar active area background
+    GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3)));         // Draw the slider bar
+
+    // Draw arrows (using icon if available)
+    if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE))
+    {
+#if defined(RAYGUI_NO_ICONS)
+        GuiDrawText(isVertical? "^" : "<",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+        GuiDrawText(isVertical? "v" : ">",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))));
+#else
+        GuiDrawText(isVertical? "#121#" : "#118#",
+            RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL
+        GuiDrawText(isVertical? "#120#" : "#119#",
+            RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height },
+            TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)));   // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL
+#endif
+    }
+    //--------------------------------------------------------------------
+
+    return value;
+}
+
+// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f
+// WARNING: It multiplies current alpha by alpha scale factor
+static Color GuiFade(Color color, float alpha)
+{
+    if (alpha < 0.0f) alpha = 0.0f;
+    else if (alpha > 1.0f) alpha = 1.0f;
+
+    Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) };
+
+    return result;
+}
+
+#if defined(RAYGUI_STANDALONE)
+// Returns a Color struct from hexadecimal value
+static Color GetColor(int hexValue)
+{
+    Color color;
+
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
+
+    return color;
+}
+
+// Returns hexadecimal value for a Color
+static int ColorToInt(Color color)
+{
+    return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
+}
+
+// Check if point is inside rectangle
+static bool CheckCollisionPointRec(Vector2 point, Rectangle rec)
+{
+    bool collision = false;
+
+    if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) &&
+        (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true;
+
+    return collision;
+}
+
+// Formatting of text with variables to 'embed'
+static const char *TextFormat(const char *text, ...)
+{
+    #if !defined(RAYGUI_TEXTFORMAT_MAX_SIZE)
+        #define RAYGUI_TEXTFORMAT_MAX_SIZE   256
+    #endif
+
+    static char buffer[RAYGUI_TEXTFORMAT_MAX_SIZE];
+
+    va_list args;
+    va_start(args, text);
+    vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
+    va_end(args);
+
+    return buffer;
+}
+
+// Draw rectangle with vertical gradient fill color
+// NOTE: This function is only used by GuiColorPicker()
+static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
+{
+    Rectangle bounds = { (float)posX, (float)posY, (float)width, (float)height };
+    DrawRectangleGradientEx(bounds, color1, color2, color2, color1);
+}
+
+// Split string into multiple strings
+const char **TextSplit(const char *text, char delimiter, int *count)
+{
+    // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
+    // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
+    // all used memory is static... it has some limitations:
+    //      1. Maximum number of possible split strings is set by RAYGUI_TEXTSPLIT_MAX_ITEMS
+    //      2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE
+
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS)
+        #define RAYGUI_TEXTSPLIT_MAX_ITEMS          128
+    #endif
+    #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE)
+        #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE      1024
+    #endif
+
+    static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL };
+    static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 };
+    memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE);
+
+    result[0] = buffer;
+    int counter = 0;
+
+    if (text != NULL)
+    {
+        counter = 1;
+
+        // Count how many substrings we have on text and point to every one
+        for (int i = 0; i < RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE; i++)
+        {
+            buffer[i] = text[i];
+            if (buffer[i] == '\0') break;
+            else if (buffer[i] == delimiter)
+            {
+                buffer[i] = '\0';   // Set an end of string at this point
+                result[counter] = buffer + i + 1;
+                counter++;
+
+                if (counter == RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            }
+        }
+    }
+
+    *count = counter;
+    return result;
+}
+
+// Get integer value from text
+// NOTE: This function replaces atoi() [stdlib.h]
+static int TextToInteger(const char *text)
+{
+    int value = 0;
+    int sign = 1;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1;
+        text++;
+    }
+
+    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
+
+    return value*sign;
+}
+
+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+    float value = 0.0f;
+    float sign = 1.0f;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1.0f;
+        text++;
+    }
+
+    int i = 0;
+    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+    if (text[i++] != '.') value *= sign;
+    else
+    {
+        float divisor = 10.0f;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        {
+            value += ((float)(text[i] - '0'))/divisor;
+            divisor = divisor*10.0f;
+        }
+    }
+
+    return value;
+}
+
+// Encode codepoint into UTF-8 text (char array size returned as parameter)
+static const char *CodepointToUTF8(int codepoint, int *byteSize)
+{
+    static char utf8[6] = { 0 };
+    int size = 0;
+
+    if (codepoint <= 0x7f)
+    {
+        utf8[0] = (char)codepoint;
+        size = 1;
+    }
+    else if (codepoint <= 0x7ff)
+    {
+        utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
+        utf8[1] = (char)((codepoint & 0x3f) | 0x80);
+        size = 2;
+    }
+    else if (codepoint <= 0xffff)
+    {
+        utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
+        utf8[1] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[2] = (char)((codepoint & 0x3f) | 0x80);
+        size = 3;
+    }
+    else if (codepoint <= 0x10ffff)
+    {
+        utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
+        utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
+        utf8[2] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
+        utf8[3] = (char)((codepoint & 0x3f) | 0x80);
+        size = 4;
+    }
+
+    *byteSize = size;
+
+    return utf8;
+}
+
+// Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found
+// When a invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
+// Total number of bytes processed are returned as a parameter
+// NOTE: the standard says U+FFFD should be returned in case of errors
+// but that character is not supported by the default font in raylib
+static int GetCodepointNext(const char *text, int *codepointSize)
+{
+    const char *ptr = text;
+    int codepoint = 0x3f;       // Codepoint (defaults to '?')
+    *codepointSize = 1;
+
+    // Get current codepoint and bytes processed
+    if (0xf0 == (0xf8 & ptr[0]))
+    {
+        // 4 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
+        *codepointSize = 4;
+    }
+    else if (0xe0 == (0xf0 & ptr[0]))
+    {
+        // 3 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
+        *codepointSize = 3;
+    }
+    else if (0xc0 == (0xe0 & ptr[0]))
+    {
+        // 2 byte UTF-8 codepoint
+        if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
+        codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
+        *codepointSize = 2;
+    }
+    else if (0x00 == (0x80 & ptr[0]))
+    {
+        // 1 byte UTF-8 codepoint
+        codepoint = ptr[0];
+        *codepointSize = 1;
+    }
+
+    return codepoint;
+}
+#endif      // RAYGUI_STANDALONE
+
+#endif      // RAYGUI_IMPLEMENTATION
diff --git a/raylib/examples/shaders/rlights.h b/raylib/examples/shaders/rlights.h
--- a/raylib/examples/shaders/rlights.h
+++ b/raylib/examples/shaders/rlights.h
@@ -109,7 +109,7 @@
 static int lightsCount = 0;    // Current amount of created lights
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 // ...
 
diff --git a/raylib/examples/shaders/shaders_ascii_rendering.c b/raylib/examples/shaders/shaders_ascii_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_ascii_rendering.c
@@ -0,0 +1,121 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - ascii rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Maicon Santana (@maiconpintoabreu) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Maicon Santana (@maiconpintoabreu)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - ascii rendering");
+
+    // Texture to test static drawing
+    Texture2D fudesumi = LoadTexture("resources/fudesumi.png");
+    // Texture to test moving drawing
+    Texture2D raysan = LoadTexture("resources/raysan.png");
+
+    // Load shader to be used on postprocessing
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/ascii.fs", GLSL_VERSION));
+
+    // These locations are used to send data to the GPU
+    int resolutionLoc = GetShaderLocation(shader, "resolution");
+    int fontSizeLoc = GetShaderLocation(shader, "fontSize");
+
+    // Set the character size for the ASCII effect
+    // Fontsize should be 9 or more
+    float fontSize = 9.0f;
+
+    // Send the updated values to the shader
+    float resolution[2] = { (float)screenWidth, (float)screenHeight };
+    SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
+
+    Vector2 circlePos = (Vector2){40.0f, (float)screenHeight*0.5f};
+    float circleSpeed = 1.0f;
+
+    // RenderTexture to apply the postprocessing later
+    RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        circlePos.x += circleSpeed;
+        if ((circlePos.x > 200.0f) || (circlePos.x < 40.0f)) circleSpeed *= -1; // Revert speed
+
+        if (IsKeyPressed(KEY_LEFT) && (fontSize > 9.0)) fontSize -= 1;  // Reduce fontSize
+        if (IsKeyPressed(KEY_RIGHT) && (fontSize < 15.0)) fontSize += 1;  // Increase fontSize
+
+        // Set fontsize for the shader
+        SetShaderValue(shader, fontSizeLoc, &fontSize, SHADER_UNIFORM_FLOAT);
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginTextureMode(target);
+            ClearBackground(WHITE);
+
+            // Draw scene in our render texture
+            DrawTexture(fudesumi, 500, -30, WHITE);
+            DrawTextureV(raysan, circlePos, WHITE);
+        EndTextureMode();
+
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            BeginShaderMode(shader);
+                // Draw the scene texture (that we rendered earlier) to the screen
+                // The shader will process every pixel of this texture
+                DrawTextureRec(target.texture,
+                    (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height },
+                    (Vector2){ 0, 0 }, WHITE);
+            EndShaderMode();
+
+            DrawRectangle(0, 0, screenWidth, 40, BLACK);
+            DrawText(TextFormat("Ascii effect - FontSize:%2.0f - [Left] -1 [Right] +1 ", fontSize), 120, 10, 20, LIGHTGRAY);
+            DrawFPS(10, 10);
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTexture(target);    // Unload render texture
+
+    UnloadShader(shader);           // Unload shader
+    UnloadTexture(fudesumi);        // Unload texture
+    UnloadTexture(raysan);          // Unload texture
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_basic_lighting.c b/raylib/examples/shaders/shaders_basic_lighting.c
--- a/raylib/examples/shaders/shaders_basic_lighting.c
+++ b/raylib/examples/shaders/shaders_basic_lighting.c
@@ -2,19 +2,21 @@
 *
 *   raylib [shaders] example - basic lighting
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
-*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
 *
 *   Example originally created with raylib 3.0, last time updated with raylib 4.2
 *
-*   Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -57,10 +59,10 @@
                                TextFormat("resources/shaders/glsl%i/lighting.fs", GLSL_VERSION));
     // Get some required shader locations
     shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
-    // NOTE: "matModel" location name is automatically assigned on shader loading, 
+    // NOTE: "matModel" location name is automatically assigned on shader loading,
     // no need to get the location again if using that uniform name
     //shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
-    
+
     // Ambient light level (some basic lighting)
     int ambientLoc = GetShaderLocation(shader, "ambient");
     SetShaderValue(shader, ambientLoc, (float[4]){ 0.1f, 0.1f, 0.1f, 1.0f }, SHADER_UNIFORM_VEC4);
@@ -85,13 +87,13 @@
         // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
         float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
         SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
-        
+
         // Check key inputs to enable/disable lights
         if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
         if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; }
         if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; }
         if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
-        
+
         // Update light values (actually, only enable/disable them)
         for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(shader, lights[i]);
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/shaders/shaders_basic_pbr.c b/raylib/examples/shaders/shaders_basic_pbr.c
--- a/raylib/examples/shaders/shaders_basic_pbr.c
+++ b/raylib/examples/shaders/shaders_basic_pbr.c
@@ -1,18 +1,20 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Basic PBR
+*   raylib [shaders] example - basic pbr
 *
-*   Example originally created with raylib 5.0, last time updated with raylib 5.1-dev
+*   Example complexity rating: [★★★★] 4/4
 *
+*   Example originally created with raylib 5.0, last time updated with raylib 5.5
+*
 *   Example contributed by Afan OLOVCIC (@_DevDad) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad)
+*   Copyright (c) 2023-2025 Afan OLOVCIC (@_DevDad)
 *
-*   Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox, 
-*   licensed under Creative Commons Attribution-NonCommercial 
+*   Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox,
+*   licensed under Creative Commons Attribution-NonCommercial
 *   (http://creativecommons.org/licenses/by-nc/4.0/)
 *
 ********************************************************************************************/
@@ -22,7 +24,7 @@
 #if defined(PLATFORM_DESKTOP)
     #define GLSL_VERSION            330
 #else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            120
+    #define GLSL_VERSION            100
 #endif
 
 #include <stdlib.h>             // Required for: NULL
@@ -32,7 +34,6 @@
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
-
 // Light type
 typedef enum {
     LIGHT_DIRECTIONAL = 0,
@@ -64,7 +65,7 @@
 static int lightCount = 0;     // Current number of dynamic lights that have been created
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Functions Declaration
 //----------------------------------------------------------------------------------
 // Create a light and get shader locations
 static Light CreateLight(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader);
@@ -74,9 +75,9 @@
 static void UpdateLight(Shader shader, Light light);
 
 //----------------------------------------------------------------------------------
-// Main Entry Point
+// Program main entry point
 //----------------------------------------------------------------------------------
-int main()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
@@ -103,7 +104,7 @@
     // shader already takes care of it accordingly
     shader.locs[SHADER_LOC_MAP_METALNESS] = GetShaderLocation(shader, "mraMap");
     shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap");
-    // WARNING: Similar to the MRA map, the emissive map packs different information 
+    // WARNING: Similar to the MRA map, the emissive map packs different information
     // into a single texture: it stores height and emission data
     // It is binded to SHADER_LOC_MAP_EMISSION location an properly processed on shader
     shader.locs[SHADER_LOC_MAP_EMISSION] = GetShaderLocation(shader, "emissiveMap");
@@ -123,6 +124,8 @@
     SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &ambientIntensity, SHADER_UNIFORM_FLOAT);
 
     // Get location for shader parameters that can be modified in real time
+    int metallicValueLoc = GetShaderLocation(shader, "metallicValue");
+    int roughnessValueLoc = GetShaderLocation(shader, "roughnessValue");
     int emissiveIntensityLoc = GetShaderLocation(shader, "emissivePower");
     int emissiveColorLoc = GetShaderLocation(shader, "emissiveColor");
     int textureTilingLoc = GetShaderLocation(shader, "tiling");
@@ -139,7 +142,7 @@
 
     // Setup materials[0].maps default parameters
     car.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
-    car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
+    car.materials[0].maps[MATERIAL_MAP_METALNESS].value = 1.0f;
     car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
     car.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
     car.materials[0].maps[MATERIAL_MAP_EMISSION].color = (Color){ 255, 162, 0, 255 };
@@ -149,7 +152,7 @@
     car.materials[0].maps[MATERIAL_MAP_METALNESS].texture = LoadTexture("resources/old_car_mra.png");
     car.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/old_car_n.png");
     car.materials[0].maps[MATERIAL_MAP_EMISSION].texture = LoadTexture("resources/old_car_e.png");
-    
+
     // Load floor model mesh and assign material parameters
     // NOTE: A basic plane shape can be generated instead of being loaded from a model file
     Model floor = LoadModel("resources/models/plane.glb");
@@ -157,12 +160,12 @@
     //GenMeshTangents(&floorMesh);      // TODO: Review tangents generation
     //Model floor = LoadModelFromMesh(floorMesh);
 
-    // Assign material shader for our floor model, same PBR shader 
+    // Assign material shader for our floor model, same PBR shader
     floor.materials[0].shader = shader;
-    
+
     floor.materials[0].maps[MATERIAL_MAP_ALBEDO].color = WHITE;
-    floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.0f;
-    floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.0f;
+    floor.materials[0].maps[MATERIAL_MAP_METALNESS].value = 0.8f;
+    floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value = 0.1f;
     floor.materials[0].maps[MATERIAL_MAP_OCCLUSION].value = 1.0f;
     floor.materials[0].maps[MATERIAL_MAP_EMISSION].color = BLACK;
 
@@ -189,7 +192,7 @@
     SetShaderValue(shader, GetShaderLocation(shader, "useTexNormal"), &usage, SHADER_UNIFORM_INT);
     SetShaderValue(shader, GetShaderLocation(shader, "useTexMRA"), &usage, SHADER_UNIFORM_INT);
     SetShaderValue(shader, GetShaderLocation(shader, "useTexEmissive"), &usage, SHADER_UNIFORM_INT);
-    
+
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     //---------------------------------------------------------------------------------------
 
@@ -217,16 +220,20 @@
         // Draw
         //----------------------------------------------------------------------------------
         BeginDrawing();
-        
+
             ClearBackground(BLACK);
-            
+
             BeginMode3D(camera);
-                
+
                 // Set floor model texture tiling and emissive color parameters on shader
                 SetShaderValue(shader, textureTilingLoc, &floorTextureTiling, SHADER_UNIFORM_VEC2);
                 Vector4 floorEmissiveColor = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color);
                 SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, SHADER_UNIFORM_VEC4);
-                
+
+                // Set floor metallic and roughness values
+                SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT);
+                SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT);
+
                 DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE);   // Draw floor model
 
                 // Set old car model texture tiling, emissive color and emissive intensity parameters on shader
@@ -235,24 +242,32 @@
                 SetShaderValue(shader, emissiveColorLoc, &carEmissiveColor, SHADER_UNIFORM_VEC4);
                 float emissiveIntensity = 0.01f;
                 SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT);
-                
+
+                // Set old car metallic and roughness values
+                SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT);
+                SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT);
+
                 DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE);   // Draw car model
 
                 // Draw spheres to show the lights positions
                 for (int i = 0; i < MAX_LIGHTS; i++)
                 {
-                    Color lightColor = (Color){ lights[i].color[0]*255, lights[i].color[1]*255, lights[i].color[2]*255, lights[i].color[3]*255 };
-                    
+                    Color lightColor = (Color){
+                        (unsigned char)(lights[i].color[0]*255),
+                        (unsigned char)(lights[i].color[1]*255),
+                        (unsigned char)(lights[i].color[2]*255),
+                        (unsigned char)(lights[i].color[3]*255) };
+
                     if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lightColor);
                     else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lightColor, 0.3f));
                 }
-                
+
             EndMode3D();
-            
+
             DrawText("Toggle lights: [1][2][3][4]", 10, 40, 20, LIGHTGRAY);
 
             DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, LIGHTGRAY);
-            
+
             DrawFPS(10, 10);
 
         EndDrawing();
@@ -261,26 +276,29 @@
 
     // De-Initialization
     //--------------------------------------------------------------------------------------
-    // Unbind (disconnect) shader from car.material[0] 
+    // Unbind (disconnect) shader from car.material[0]
     // to avoid UnloadMaterial() trying to unload it automatically
     car.materials[0].shader = (Shader){ 0 };
     UnloadMaterial(car.materials[0]);
     car.materials[0].maps = NULL;
     UnloadModel(car);
-    
+
     floor.materials[0].shader = (Shader){ 0 };
     UnloadMaterial(floor.materials[0]);
     floor.materials[0].maps = NULL;
     UnloadModel(floor);
-    
+
     UnloadShader(shader);       // Unload Shader
-    
+
     CloseWindow();              // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
 
     return 0;
 }
 
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
 // Create light with provided data
 // NOTE: It updated the global lightCount and it's limited to MAX_LIGHTS
 static Light CreateLight(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader)
@@ -298,7 +316,7 @@
         light.color[2] = (float)color.b/255.0f;
         light.color[3] = (float)color.a/255.0f;
         light.intensity = intensity;
-        
+
         // NOTE: Shader parameters names for lights must match the requested ones
         light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightCount));
         light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightCount));
@@ -306,7 +324,7 @@
         light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightCount));
         light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightCount));
         light.intensityLoc = GetShaderLocation(shader, TextFormat("lights[%i].intensity", lightCount));
-        
+
         UpdateLight(shader, light);
 
         lightCount++;
@@ -321,7 +339,7 @@
 {
     SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
     SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
-    
+
     // Send to shader light position values
     float position[3] = { light.position.x, light.position.y, light.position.z };
     SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
diff --git a/raylib/examples/shaders/shaders_color_correction.c b/raylib/examples/shaders/shaders_color_correction.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_color_correction.c
@@ -0,0 +1,148 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - color correction
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jordi Santonja (@JordSant) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#define MAX_TEXTURES 4
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - color correction");
+
+    Texture2D texture[MAX_TEXTURES] = {
+        LoadTexture("resources/parrots.png"),
+        LoadTexture("resources/cat.png"),
+        LoadTexture("resources/mandrill.png"),
+        LoadTexture("resources/fudesumi.png")
+    };
+
+    Shader shdrColorCorrection = LoadShader(0, TextFormat("resources/shaders/glsl%i/color_correction.fs", GLSL_VERSION));
+
+    int imageIndex = 0;
+    int resetButtonClicked = 0;
+
+    float contrast = 0.0f;
+    float saturation = 0.0f;
+    float brightness = 0.0f;
+
+    // Get shader locations
+    int contrastLoc = GetShaderLocation(shdrColorCorrection, "contrast");
+    int saturationLoc = GetShaderLocation(shdrColorCorrection, "saturation");
+    int brightnessLoc = GetShaderLocation(shdrColorCorrection, "brightness");
+
+    // Set shader values (they can be changed later)
+    SetShaderValue(shdrColorCorrection, contrastLoc, &contrast, SHADER_UNIFORM_FLOAT);
+    SetShaderValue(shdrColorCorrection, saturationLoc, &saturation, SHADER_UNIFORM_FLOAT);
+    SetShaderValue(shdrColorCorrection, brightnessLoc, &brightness, SHADER_UNIFORM_FLOAT);
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Select texture to draw
+        if (IsKeyPressed(KEY_ONE)) imageIndex = 0;
+        else if (IsKeyPressed(KEY_TWO)) imageIndex = 1;
+        else if (IsKeyPressed(KEY_THREE)) imageIndex = 2;
+        else if (IsKeyPressed(KEY_FOUR)) imageIndex = 3;
+
+        // Reset values to 0
+        if (IsKeyPressed(KEY_R) || resetButtonClicked)
+        {
+            contrast = 0.0f;
+            saturation = 0.0f;
+            brightness = 0.0f;
+        }
+
+        // Send the values to the shader
+        SetShaderValue(shdrColorCorrection, contrastLoc, &contrast, SHADER_UNIFORM_FLOAT);
+        SetShaderValue(shdrColorCorrection, saturationLoc, &saturation, SHADER_UNIFORM_FLOAT);
+        SetShaderValue(shdrColorCorrection, brightnessLoc, &brightness, SHADER_UNIFORM_FLOAT);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginShaderMode(shdrColorCorrection);
+
+                DrawTexture(texture[imageIndex], 580/2 - texture[imageIndex].width/2, GetScreenHeight()/2 - texture[imageIndex].height/2, WHITE);
+
+            EndShaderMode();
+
+            DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
+            DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), (Color){ 232, 232, 232, 255 });
+
+            // Draw UI info text
+            DrawText("Color Correction", 585, 40, 20, GRAY);
+
+            DrawText("Picture", 602, 75, 10, GRAY);
+            DrawText("Press [1] - [4] to Change Picture", 600, 230, 8, GRAY);
+            DrawText("Press [R] to Reset Values", 600, 250, 8, GRAY);
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiToggleGroup((Rectangle){ 645, 70, 20, 20 }, "1;2;3;4", &imageIndex);
+
+            GuiSliderBar((Rectangle){ 645, 100, 120, 20 }, "Contrast", TextFormat("%.0f", contrast), &contrast, -100.0f, 100.0f);
+            GuiSliderBar((Rectangle){ 645, 130, 120, 20 }, "Saturation", TextFormat("%.0f", saturation), &saturation, -100.0f, 100.0f);
+            GuiSliderBar((Rectangle){ 645, 160, 120, 20 }, "Brightness", TextFormat("%.0f", brightness), &brightness, -100.0f, 100.0f);
+
+            resetButtonClicked = GuiButton((Rectangle){ 645, 190, 40, 20 }, "Reset");
+            //------------------------------------------------------------------------------
+
+            DrawFPS(710, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    for (int i = 0; i < MAX_TEXTURES; i++) UnloadTexture(texture[i]);
+    UnloadShader(shdrColorCorrection);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_custom_uniform.c b/raylib/examples/shaders/shaders_custom_uniform.c
--- a/raylib/examples/shaders/shaders_custom_uniform.c
+++ b/raylib/examples/shaders/shaders_custom_uniform.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Postprocessing with custom uniform variable
+*   raylib [shaders] example - custom uniform
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -38,7 +40,7 @@
 
     SetConfigFlags(FLAG_MSAA_4X_HINT);      // Enable Multi Sampling Anti Aliasing 4x (if available)
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform variable");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - custom uniform");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -76,7 +78,7 @@
         // Update
         //----------------------------------------------------------------------------------
         UpdateCamera(&camera, CAMERA_ORBITAL);
-        
+
         Vector2 mousePosition = GetMousePosition();
 
         swirlCenter[0] = mousePosition.x;
diff --git a/raylib/examples/shaders/shaders_deferred_render.c b/raylib/examples/shaders/shaders_deferred_render.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_deferred_render.c
+++ /dev/null
@@ -1,339 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - deferred rendering
-*
-*   NOTE: This example requires raylib OpenGL 3.3 or OpenGL ES 3.0
-*
-*   Example originally created with raylib 4.5, last time updated with raylib 4.5
-*
-*   Example contributed by Justin Andreas Lacoste (@27justin) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2023 Justin Andreas Lacoste (@27justin)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "rlgl.h"
-#include "raymath.h"
-
-#define RLIGHTS_IMPLEMENTATION
-#include "rlights.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-#include <stdlib.h>         // Required for: NULL
-
-#define MAX_CUBES   30
-
-// GBuffer data
-typedef struct GBuffer {
-    unsigned int framebuffer;
-
-    unsigned int positionTexture;
-    unsigned int normalTexture;
-    unsigned int albedoSpecTexture;
-    
-    unsigned int depthRenderbuffer;
-} GBuffer;
-
-// Deferred mode passes
-typedef enum {
-   DEFERRED_POSITION,
-   DEFERRED_NORMAL,
-   DEFERRED_ALBEDO,
-   DEFERRED_SHADING
-} DeferredMode;
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    // -------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - deferred render");
-
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 5.0f, 4.0f, 5.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 1.0f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 60.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    // Load plane model from a generated mesh
-    Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3));
-    Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f));
-
-    // Load geometry buffer (G-buffer) shader and deferred shader
-    Shader gbufferShader = LoadShader("resources/shaders/glsl330/gbuffer.vs",
-                               "resources/shaders/glsl330/gbuffer.fs");
-
-    Shader deferredShader = LoadShader("resources/shaders/glsl330/deferred_shading.vs",
-                               "resources/shaders/glsl330/deferred_shading.fs");
-    deferredShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(deferredShader, "viewPosition");
-
-    // Initialize the G-buffer
-    GBuffer gBuffer = { 0 };
-    gBuffer.framebuffer = rlLoadFramebuffer();
-
-    if (!gBuffer.framebuffer)
-    {
-        TraceLog(LOG_WARNING, "Failed to create framebuffer");
-        exit(1);
-    }
-    
-    rlEnableFramebuffer(gBuffer.framebuffer);
-
-    // Since we are storing position and normal data in these textures, 
-    // we need to use a floating point format.
-    gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1);
-
-    gBuffer.normalTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1);
-    // Albedo (diffuse color) and specular strength can be combined into one texture.
-    // The color in RGB, and the specular strength in the alpha channel.
-    gBuffer.albedoSpecTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
-
-    // Activate the draw buffers for our framebuffer
-    rlActiveDrawBuffers(3);
-
-    // Now we attach our textures to the framebuffer.
-    rlFramebufferAttach(gBuffer.framebuffer, gBuffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
-    rlFramebufferAttach(gBuffer.framebuffer, gBuffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0);
-    rlFramebufferAttach(gBuffer.framebuffer, gBuffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0);
-
-    // Finally we attach the depth buffer.
-    gBuffer.depthRenderbuffer = rlLoadTextureDepth(screenWidth, screenHeight, true);
-    rlFramebufferAttach(gBuffer.framebuffer, gBuffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
-
-    // Make sure our framebuffer is complete.
-    // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have
-    // to rlDisableFramebuffer() here.
-    if (!rlFramebufferComplete(gBuffer.framebuffer))
-    {
-        TraceLog(LOG_WARNING, "Framebuffer is not complete");
-        exit(1);
-    }
-
-    // Now we initialize the sampler2D uniform's in the deferred shader.
-    // We do this by setting the uniform's value to the color channel slot we earlier
-    // bound our textures to.
-    rlEnableShader(deferredShader.id);
-
-        rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gPosition"), 0);
-        rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gNormal"), 1);
-        rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), 2);
-
-    rlDisableShader();
-
-    // Assign out lighting shader to model
-    model.materials[0].shader = gbufferShader;
-    cube.materials[0].shader = gbufferShader;
-
-    // Create lights
-    //--------------------------------------------------------------------------------------
-    Light lights[MAX_LIGHTS] = { 0 };
-    lights[0] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, -2 }, Vector3Zero(), YELLOW, deferredShader);
-    lights[1] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, 2 }, Vector3Zero(), RED, deferredShader);
-    lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader);
-    lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader);
-
-    const float CUBE_SCALE = 0.25;
-    Vector3 cubePositions[MAX_CUBES] = { 0 };
-    float cubeRotations[MAX_CUBES] = { 0 };
-    
-    for (int i = 0; i < MAX_CUBES; i++)
-    {
-        cubePositions[i] = (Vector3){
-            .x = (float)(rand()%10) - 5,
-            .y = (float)(rand()%5),
-            .z = (float)(rand()%10) - 5,
-        };
-        
-        cubeRotations[i] = (float)(rand()%360);
-    }
-
-    DeferredMode mode = DEFERRED_SHADING;
-
-    rlEnableDepthTest();
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //---------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-
-        // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
-        float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
-        SetShaderValue(deferredShader, deferredShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
-        
-        // Check key inputs to enable/disable lights
-        if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
-        if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; }
-        if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; }
-        if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
-
-        // Check key inputs to switch between G-buffer textures
-        if (IsKeyPressed(KEY_ONE)) mode = DEFERRED_POSITION;
-        if (IsKeyPressed(KEY_TWO)) mode = DEFERRED_NORMAL;
-        if (IsKeyPressed(KEY_THREE)) mode = DEFERRED_ALBEDO;
-        if (IsKeyPressed(KEY_FOUR)) mode = DEFERRED_SHADING;
-
-        // Update light values (actually, only enable/disable them)
-        for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        // ---------------------------------------------------------------------------------
-        BeginDrawing();
-        
-            ClearBackground(RAYWHITE);
-        
-            // Draw to the geometry buffer by first activating it
-            rlEnableFramebuffer(gBuffer.framebuffer);
-            rlClearScreenBuffers();  // Clear color and depth buffer
-            
-            rlDisableColorBlend();
-            BeginMode3D(camera);
-                // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader`
-                // will not work, as they won't immediately load the shader program.
-                rlEnableShader(gbufferShader.id);
-                    // When drawing a model here, make sure that the material's shaders
-                    // are set to the gbuffer shader!
-                    DrawModel(model, Vector3Zero(), 1.0f, WHITE);
-                    DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE);
-
-                    for (int i = 0; i < MAX_CUBES; i++)
-                    {
-                        Vector3 position = cubePositions[i];
-                        DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE);
-                    }
-
-                rlDisableShader();
-            EndMode3D();
-            rlEnableColorBlend();
-
-            // Go back to the default framebuffer (0) and draw our deferred shading.
-            rlDisableFramebuffer();
-            rlClearScreenBuffers(); // Clear color & depth buffer
-
-            switch (mode)
-            {
-                case DEFERRED_SHADING:
-                {
-                    BeginMode3D(camera);
-                        rlDisableColorBlend();
-                        rlEnableShader(deferredShader.id);
-                            // Activate our g-buffer textures
-                            // These will now be bound to the sampler2D uniforms `gPosition`, `gNormal`,
-                            // and `gAlbedoSpec`
-                            rlActiveTextureSlot(0);
-                            rlEnableTexture(gBuffer.positionTexture);
-                            rlActiveTextureSlot(1);
-                            rlEnableTexture(gBuffer.normalTexture);
-                            rlActiveTextureSlot(2);
-                            rlEnableTexture(gBuffer.albedoSpecTexture);
-
-                            // Finally, we draw a fullscreen quad to our default framebuffer
-                            // This will now be shaded using our deferred shader
-                            rlLoadDrawQuad();
-                        rlDisableShader();
-                        rlEnableColorBlend();
-                    EndMode3D();
-
-                    // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer.
-                    rlBindFramebuffer(RL_READ_FRAMEBUFFER, gBuffer.framebuffer);
-                    rlBindFramebuffer(RL_DRAW_FRAMEBUFFER, 0);
-                    rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100);    // GL_DEPTH_BUFFER_BIT
-                    rlDisableFramebuffer();
-
-                    // Since our shader is now done and disabled, we can draw our lights in default
-                    // forward rendering
-                    BeginMode3D(camera);
-                        rlEnableShader(rlGetShaderIdDefault());
-                            for(int i = 0; i < MAX_LIGHTS; i++)
-                            {
-                                if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
-                                else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
-                            }
-                        rlDisableShader();
-                    EndMode3D();
-                    
-                    DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN);
-                } break;
-                case DEFERRED_POSITION:
-                {
-                    DrawTextureRec((Texture2D){
-                        .id = gBuffer.positionTexture,
-                        .width = screenWidth,
-                        .height = screenHeight,
-                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
-                    
-                    DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
-                } break;
-                case DEFERRED_NORMAL:
-                {
-                    DrawTextureRec((Texture2D){
-                        .id = gBuffer.normalTexture,
-                        .width = screenWidth,
-                        .height = screenHeight,
-                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
-                    
-                    DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
-                } break;
-                case DEFERRED_ALBEDO:
-                {
-                    DrawTextureRec((Texture2D){
-                        .id = gBuffer.albedoSpecTexture,
-                        .width = screenWidth,
-                        .height = screenHeight,
-                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
-                    
-                    DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
-                } break;
-                default: break;
-            }
-
-            DrawText("Toggle lights keys: [Y][R][G][B]", 10, 40, 20, DARKGRAY);
-            DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, DARKGRAY);
-
-            DrawFPS(10, 10);
-            
-        EndDrawing();
-        // -----------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadModel(model);     // Unload the models
-    UnloadModel(cube);
-
-    UnloadShader(deferredShader); // Unload shaders
-    UnloadShader(gbufferShader);
-
-    // Unload geometry buffer and all attached textures
-    rlUnloadFramebuffer(gBuffer.framebuffer);
-    rlUnloadTexture(gBuffer.positionTexture);
-    rlUnloadTexture(gBuffer.normalTexture);
-    rlUnloadTexture(gBuffer.albedoSpecTexture);
-    rlUnloadTexture(gBuffer.depthRenderbuffer);
-
-    CloseWindow();          // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_deferred_rendering.c b/raylib/examples/shaders/shaders_deferred_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_deferred_rendering.c
@@ -0,0 +1,343 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - deferred rendering
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or OpenGL ES 3.0
+*
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
+*   Example contributed by Justin Andreas Lacoste (@27justin) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2023-2025 Justin Andreas Lacoste (@27justin)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+#include "raymath.h"
+
+#define RLIGHTS_IMPLEMENTATION
+#include "rlights.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#include <stdlib.h>         // Required for: NULL
+
+#define MAX_CUBES   30
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// GBuffer data
+typedef struct GBuffer {
+    unsigned int framebufferId;
+
+    unsigned int positionTextureId;
+    unsigned int normalTextureId;
+    unsigned int albedoSpecTextureId;
+
+    unsigned int depthRenderbufferId;
+} GBuffer;
+
+// Deferred mode passes
+typedef enum {
+   DEFERRED_POSITION,
+   DEFERRED_NORMAL,
+   DEFERRED_ALBEDO,
+   DEFERRED_SHADING
+} DeferredMode;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    // -------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - deferred rendering");
+
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 5.0f, 4.0f, 5.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 1.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 60.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    // Load plane model from a generated mesh
+    Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3));
+    Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f));
+
+    // Load geometry buffer (G-buffer) shader and deferred shader
+    Shader gbufferShader = LoadShader(TextFormat("resources/shaders/glsl%i/gbuffer.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/gbuffer.fs", GLSL_VERSION));
+
+    Shader deferredShader = LoadShader(TextFormat("resources/shaders/glsl%i/deferred_shading.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/deferred_shading.fs", GLSL_VERSION));
+    deferredShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(deferredShader, "viewPosition");
+
+    // Initialize the G-buffer
+    GBuffer gBuffer = { 0 };
+    gBuffer.framebufferId = rlLoadFramebuffer();
+    if (gBuffer.framebufferId == 0) TraceLog(LOG_WARNING, "Failed to create framebufferId");
+
+    rlEnableFramebuffer(gBuffer.framebufferId);
+
+    // NOTE: Vertex positions are stored in a texture for simplicity. A better approach would use a depth texture
+    // (instead of a detph renderbuffer) to reconstruct world positions in the final render shader via clip-space position,
+    // depth, and the inverse view/projection matrices
+
+    // 16-bit precision ensures OpenGL ES 3 compatibility, though it may lack precision for real scenarios
+    // But as mentioned above, the positions could be reconstructed instead of stored. If not targeting OpenGL ES
+    // and you wish to maintain this approach, consider using `RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32`
+    gBuffer.positionTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1);
+
+    // Similarly, 16-bit precision is used for normals ensures OpenGL ES 3 compatibility
+    // This is generally sufficient, but a 16-bit fixed-point format offer a better uniform precision in all orientations
+    gBuffer.normalTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, 1);
+
+    // Albedo (diffuse color) and specular strength can be combined into one texture
+    // The color in RGB, and the specular strength in the alpha channel
+    gBuffer.albedoSpecTextureId = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
+
+    // Activate the draw buffers for our framebufferId
+    rlActiveDrawBuffers(3);
+
+    // Now we attach our textures to the framebufferId
+    rlFramebufferAttach(gBuffer.framebufferId, gBuffer.positionTextureId, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
+    rlFramebufferAttach(gBuffer.framebufferId, gBuffer.normalTextureId, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0);
+    rlFramebufferAttach(gBuffer.framebufferId, gBuffer.albedoSpecTextureId, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0);
+
+    // Finally we attach the depth buffer
+    gBuffer.depthRenderbufferId = rlLoadTextureDepth(screenWidth, screenHeight, true);
+    rlFramebufferAttach(gBuffer.framebufferId, gBuffer.depthRenderbufferId, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
+
+    // Make sure our framebufferId is complete
+    // NOTE: rlFramebufferComplete() automatically unbinds the framebufferId, so we don't have to rlDisableFramebuffer() here
+    if (!rlFramebufferComplete(gBuffer.framebufferId)) TraceLog(LOG_WARNING, "Framebuffer is not complete");
+
+    // Now we initialize the sampler2D uniform's in the deferred shader
+    // We do this by setting the uniform's values to the texture units that
+    // we later bind our g-buffer textures to
+    rlEnableShader(deferredShader.id);
+    int texUnitPosition = 0;
+    int texUnitNormal = 1;
+    int texUnitAlbedoSpec = 2;
+    SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gPosition"), &texUnitPosition, RL_SHADER_UNIFORM_SAMPLER2D);
+    SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gNormal"), &texUnitNormal, RL_SHADER_UNIFORM_SAMPLER2D);
+    SetShaderValue(deferredShader, rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), &texUnitAlbedoSpec, RL_SHADER_UNIFORM_SAMPLER2D);
+    rlDisableShader();
+
+    // Assign out lighting shader to model
+    model.materials[0].shader = gbufferShader;
+    cube.materials[0].shader = gbufferShader;
+
+    // Create lights
+    //--------------------------------------------------------------------------------------
+    Light lights[MAX_LIGHTS] = { 0 };
+    lights[0] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, -2 }, Vector3Zero(), YELLOW, deferredShader);
+    lights[1] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, 2 }, Vector3Zero(), RED, deferredShader);
+    lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader);
+    lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader);
+
+    const float CUBE_SCALE = 0.25;
+    Vector3 cubePositions[MAX_CUBES] = { 0 };
+    float cubeRotations[MAX_CUBES] = { 0 };
+
+    for (int i = 0; i < MAX_CUBES; i++)
+    {
+        cubePositions[i] = (Vector3){
+            .x = (float)(rand()%10) - 5,
+            .y = (float)(rand()%5),
+            .z = (float)(rand()%10) - 5,
+        };
+
+        cubeRotations[i] = (float)(rand()%360);
+    }
+
+    int mode = DEFERRED_SHADING;
+
+    rlEnableDepthTest();
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
+        float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+        SetShaderValue(deferredShader, deferredShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+
+        // Check key inputs to enable/disable lights
+        if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
+        if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; }
+        if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; }
+        if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
+
+        // Check key inputs to switch between G-buffer textures
+        if (IsKeyPressed(KEY_ONE)) mode = DEFERRED_POSITION;
+        if (IsKeyPressed(KEY_TWO)) mode = DEFERRED_NORMAL;
+        if (IsKeyPressed(KEY_THREE)) mode = DEFERRED_ALBEDO;
+        if (IsKeyPressed(KEY_FOUR)) mode = DEFERRED_SHADING;
+
+        // Update light values (actually, only enable/disable them)
+        for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        // ---------------------------------------------------------------------------------
+        BeginDrawing();
+
+            // Draw to the geometry buffer by first activating it
+            rlEnableFramebuffer(gBuffer.framebufferId);
+            rlClearColor(0, 0, 0, 0);
+            rlClearScreenBuffers();  // Clear color and depth buffer
+            rlDisableColorBlend();
+            
+            BeginMode3D(camera);
+                // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader`
+                // will not work, as they won't immediately load the shader program
+                rlEnableShader(gbufferShader.id);
+                    // When drawing a model here, make sure that the material's shaders are set to the gbuffer shader!
+                    DrawModel(model, Vector3Zero(), 1.0f, WHITE);
+                    DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE);
+
+                    for (int i = 0; i < MAX_CUBES; i++)
+                    {
+                        Vector3 position = cubePositions[i];
+                        DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE);
+                    }
+                rlDisableShader();
+            EndMode3D();
+            
+            rlEnableColorBlend();
+
+            // Go back to the default framebufferId (0) and draw our deferred shading
+            rlDisableFramebuffer();
+            rlClearScreenBuffers(); // Clear color & depth buffer
+
+            switch (mode)
+            {
+                case DEFERRED_SHADING:
+                {
+                    BeginMode3D(camera);
+                        rlDisableColorBlend();
+                        rlEnableShader(deferredShader.id);
+                            // Bind our g-buffer textures
+                            // We are binding them to locations that we earlier set in sampler2D uniforms `gPosition`, `gNormal`,
+                            // and `gAlbedoSpec`
+                            rlActiveTextureSlot(texUnitPosition);
+                            rlEnableTexture(gBuffer.positionTextureId);
+                            rlActiveTextureSlot(texUnitNormal);
+                            rlEnableTexture(gBuffer.normalTextureId);
+                            rlActiveTextureSlot(texUnitAlbedoSpec);
+                            rlEnableTexture(gBuffer.albedoSpecTextureId);
+
+                            // Finally, we draw a fullscreen quad to our default framebufferId
+                            // This will now be shaded using our deferred shader
+                            rlLoadDrawQuad();
+                        rlDisableShader();
+                        rlEnableColorBlend();
+                    EndMode3D();
+
+                    // As a last step, we now copy over the depth buffer from our g-buffer to the default framebufferId
+                    rlBindFramebuffer(RL_READ_FRAMEBUFFER, gBuffer.framebufferId);
+                    rlBindFramebuffer(RL_DRAW_FRAMEBUFFER, 0);
+                    rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT
+                    rlDisableFramebuffer();
+
+                    // Since our shader is now done and disabled, we can draw spheres
+                    // that represent light positions in default forward rendering
+                    BeginMode3D(camera);
+                        rlEnableShader(rlGetShaderIdDefault());
+                            for (int i = 0; i < MAX_LIGHTS; i++)
+                            {
+                                if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color);
+                                else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f));
+                            }
+                        rlDisableShader();
+                    EndMode3D();
+
+                    DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN);
+                } break;
+                case DEFERRED_POSITION:
+                {
+                    DrawTextureRec((Texture2D){
+                        .id = gBuffer.positionTextureId,
+                        .width = screenWidth,
+                        .height = screenHeight,
+                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
+
+                    DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
+                } break;
+                case DEFERRED_NORMAL:
+                {
+                    DrawTextureRec((Texture2D){
+                        .id = gBuffer.normalTextureId,
+                        .width = screenWidth,
+                        .height = screenHeight,
+                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
+
+                    DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
+                } break;
+                case DEFERRED_ALBEDO:
+                {
+                    DrawTextureRec((Texture2D){
+                        .id = gBuffer.albedoSpecTextureId,
+                        .width = screenWidth,
+                        .height = screenHeight,
+                    }, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, Vector2Zero(), RAYWHITE);
+
+                    DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN);
+                } break;
+                default: break;
+            }
+
+            DrawText("Toggle lights keys: [Y][R][G][B]", 10, 40, 20, DARKGRAY);
+            DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, DARKGRAY);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        // -----------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    // Unload the models
+    UnloadModel(model);
+    UnloadModel(cube);
+
+    // Unload shaders
+    UnloadShader(deferredShader);
+    UnloadShader(gbufferShader);
+
+    // Unload geometry buffer and all attached textures
+    rlUnloadFramebuffer(gBuffer.framebufferId);
+    rlUnloadTexture(gBuffer.positionTextureId);
+    rlUnloadTexture(gBuffer.normalTextureId);
+    rlUnloadTexture(gBuffer.albedoSpecTextureId);
+    rlUnloadTexture(gBuffer.depthRenderbufferId);
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_depth_rendering.c b/raylib/examples/shaders/shaders_depth_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_depth_rendering.c
@@ -0,0 +1,183 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - depth rendering
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Luís Almeida (@luis605) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Luís Almeida (@luis605)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+// Load custom render texture with depth texture attached
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
+
+// Unload render texture from GPU memory (VRAM)
+static void UnloadRenderTextureDepthTex(RenderTexture2D target);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - depth rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 4.0f, 1.0f, 5.0f };
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    camera.fovy = 45.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+
+    // Load render texture with a depth texture attached
+    RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
+
+    // Load depth shader and get depth texture shader location
+    Shader depthShader = LoadShader(0, TextFormat("resources/shaders/glsl%i/depth_render.fs", GLSL_VERSION));
+    int depthLoc = GetShaderLocation(depthShader, "depthTexture");
+    int flipTextureLoc = GetShaderLocation(depthShader, "flipY");
+    SetShaderValue(depthShader, flipTextureLoc, (int[]){ 1 }, SHADER_UNIFORM_INT); // Flip Y texture
+
+    // Load scene models
+    Model cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
+    Model floor = LoadModelFromMesh(GenMeshPlane(20.0f, 20.0f, 1, 1));
+
+    DisableCursor();  // Limit cursor to relative movement inside the window
+
+    SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FREE);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginTextureMode(target);
+            ClearBackground(WHITE);
+
+            BeginMode3D(camera);
+                DrawModel(cube, (Vector3){ 0.0f, 0.0f, 0.0f }, 3.0f, YELLOW);
+                DrawModel(floor, (Vector3){ 10.0f, 0.0f, 2.0f }, 2.0f, RED);
+            EndMode3D();
+        EndTextureMode();
+
+        // Draw into screen (main framebuffer)
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            BeginShaderMode(depthShader);
+                SetShaderValueTexture(depthShader, depthLoc, target.depth);
+                DrawTexture(target.depth, 0, 0, WHITE);
+            EndShaderMode();
+
+            DrawRectangle( 10, 10, 320, 93, Fade(SKYBLUE, 0.5f));
+            DrawRectangleLines( 10, 10, 320, 93, BLUE);
+
+            DrawText("Camera Controls:", 20, 20, 10, BLACK);
+            DrawText("- WASD to move", 40, 40, 10, DARKGRAY);
+            DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY);
+            DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, DARKGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModel(cube);              // Unload model
+    UnloadModel(floor);             // Unload model
+    UnloadRenderTextureDepthTex(target);
+    UnloadShader(depthShader);      // Unload shader
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+// Load custom render texture, create a writable depth texture buffer
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
+{
+    RenderTexture2D target = { 0 };
+
+    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
+
+    if (target.id > 0)
+    {
+        rlEnableFramebuffer(target.id);
+
+        // Create color texture (default to RGBA)
+        target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
+        target.texture.width = width;
+        target.texture.height = height;
+        target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+        target.texture.mipmaps = 1;
+
+        // Create depth texture buffer (instead of raylib default renderbuffer)
+        target.depth.id = rlLoadTextureDepth(width, height, false);
+        target.depth.width = width;
+        target.depth.height = height;
+        target.depth.format = 19;       // DEPTH_COMPONENT_24BIT: Not defined in raylib
+        target.depth.mipmaps = 1;
+
+        // Attach color texture and depth texture to FBO
+        rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
+        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
+
+        // Check if fbo is complete with attachments (valid)
+        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
+
+        rlDisableFramebuffer();
+    }
+    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
+
+    return target;
+}
+
+// Unload render texture from GPU memory (VRAM)
+void UnloadRenderTextureDepthTex(RenderTexture2D target)
+{
+    if (target.id > 0)
+    {
+        // Color texture attached to FBO is deleted
+        rlUnloadTexture(target.texture.id);
+        rlUnloadTexture(target.depth.id);
+
+        // NOTE: Depth texture is automatically
+        // queried and deleted before deleting framebuffer
+        rlUnloadFramebuffer(target.id);
+    }
+}
diff --git a/raylib/examples/shaders/shaders_depth_writing.c b/raylib/examples/shaders/shaders_depth_writing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_depth_writing.c
@@ -0,0 +1,169 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - depth writing
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 4.2, last time updated with raylib 4.2
+*
+*   Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+// Load custom render texture, create a writable depth texture buffer
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
+
+// Unload render texture from GPU memory (VRAM)
+static void UnloadRenderTextureDepthTex(RenderTexture2D target);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - depth writing");
+
+    // Define the camera to look into our 3d world
+    Camera camera = {
+        .position = (Vector3){ 2.0f, 2.0f, 3.0f },    // Camera position
+        .target = (Vector3){ 0.0f, 0.5f, 0.0f },      // Camera looking at point
+        .up = (Vector3){ 0.0f, 1.0f, 0.0f },          // Camera up vector (rotation towards target)
+        .fovy = 45.0f,                                // Camera field-of-view Y
+        .projection = CAMERA_PERSPECTIVE              // Camera projection type
+    };
+
+    // Load custom render texture with writable depth texture buffer
+    RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
+
+    // Load depth writing shader
+    // NOTE: The shader inverts the depth buffer by writing into it by `gl_FragDepth = 1 - gl_FragCoord.z;`
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/depth_write.fs", GLSL_VERSION));
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        // Draw into our custom render texture
+        BeginTextureMode(target);
+            ClearBackground(WHITE);
+
+            BeginMode3D(camera);
+                BeginShaderMode(shader);
+                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED);
+                    DrawCubeV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, PURPLE);
+                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, DARKGREEN);
+                    DrawCubeV((Vector3) { 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, YELLOW);
+                    DrawGrid(10, 1.0f);
+                EndShaderMode();
+            EndMode3D();
+        EndTextureMode();
+
+        // Draw into screen our custom render texture
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE);
+
+            DrawFPS(10, 10);
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTextureDepthTex(target);
+    UnloadShader(shader);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+// Load custom render texture, create a writable depth texture buffer
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
+{
+    RenderTexture2D target = { 0 };
+
+    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
+
+    if (target.id > 0)
+    {
+        rlEnableFramebuffer(target.id);
+
+        // Create color texture (default to RGBA)
+        target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
+        target.texture.width = width;
+        target.texture.height = height;
+        target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+        target.texture.mipmaps = 1;
+
+        // Create depth texture buffer (instead of raylib default renderbuffer)
+        target.depth.id = rlLoadTextureDepth(width, height, false);
+        target.depth.width = width;
+        target.depth.height = height;
+        target.depth.format = 19;       // DEPTH_COMPONENT_24BIT: Not defined in raylib
+        target.depth.mipmaps = 1;
+
+        // Attach color texture and depth texture to FBO
+        rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
+        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
+
+        // Check if fbo is complete with attachments (valid)
+        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
+
+        rlDisableFramebuffer();
+    }
+    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
+
+    return target;
+}
+
+// Unload render texture from GPU memory (VRAM)
+void UnloadRenderTextureDepthTex(RenderTexture2D target)
+{
+    if (target.id > 0)
+    {
+        // Color texture attached to FBO is deleted
+        rlUnloadTexture(target.texture.id);
+        rlUnloadTexture(target.depth.id);
+
+        // NOTE: Depth texture is automatically
+        // queried and deleted before deleting framebuffer
+        rlUnloadFramebuffer(target.id);
+    }
+}
diff --git a/raylib/examples/shaders/shaders_eratosthenes.c b/raylib/examples/shaders/shaders_eratosthenes.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_eratosthenes.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Sieve of Eratosthenes
-*
-*   NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve.
-*
-*       "Sift the twos and sift the threes,
-*        The Sieve of Eratosthenes.
-*        When the multiples sublime,
-*        the numbers that are left are prime."
-*
-*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
-*
-*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 4.0
-*
-*   Example contributed by ProfJski and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 ProfJski and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Sieve of Eratosthenes");
-
-    RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
-
-    // Load Eratosthenes shader
-    // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
-    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/eratosthenes.fs", GLSL_VERSION));
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // Nothing to do here, everything is happening in the shader
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginTextureMode(target);       // Enable drawing to texture
-            ClearBackground(BLACK);     // Clear the render texture
-
-            // Draw a rectangle in shader mode to be used as shader canvas
-            // NOTE: Rectangle uses font white character texture coordinates,
-            // so shader can not be applied here directly because input vertexTexCoord
-            // do not represent full screen coordinates (space where want to apply shader)
-            DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLACK);
-        EndTextureMode();               // End drawing to texture (now we have a blank texture available for the shader)
-
-        BeginDrawing();
-            ClearBackground(RAYWHITE);  // Clear screen background
-
-            BeginShaderMode(shader);
-                // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
-                DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2){ 0.0f, 0.0f }, WHITE);
-            EndShaderMode();
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadShader(shader);               // Unload shader
-    UnloadRenderTexture(target);        // Unload render texture
-
-    CloseWindow();                      // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_eratosthenes_sieve.c b/raylib/examples/shaders/shaders_eratosthenes_sieve.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_eratosthenes_sieve.c
@@ -0,0 +1,99 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - eratosthenes sieve
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: Sieve of Eratosthenes, the earliest known (ancient Greek) prime number sieve
+*
+*       "Sift the twos and sift the threes,
+*        The Sieve of Eratosthenes.
+*        When the multiples sublime,
+*        the numbers that are left are prime."
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 4.0
+*
+*   Example contributed by ProfJski (@ProfJski) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 ProfJski (@ProfJski) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - eratosthenes sieve");
+
+    RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
+
+    // Load Eratosthenes shader
+    // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/eratosthenes.fs", GLSL_VERSION));
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Nothing to do here, everything is happening in the shader
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginTextureMode(target);       // Enable drawing to texture
+            ClearBackground(BLACK);     // Clear the render texture
+
+            // Draw a rectangle in shader mode to be used as shader canvas
+            // NOTE: Rectangle uses font white character texture coordinates,
+            // so shader can not be applied here directly because input vertexTexCoord
+            // do not represent full screen coordinates (space where want to apply shader)
+            DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLACK);
+        EndTextureMode();               // End drawing to texture (now we have a blank texture available for the shader)
+
+        BeginDrawing();
+            ClearBackground(RAYWHITE);  // Clear screen background
+
+            BeginShaderMode(shader);
+                // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
+                DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2){ 0.0f, 0.0f }, WHITE);
+            EndShaderMode();
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);               // Unload shader
+    UnloadRenderTexture(target);        // Unload render texture
+
+    CloseWindow();                      // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_fog.c b/raylib/examples/shaders/shaders_fog.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_fog.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - fog
-*
-*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
-*
-*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 3.7
-*
-*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "raymath.h"
-
-#define RLIGHTS_IMPLEMENTATION
-#include "rlights.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT);  // Enable Multi Sampling Anti Aliasing 4x (if available)
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 2.0f, 2.0f, 6.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 0.5f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    // Load models and texture
-    Model modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32));
-    Model modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
-    Model modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32));
-    Texture texture = LoadTexture("resources/texel_checker.png");
-
-    // Assign texture to default model material
-    modelA.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
-    modelB.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
-    modelC.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
-
-    // Load shader and set up some uniforms
-    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lighting.vs", GLSL_VERSION),
-                               TextFormat("resources/shaders/glsl%i/fog.fs", GLSL_VERSION));
-    shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
-    shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
-
-    // Ambient light level
-    int ambientLoc = GetShaderLocation(shader, "ambient");
-    SetShaderValue(shader, ambientLoc, (float[4]){ 0.2f, 0.2f, 0.2f, 1.0f }, SHADER_UNIFORM_VEC4);
-
-    float fogDensity = 0.15f;
-    int fogDensityLoc = GetShaderLocation(shader, "fogDensity");
-    SetShaderValue(shader, fogDensityLoc, &fogDensity, SHADER_UNIFORM_FLOAT);
-
-    // NOTE: All models share the same shader
-    modelA.materials[0].shader = shader;
-    modelB.materials[0].shader = shader;
-    modelC.materials[0].shader = shader;
-
-    // Using just 1 point lights
-    CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader);
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-
-        if (IsKeyDown(KEY_UP))
-        {
-            fogDensity += 0.001f;
-            if (fogDensity > 1.0f) fogDensity = 1.0f;
-        }
-
-        if (IsKeyDown(KEY_DOWN))
-        {
-            fogDensity -= 0.001f;
-            if (fogDensity < 0.0f) fogDensity = 0.0f;
-        }
-
-        SetShaderValue(shader, fogDensityLoc, &fogDensity, SHADER_UNIFORM_FLOAT);
-
-        // Rotate the torus
-        modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateX(-0.025f));
-        modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateZ(0.012f));
-
-        // Update the light shader with the camera view position
-        SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], &camera.position.x, SHADER_UNIFORM_VEC3);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(GRAY);
-
-            BeginMode3D(camera);
-
-                // Draw the three models
-                DrawModel(modelA, Vector3Zero(), 1.0f, WHITE);
-                DrawModel(modelB, (Vector3){ -2.6f, 0, 0 }, 1.0f, WHITE);
-                DrawModel(modelC, (Vector3){ 2.6f, 0, 0 }, 1.0f, WHITE);
-
-                for (int i = -20; i < 20; i += 2) DrawModel(modelA,(Vector3){ (float)i, 0, 2 }, 1.0f, WHITE);
-
-            EndMode3D();
-
-            DrawText(TextFormat("Use KEY_UP/KEY_DOWN to change fog density [%.2f]", fogDensity), 10, 10, 20, RAYWHITE);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadModel(modelA);        // Unload the model A
-    UnloadModel(modelB);        // Unload the model B
-    UnloadModel(modelC);        // Unload the model C
-    UnloadTexture(texture);     // Unload the texture
-    UnloadShader(shader);       // Unload shader
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_fog_rendering.c b/raylib/examples/shaders/shaders_fog_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_fog_rendering.c
@@ -0,0 +1,157 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - fog rendering
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 3.7
+*
+*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#define RLIGHTS_IMPLEMENTATION
+#include "rlights.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);  // Enable Multi Sampling Anti Aliasing 4x (if available)
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - fog rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 2.0f, 2.0f, 6.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 0.5f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    // Load models and texture
+    Model modelA = LoadModelFromMesh(GenMeshTorus(0.4f, 1.0f, 16, 32));
+    Model modelB = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
+    Model modelC = LoadModelFromMesh(GenMeshSphere(0.5f, 32, 32));
+    Texture texture = LoadTexture("resources/texel_checker.png");
+
+    // Assign texture to default model material
+    modelA.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
+    modelB.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
+    modelC.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
+
+    // Load shader and set up some uniforms
+    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lighting.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/fog.fs", GLSL_VERSION));
+    shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
+    shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+
+    // Ambient light level
+    int ambientLoc = GetShaderLocation(shader, "ambient");
+    SetShaderValue(shader, ambientLoc, (float[4]){ 0.2f, 0.2f, 0.2f, 1.0f }, SHADER_UNIFORM_VEC4);
+
+    float fogDensity = 0.15f;
+    int fogDensityLoc = GetShaderLocation(shader, "fogDensity");
+    SetShaderValue(shader, fogDensityLoc, &fogDensity, SHADER_UNIFORM_FLOAT);
+
+    // NOTE: All models share the same shader
+    modelA.materials[0].shader = shader;
+    modelB.materials[0].shader = shader;
+    modelC.materials[0].shader = shader;
+
+    // Using just 1 point lights
+    CreateLight(LIGHT_POINT, (Vector3){ 0, 2, 6 }, Vector3Zero(), WHITE, shader);
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        if (IsKeyDown(KEY_UP))
+        {
+            fogDensity += 0.001f;
+            if (fogDensity > 1.0f) fogDensity = 1.0f;
+        }
+
+        if (IsKeyDown(KEY_DOWN))
+        {
+            fogDensity -= 0.001f;
+            if (fogDensity < 0.0f) fogDensity = 0.0f;
+        }
+
+        SetShaderValue(shader, fogDensityLoc, &fogDensity, SHADER_UNIFORM_FLOAT);
+
+        // Rotate the torus
+        modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateX(-0.025f));
+        modelA.transform = MatrixMultiply(modelA.transform, MatrixRotateZ(0.012f));
+
+        // Update the light shader with the camera view position
+        SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], &camera.position.x, SHADER_UNIFORM_VEC3);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(GRAY);
+
+            BeginMode3D(camera);
+
+                // Draw the three models
+                DrawModel(modelA, Vector3Zero(), 1.0f, WHITE);
+                DrawModel(modelB, (Vector3){ -2.6f, 0, 0 }, 1.0f, WHITE);
+                DrawModel(modelC, (Vector3){ 2.6f, 0, 0 }, 1.0f, WHITE);
+
+                for (int i = -20; i < 20; i += 2) DrawModel(modelA,(Vector3){ (float)i, 0, 2 }, 1.0f, WHITE);
+
+            EndMode3D();
+
+            DrawText(TextFormat("Use KEY_UP/KEY_DOWN to change fog density [%.2f]", fogDensity), 10, 10, 20, RAYWHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadModel(modelA);        // Unload the model A
+    UnloadModel(modelB);        // Unload the model B
+    UnloadModel(modelC);        // Unload the model C
+    UnloadTexture(texture);     // Unload the texture
+    UnloadShader(shader);       // Unload shader
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_game_of_life.c b/raylib/examples/shaders/shaders_game_of_life.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_game_of_life.c
@@ -0,0 +1,353 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - game of life
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jordi Santonja (@JordSant) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Interaction mode
+typedef enum {
+    MODE_RUN = 0,
+    MODE_PAUSE,
+    MODE_DRAW,
+} InteractionMode;
+
+// Struct to store example preset patterns
+typedef struct {
+    char *name;
+    Vector2 position;
+} PresetPattern;
+
+//----------------------------------------------------------------------------------
+// Functions declaration
+//----------------------------------------------------------------------------------
+void FreeImageToDraw(Image **imageToDraw);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+    
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - game of life");
+
+    const int menuWidth = 100;
+    const int windowWidth = screenWidth - menuWidth;
+    const int windowHeight = screenHeight;
+
+    const int worldWidth = 2048;
+    const int worldHeight = 2048;
+
+    const int randomTiles = 8;      // Random preset: divide the world to compute random points in each tile
+
+    const Rectangle worldRectSource = { 0, 0, (float)worldWidth, (float)-worldHeight };
+    const Rectangle worldRectDest = { 0, 0, (float)worldWidth, (float)worldHeight };
+    const Rectangle textureOnScreen = { 0, 0, (float)windowWidth, (float)windowHeight };
+
+    const PresetPattern presetPatterns[] = {
+        { "Glider", { 0.5f, 0.5f } }, { "R-pentomino", { 0.5f, 0.5f } }, { "Acorn", { 0.5f,0.5f } },
+        { "Spaceships", { 0.1f, 0.5f } }, { "Still lifes", { 0.5f, 0.5f } }, { "Oscillators", { 0.5f, 0.5f } },
+        { "Puffer train", { 0.1f, 0.5f } }, { "Glider Gun", { 0.2f, 0.2f } }, { "Breeder", { 0.1f, 0.5f } },
+        { "Random", { 0.5f, 0.5f } }
+    };
+    
+    const int numberOfPresets = sizeof(presetPatterns)/sizeof(presetPatterns[0]);
+
+    int zoom = 1;
+    float offsetX = (worldWidth - windowWidth)/2.0f;    // Centered on window
+    float offsetY = (worldHeight - windowHeight)/2.0f;  // Centered on window
+    int framesPerStep = 1;
+    int frame = 0;
+
+    int preset = -1;            // No button pressed for preset 
+    int mode = MODE_RUN;        // Starting mode: running
+    bool buttonZoomIn = false;  // Button states: false not pressed
+    bool buttonZomOut = false;
+    bool buttonFaster = false;
+    bool buttonSlower = false;
+
+    // Load shader
+    Shader shdrGameOfLife = LoadShader(0, TextFormat("resources/shaders/glsl%i/game_of_life.fs", GLSL_VERSION));
+
+    // Set shader uniform size of the world
+    int resolutionLoc = GetShaderLocation(shdrGameOfLife, "resolution");
+    const float resolution[2] = { (float)worldWidth, (float)worldHeight };
+    SetShaderValue(shdrGameOfLife, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
+
+    // Define two textures: the current world and the previous world
+    RenderTexture2D world1 = LoadRenderTexture(worldWidth, worldHeight);
+    RenderTexture2D world2 = LoadRenderTexture(worldWidth, worldHeight);
+    BeginTextureMode(world2);
+        ClearBackground(RAYWHITE);
+    EndTextureMode();
+
+    Image startPattern = LoadImage("resources/game_of_life/r_pentomino.png");
+    UpdateTextureRec(world2.texture, (Rectangle){ worldWidth/2.0f, worldHeight/2.0f, (float)(startPattern.width), (float)(startPattern.height) }, startPattern.data);
+    UnloadImage(startPattern);
+
+    // Pointers to the two textures, to be swapped
+    RenderTexture2D *currentWorld = &world2;
+    RenderTexture2D *previousWorld = &world1;
+
+    // Image to be used in DRAW mode, to be changed with mouse input
+    Image *imageToDraw = NULL;
+
+    SetTargetFPS(60);               // Set at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        frame++;
+
+        // Change zoom: both by buttons or by mouse wheel
+        float mouseWheelMove = GetMouseWheelMove();
+        if (buttonZoomIn || (buttonZomOut && (zoom > 1)) || (mouseWheelMove != 0.0f))
+        {
+            FreeImageToDraw(&imageToDraw);  // Zoom change: free the image to draw to be recreated again
+
+            const float centerX = offsetX + (windowWidth/2.0f)/zoom;
+            const float centerY = offsetY + (windowHeight/2.0f)/zoom;
+            if (buttonZoomIn || (mouseWheelMove > 0.0f)) zoom *= 2;
+            if ((buttonZomOut || (mouseWheelMove < 0.0f)) && (zoom > 1)) zoom /= 2;
+            offsetX = centerX - (windowWidth/2.0f)/zoom;
+            offsetY = centerY - (windowHeight/2.0f)/zoom;
+        }
+
+        // Change speed: number of frames per step
+        if (buttonFaster && framesPerStep > 1)    framesPerStep--;
+        if (buttonSlower)                         framesPerStep++;
+
+        // Mouse management
+        if ((mode == MODE_RUN) || (mode == MODE_PAUSE))
+        {
+            FreeImageToDraw(&imageToDraw);  // Free the image to draw: no longer needed in these modes
+
+            // Pan with mouse left button
+            static Vector2 previousMousePosition = { 0.0f, 0.0f };
+            const Vector2 mousePosition = GetMousePosition();
+            if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && (mousePosition.x < windowWidth))
+            {
+                offsetX -= (mousePosition.x - previousMousePosition.x)/zoom;
+                offsetY -= (mousePosition.y - previousMousePosition.y)/zoom;
+            }
+            previousMousePosition = mousePosition;
+        }
+        else // MODE_DRAW
+        {
+            const float offsetDecimalX = offsetX - floorf(offsetX);
+            const float offsetDecimalY = offsetY - floorf(offsetY);
+            int sizeInWorldX = (int)(ceilf((float)(windowWidth + offsetDecimalX*zoom)/zoom));
+            int sizeInWorldY = (int)(ceilf((float)(windowHeight + offsetDecimalY*zoom)/zoom));
+            if (offsetX + sizeInWorldX >= worldWidth) sizeInWorldX = worldWidth - (int)floorf(offsetX);
+            if (offsetY + sizeInWorldY >= worldHeight) sizeInWorldY = worldHeight - (int)floorf(offsetY);
+
+            // Create image to draw if not created yet
+            if (imageToDraw == NULL)
+            {
+                RenderTexture2D worldOnScreen = LoadRenderTexture(sizeInWorldX, sizeInWorldY);
+                BeginTextureMode(worldOnScreen);
+                    DrawTexturePro(currentWorld->texture, (Rectangle) { floorf(offsetX), floorf(offsetY), (float)(sizeInWorldX), -(float)(sizeInWorldY) },
+                            (Rectangle) { 0, 0, (float)(sizeInWorldX), (float)(sizeInWorldY) }, (Vector2) { 0, 0 }, 0.0f, WHITE);
+                EndTextureMode();
+                imageToDraw = (Image*)RL_MALLOC(sizeof(Image));
+                *imageToDraw = LoadImageFromTexture(worldOnScreen.texture);
+                
+                UnloadRenderTexture(worldOnScreen);
+            }
+
+            const Vector2 mousePosition = GetMousePosition();
+            static int firstColor = -1;
+            if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) && (mousePosition.x < windowWidth))
+            {
+                int mouseX = (int)(mousePosition.x + offsetDecimalX*zoom)/zoom;
+                int mouseY = (int)(mousePosition.y + offsetDecimalY*zoom)/zoom;
+                if (mouseX >= sizeInWorldX) mouseX = sizeInWorldX - 1;
+                if (mouseY >= sizeInWorldY) mouseY = sizeInWorldY - 1;
+                if (firstColor == -1) firstColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1;
+                const int prevColor = (GetImageColor(*imageToDraw, mouseX, mouseY).r < 5)? 0 : 1;
+                
+                ImageDrawPixel(imageToDraw, mouseX, mouseY, (firstColor) ? BLACK : RAYWHITE);
+                
+                if (prevColor != firstColor) UpdateTextureRec(currentWorld->texture, (Rectangle){ floorf(offsetX), floorf(offsetY), (float)(sizeInWorldX), (float)(sizeInWorldY) }, imageToDraw->data);
+            }
+            else firstColor = -1;
+        }
+
+        // Load selected preset
+        if (preset >= 0)
+        {
+            Image pattern;
+            if (preset < numberOfPresets - 1)   // Preset with pattern image lo load
+            {
+                switch (preset)
+                {
+                    case 0: pattern = LoadImage("resources/game_of_life/glider.png"); break;
+                    case 1: pattern = LoadImage("resources/game_of_life/r_pentomino.png"); break;
+                    case 2: pattern = LoadImage("resources/game_of_life/acorn.png"); break;
+                    case 3: pattern = LoadImage("resources/game_of_life/spaceships.png"); break;
+                    case 4: pattern = LoadImage("resources/game_of_life/still_lifes.png"); break;
+                    case 5: pattern = LoadImage("resources/game_of_life/oscillators.png"); break;
+                    case 6: pattern = LoadImage("resources/game_of_life/puffer_train.png"); break;
+                    case 7: pattern = LoadImage("resources/game_of_life/glider_gun.png"); break;
+                    case 8: pattern = LoadImage("resources/game_of_life/breeder.png"); break;
+                }
+                BeginTextureMode(*currentWorld);
+                    ClearBackground(RAYWHITE);
+                EndTextureMode();
+                
+                UpdateTextureRec(currentWorld->texture, (Rectangle){ worldWidth*presetPatterns[preset].position.x - pattern.width/2.0f,
+                                                                     worldHeight*presetPatterns[preset].position.y - pattern.height/2.0f,
+                                                                     (float)(pattern.width), (float)(pattern.height) }, pattern.data);
+            }
+            else    // Last preset: Random values
+            {
+                pattern = GenImageColor(worldWidth/randomTiles, worldHeight/randomTiles, RAYWHITE);
+                for (int i = 0; i < randomTiles; i++)
+                {
+                    for (int j = 0; j < randomTiles; j++)
+                    {
+                        ImageClearBackground(&pattern, RAYWHITE);
+                        for (int x = 0; x < pattern.width; x++)
+                        {
+                            for (int y = 0; y < pattern.height; y++)
+                            {
+                                if (GetRandomValue(0, 100) < 15) ImageDrawPixel(&pattern, x, y, BLACK);
+                            }
+                        }
+                        UpdateTextureRec(currentWorld->texture,
+                                         (Rectangle){ (float)(pattern.width*i), (float)(pattern.height*j),
+                                                      (float)(pattern.width), (float)(pattern.height) }, pattern.data);
+                    }
+                }
+            }
+
+            UnloadImage(pattern);
+            
+            mode = MODE_PAUSE;
+            offsetX = worldWidth*presetPatterns[preset].position.x - windowWidth/zoom/2.0f;
+            offsetY = worldHeight*presetPatterns[preset].position.y - windowHeight/zoom/2.0f;
+        }
+
+        // Check window draw inside world limits
+        if (offsetX < 0) offsetX = 0;
+        if (offsetY < 0) offsetY = 0;
+        if (offsetX > worldWidth - (float)(windowWidth)/zoom) offsetX = worldWidth - (float)(windowWidth)/zoom;
+        if (offsetY > worldHeight - (float)(windowHeight)/zoom) offsetY = worldHeight - (float)(windowHeight)/zoom;
+
+        // Rectangles for drawing texture portion to screen
+        const Rectangle textureSourceToScreen = { offsetX, offsetY, (float)windowWidth/zoom, (float)windowHeight/zoom };
+        //----------------------------------------------------------------------------------
+
+        // Draw to texture
+        //----------------------------------------------------------------------------------
+        if ((mode == MODE_RUN) && ((frame%framesPerStep) == 0))
+        {
+            // Swap worlds
+            RenderTexture2D *tempWorld = currentWorld;
+            currentWorld = previousWorld;
+            previousWorld = tempWorld;
+
+            // Draw to texture
+            BeginTextureMode(*currentWorld);
+                BeginShaderMode(shdrGameOfLife);
+                    DrawTexturePro(previousWorld->texture, worldRectSource, worldRectDest, (Vector2){ 0, 0 }, 0.0f, RAYWHITE);
+                EndShaderMode();
+            EndTextureMode();
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw to screen
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+        
+            DrawTexturePro(currentWorld->texture, textureSourceToScreen, textureOnScreen, (Vector2){ 0, 0 }, 0.0f, WHITE);
+
+            DrawLine(windowWidth, 0, windowWidth, screenHeight, (Color){ 218, 218, 218, 255 });
+            DrawRectangle(windowWidth, 0, screenWidth - windowWidth, screenHeight, (Color){ 232, 232, 232, 255 });
+
+            DrawText("Conway's", 704, 4, 20, DARKBLUE);
+            DrawText(" game of", 704, 19, 20, DARKBLUE);
+            DrawText("  life", 708, 34, 20, DARKBLUE);
+            DrawText("in raylib", 757, 42, 6, BLACK);
+
+            DrawText("Presets", 710, 58, 8, GRAY);
+            preset = -1;
+            for (int i = 0; i < numberOfPresets; i++)
+                if (GuiButton((Rectangle){ 710.0f, 70.0f + 18*i, 80.0f, 16.0f }, presetPatterns[i].name)) preset = i;
+
+            GuiToggleGroup((Rectangle){ 710, 258, 80, 16 }, "Run\nPause\nDraw", &mode);
+
+            DrawText(TextFormat("Zoom: %ix", zoom), 710, 316, 8, GRAY);
+            buttonZoomIn = GuiButton((Rectangle){ 710, 328, 80, 16 }, "Zoom in");
+            buttonZomOut = GuiButton((Rectangle){ 710, 346, 80, 16 }, "Zoom out");
+
+            DrawText(TextFormat("Speed: %i frame%s", framesPerStep, (framesPerStep > 1)? "s" : ""), 710, 370, 8, GRAY);
+            buttonFaster = GuiButton((Rectangle){ 710, 382, 80, 16 }, "Faster");
+            buttonSlower = GuiButton((Rectangle){ 710, 400, 80, 16 }, "Slower");
+
+            DrawFPS(712, 426);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shdrGameOfLife);
+    UnloadRenderTexture(world1);
+    UnloadRenderTexture(world2);
+
+    FreeImageToDraw(&imageToDraw);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Functions definition
+//----------------------------------------------------------------------------------
+void FreeImageToDraw(Image **imageToDraw)
+{
+    if (*imageToDraw != NULL)
+    {
+        UnloadImage(**imageToDraw);
+        RL_FREE(*imageToDraw);
+        *imageToDraw = NULL;
+    }
+}
diff --git a/raylib/examples/shaders/shaders_hot_reloading.c b/raylib/examples/shaders/shaders_hot_reloading.c
--- a/raylib/examples/shaders/shaders_hot_reloading.c
+++ b/raylib/examples/shaders/shaders_hot_reloading.c
@@ -1,16 +1,18 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Hot reloading
+*   raylib [shaders] example - hot reloading
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330
-*         is currently supported. OpenGL ES 2.0 platforms are not supported at the moment.
+*         is currently supported. OpenGL ES 2.0 platforms are not supported at the moment
 *
 *   Example originally created with raylib 3.0, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/shaders/shaders_hybrid_render.c b/raylib/examples/shaders/shaders_hybrid_render.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_hybrid_render.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Hybrid Rendering
-*
-*   Example originally created with raylib 4.2, last time updated with raylib 4.2
-*
-*   Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2022-2024 Buğra Alptekin Sarı (@BugraAlptekinSari)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-#include "rlgl.h"
-#include "math.h" // Used for tan()
-#include "raymath.h" // Used to calculate camera Direction
-
-#if defined(PLATFORM_DESKTOP)
-#define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-#define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Declare custom functions required for the example
-//------------------------------------------------------------------------------------
-// Load custom render texture, create a writable depth texture buffer
-static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
-// Unload render texture from GPU memory (VRAM)
-static void UnloadRenderTextureDepthTex(RenderTexture2D target);
-
-//------------------------------------------------------------------------------------
-// Declare custom Structs
-//------------------------------------------------------------------------------------
-
-typedef struct {
-    unsigned int camPos, camDir, screenCenter;
-}RayLocs ;
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - write depth buffer");
-
-    // This Shader calculates pixel depth and color using raymarch
-    Shader shdrRaymarch = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raymarch.fs", GLSL_VERSION));
-
-    // This Shader is a standard rasterization fragment shader with the addition of depth writing
-    // You are required to write depth for all shaders if one shader does it
-    Shader shdrRaster = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION));
-
-    // Declare Struct used to store camera locs.
-    RayLocs marchLocs = {0};
-
-    // Fill the struct with shader locs.
-    marchLocs.camPos = GetShaderLocation(shdrRaymarch, "camPos");
-    marchLocs.camDir = GetShaderLocation(shdrRaymarch, "camDir");
-    marchLocs.screenCenter = GetShaderLocation(shdrRaymarch, "screenCenter");
-
-    // Transfer screenCenter position to shader. Which is used to calculate ray direction. 
-    Vector2 screenCenter = {.x = screenWidth/2.0f, .y = screenHeight/2.0f};
-    SetShaderValue(shdrRaymarch, marchLocs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2);
-
-    // Use Customized function to create writable depth texture buffer
-    RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
-
-    // Define the camera to look into our 3d world
-    Camera camera = {
-        .position = (Vector3){ 0.5f, 1.0f, 1.5f },    // Camera position
-        .target = (Vector3){ 0.0f, 0.5f, 0.0f },      // Camera looking at point
-        .up = (Vector3){ 0.0f, 1.0f, 0.0f },          // Camera up vector (rotation towards target)
-        .fovy = 45.0f,                                // Camera field-of-view Y
-        .projection = CAMERA_PERSPECTIVE              // Camera projection type
-    };
-    
-    // Camera FOV is pre-calculated in the camera Distance.
-    float camDist = 1.0f/(tanf(camera.fovy*0.5f*DEG2RAD));
-    
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-
-        // Update Camera Postion in the ray march shader.
-        SetShaderValue(shdrRaymarch, marchLocs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3);
-        
-        // Update Camera Looking Vector. Vector length determines FOV.
-        Vector3 camDir = Vector3Scale( Vector3Normalize( Vector3Subtract(camera.target, camera.position)) , camDist);
-        SetShaderValue(shdrRaymarch, marchLocs.camDir, &(camDir), RL_SHADER_UNIFORM_VEC3);
-        //----------------------------------------------------------------------------------
-        
-        // Draw
-        //----------------------------------------------------------------------------------
-        // Draw into our custom render texture (framebuffer)
-        BeginTextureMode(target);
-            ClearBackground(WHITE);
-
-            // Raymarch Scene
-            rlEnableDepthTest(); //Manually enable Depth Test to handle multiple rendering methods.
-            BeginShaderMode(shdrRaymarch);
-                DrawRectangleRec((Rectangle){0,0, (float)screenWidth, (float)screenHeight},WHITE);
-            EndShaderMode();
-            
-            // Rasterize Scene
-            BeginMode3D(camera);
-                BeginShaderMode(shdrRaster);
-                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED);
-                    DrawCubeV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, PURPLE);
-                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, DARKGREEN);
-                    DrawCubeV((Vector3) { 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, YELLOW);
-                    DrawGrid(10, 1.0f);
-                EndShaderMode();
-            EndMode3D();
-        EndTextureMode();
-
-        // Draw into screen our custom render texture 
-        BeginDrawing();
-            ClearBackground(RAYWHITE);
-        
-            DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE);
-            DrawFPS(10, 10);
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadRenderTextureDepthTex(target);
-    UnloadShader(shdrRaymarch);
-    UnloadShader(shdrRaster);
-
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-//------------------------------------------------------------------------------------
-// Define custom functions required for the example
-//------------------------------------------------------------------------------------
-// Load custom render texture, create a writable depth texture buffer
-RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
-{
-    RenderTexture2D target = { 0 };
-
-    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
-
-    if (target.id > 0)
-    {
-        rlEnableFramebuffer(target.id);
-
-        // Create color texture (default to RGBA)
-        target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
-        target.texture.width = width;
-        target.texture.height = height;
-        target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
-        target.texture.mipmaps = 1;
-
-        // Create depth texture buffer (instead of raylib default renderbuffer)
-        target.depth.id = rlLoadTextureDepth(width, height, false);
-        target.depth.width = width;
-        target.depth.height = height;
-        target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
-        target.depth.mipmaps = 1;
-
-        // Attach color texture and depth texture to FBO
-        rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
-        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
-
-        // Check if fbo is complete with attachments (valid)
-        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
-
-        rlDisableFramebuffer();
-    }
-    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
-
-    return target;
-}
-
-// Unload render texture from GPU memory (VRAM)
-void UnloadRenderTextureDepthTex(RenderTexture2D target)
-{
-    if (target.id > 0)
-    {
-        // Color texture attached to FBO is deleted
-        rlUnloadTexture(target.texture.id);
-        rlUnloadTexture(target.depth.id);
-
-        // NOTE: Depth texture is automatically
-        // queried and deleted before deleting framebuffer
-        rlUnloadFramebuffer(target.id);
-    }
-}
diff --git a/raylib/examples/shaders/shaders_hybrid_rendering.c b/raylib/examples/shaders/shaders_hybrid_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_hybrid_rendering.c
@@ -0,0 +1,214 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - hybrid rendering
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 4.2, last time updated with raylib 4.2
+*
+*   Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2022-2025 Buğra Alptekin Sarı (@BugraAlptekinSari)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+#include "raymath.h"
+
+#include <math.h>   // Required for: tanf()
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+    unsigned int camPos;
+    unsigned int camDir;
+    unsigned int screenCenter;
+} RayLocs;
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+// Load custom render texture, create a writable depth texture buffer
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
+// Unload render texture from GPU memory (VRAM)
+static void UnloadRenderTextureDepthTex(RenderTexture2D target);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - hybrid rendering");
+
+    // This Shader calculates pixel depth and color using raymarch
+    Shader shdrRaymarch = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raymarch.fs", GLSL_VERSION));
+
+    // This Shader is a standard rasterization fragment shader with the addition of depth writing
+    // You are required to write depth for all shaders if one shader does it
+    Shader shdrRaster = LoadShader(0, TextFormat("resources/shaders/glsl%i/hybrid_raster.fs", GLSL_VERSION));
+
+    // Declare Struct used to store camera locs
+    RayLocs marchLocs = {0};
+
+    // Fill the struct with shader locs
+    marchLocs.camPos = GetShaderLocation(shdrRaymarch, "camPos");
+    marchLocs.camDir = GetShaderLocation(shdrRaymarch, "camDir");
+    marchLocs.screenCenter = GetShaderLocation(shdrRaymarch, "screenCenter");
+
+    // Transfer screenCenter position to shader. Which is used to calculate ray direction
+    Vector2 screenCenter = {.x = screenWidth/2.0f, .y = screenHeight/2.0f};
+    SetShaderValue(shdrRaymarch, marchLocs.screenCenter , &screenCenter , SHADER_UNIFORM_VEC2);
+
+    // Use Customized function to create writable depth texture buffer
+    RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
+
+    // Define the camera to look into our 3d world
+    Camera camera = {
+        .position = (Vector3){ 0.5f, 1.0f, 1.5f },    // Camera position
+        .target = (Vector3){ 0.0f, 0.5f, 0.0f },      // Camera looking at point
+        .up = (Vector3){ 0.0f, 1.0f, 0.0f },          // Camera up vector (rotation towards target)
+        .fovy = 45.0f,                                // Camera field-of-view Y
+        .projection = CAMERA_PERSPECTIVE              // Camera projection type
+    };
+
+    // Camera FOV is pre-calculated in the camera distance
+    float camDist = 1.0f/(tanf(camera.fovy*0.5f*DEG2RAD));
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        // Update Camera Postion in the ray march shader
+        SetShaderValue(shdrRaymarch, marchLocs.camPos, &(camera.position), RL_SHADER_UNIFORM_VEC3);
+
+        // Update Camera Looking Vector. Vector length determines FOV
+        Vector3 camDir = Vector3Scale( Vector3Normalize( Vector3Subtract(camera.target, camera.position)) , camDist);
+        SetShaderValue(shdrRaymarch, marchLocs.camDir, &(camDir), RL_SHADER_UNIFORM_VEC3);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        // Draw into our custom render texture (framebuffer)
+        BeginTextureMode(target);
+            ClearBackground(WHITE);
+
+            // Raymarch Scene
+            rlEnableDepthTest(); // Manually enable Depth Test to handle multiple rendering methods
+            BeginShaderMode(shdrRaymarch);
+                DrawRectangleRec((Rectangle){ 0,0, (float)screenWidth, (float)screenHeight },WHITE);
+            EndShaderMode();
+
+            // Rasterize Scene
+            BeginMode3D(camera);
+                BeginShaderMode(shdrRaster);
+                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED);
+                    DrawCubeV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, PURPLE);
+                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, DARKGREEN);
+                    DrawCubeV((Vector3) { 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, YELLOW);
+                    DrawGrid(10, 1.0f);
+                EndShaderMode();
+            EndMode3D();
+        EndTextureMode();
+
+        // Draw into screen our custom render texture
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE);
+
+            DrawFPS(10, 10);
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTextureDepthTex(target);
+    UnloadShader(shdrRaymarch);
+    UnloadShader(shdrRaster);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Load custom render texture, create a writable depth texture buffer
+static RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
+{
+    RenderTexture2D target = { 0 };
+
+    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
+
+    if (target.id > 0)
+    {
+        rlEnableFramebuffer(target.id);
+
+        // Create color texture (default to RGBA)
+        target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
+        target.texture.width = width;
+        target.texture.height = height;
+        target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+        target.texture.mipmaps = 1;
+
+        // Create depth texture buffer (instead of raylib default renderbuffer)
+        target.depth.id = rlLoadTextureDepth(width, height, false);
+        target.depth.width = width;
+        target.depth.height = height;
+        target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
+        target.depth.mipmaps = 1;
+
+        // Attach color texture and depth texture to FBO
+        rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
+        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
+
+        // Check if fbo is complete with attachments (valid)
+        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
+
+        rlDisableFramebuffer();
+    }
+    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
+
+    return target;
+}
+
+// Unload render texture from GPU memory (VRAM)
+static void UnloadRenderTextureDepthTex(RenderTexture2D target)
+{
+    if (target.id > 0)
+    {
+        // Color texture attached to FBO is deleted
+        rlUnloadTexture(target.texture.id);
+        rlUnloadTexture(target.depth.id);
+
+        // NOTE: Depth texture is automatically
+        // queried and deleted before deleting framebuffer
+        rlUnloadFramebuffer(target.id);
+    }
+}
diff --git a/raylib/examples/shaders/shaders_julia_set.c b/raylib/examples/shaders/shaders_julia_set.c
--- a/raylib/examples/shaders/shaders_julia_set.c
+++ b/raylib/examples/shaders/shaders_julia_set.c
@@ -1,11 +1,13 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Julia sets
+*   raylib [shaders] example - julia set
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
-*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
 *
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -51,7 +53,7 @@
 {
     // Initialization
     //--------------------------------------------------------------------------------------
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia set");
 
     // Load julia set shader
     // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
@@ -107,7 +109,7 @@
             SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2);
         }
 
-        // If "R" is pressed, reset zoom and offset.
+        // If "R" is pressed, reset zoom and offset
         if (IsKeyPressed(KEY_R))
         {
             zoom = startingZoom;
@@ -123,17 +125,16 @@
         if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++;
         else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--;
 
-        // If either left or right button is pressed, zoom in/out.
+        // If either left or right button is pressed, zoom in/out
         if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
         {
-            // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out.
+            // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out
             zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : 1.0f/zoomSpeed;
 
             const Vector2 mousePos = GetMousePosition();
             Vector2 offsetVelocity;
             // Find the velocity at which to change the camera. Take the distance of the mouse
-            // from the center of the screen as the direction, and adjust magnitude based on
-            // the current zoom.
+            // from the center of the screen as the direction, and adjust magnitude based on the current zoom
             offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom;
             offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom;
 
@@ -165,7 +166,7 @@
             // do not represent full screen coordinates (space where want to apply shader)
             DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLACK);
         EndTextureMode();
-            
+
         BeginDrawing();
             ClearBackground(BLACK);     // Clear screen background
 
diff --git a/raylib/examples/shaders/shaders_lightmap.c b/raylib/examples/shaders/shaders_lightmap.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_lightmap.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - lightmap
-*
-*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
-*
-*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3).
-*
-*   Example contributed by Jussi Viitala (@nullstare) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Jussi Viitala (@nullstare) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "raylib.h"
-#include "raymath.h"
-#include "rlgl.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-#define MAP_SIZE 10
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT);  // Enable Multi Sampling Anti Aliasing 4x (if available)
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - lightmap");
-
-    // Define the camera to look into our 3d world
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 4.0f, 6.0f, 8.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    Mesh mesh = GenMeshPlane((float)MAP_SIZE, (float)MAP_SIZE, 1, 1);
-
-    // GenMeshPlane doesn't generate texcoords2 so we will upload them separately
-    mesh.texcoords2 = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
-
-    // X                          // Y
-    mesh.texcoords2[0] = 0.0f;    mesh.texcoords2[1] = 0.0f;
-    mesh.texcoords2[2] = 1.0f;    mesh.texcoords2[3] = 0.0f;
-    mesh.texcoords2[4] = 0.0f;    mesh.texcoords2[5] = 1.0f;
-    mesh.texcoords2[6] = 1.0f;    mesh.texcoords2[7] = 1.0f;
-
-    // Load a new texcoords2 attributes buffer
-    mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false);
-    rlEnableVertexArray(mesh.vaoId);
-    
-    // Index 5 is for texcoords2
-    rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0);
-    rlEnableVertexAttribute(5);
-    rlDisableVertexArray();
-
-    // Load lightmap shader
-    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lightmap.vs", GLSL_VERSION),
-                               TextFormat("resources/shaders/glsl%i/lightmap.fs", GLSL_VERSION));
-
-    Texture texture = LoadTexture("resources/cubicmap_atlas.png");
-    Texture light = LoadTexture("resources/spark_flame.png");
-
-    GenTextureMipmaps(&texture);
-    SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR);
-
-    RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE);
-
-    SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR);
-
-    Material material = LoadMaterialDefault();
-    material.shader = shader;
-    material.maps[MATERIAL_MAP_ALBEDO].texture = texture;
-    material.maps[MATERIAL_MAP_METALNESS].texture = lightmap.texture;
-
-    // Drawing to lightmap
-    BeginTextureMode(lightmap);
-        ClearBackground(BLACK);
-
-        BeginBlendMode(BLEND_ADDITIVE);
-            DrawTexturePro(
-                light,
-                (Rectangle){ 0, 0, light.width, light.height },
-                (Rectangle){ 0, 0, 20, 20 },
-                (Vector2){ 10.0, 10.0 },
-                0.0,
-                RED
-            );
-            DrawTexturePro(
-                light,
-                (Rectangle){ 0, 0, light.width, light.height },
-                (Rectangle){ 8, 4, 20, 20 },
-                (Vector2){ 10.0, 10.0 },
-                0.0,
-                BLUE
-            );
-            DrawTexturePro(
-                light,
-                (Rectangle){ 0, 0, light.width, light.height },
-                (Rectangle){ 8, 8, 10, 10 },
-                (Vector2){ 5.0, 5.0 },
-                0.0,
-                GREEN
-            );
-        BeginBlendMode(BLEND_ALPHA);
-    EndTextureMode();
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-                DrawMesh(mesh, material, MatrixIdentity());
-            EndMode3D();
-
-            DrawFPS(10, 10);
-
-            DrawTexturePro(
-                lightmap.texture,
-                (Rectangle){ 0, 0, -MAP_SIZE, -MAP_SIZE },
-                (Rectangle){ GetRenderWidth() - MAP_SIZE*8 - 10, 10, MAP_SIZE*8, MAP_SIZE*8 },
-                (Vector2){ 0.0, 0.0 },
-                0.0,
-                WHITE);
-                
-            DrawText("lightmap", GetRenderWidth() - 66, 16 + MAP_SIZE*8, 10, GRAY);
-            DrawText("10x10 pixels", GetRenderWidth() - 76, 30 + MAP_SIZE*8, 10, GRAY);
-                
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadMesh(mesh);       // Unload the mesh
-    UnloadShader(shader);   // Unload shader
-    UnloadTexture(texture); // Unload texture
-    UnloadTexture(light);   // Unload texture
-
-    CloseWindow();          // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_lightmap_rendering.c b/raylib/examples/shaders/shaders_lightmap_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_lightmap_rendering.c
@@ -0,0 +1,176 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - lightmap rendering
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
+*
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
+*   Example contributed by Jussi Viitala (@nullstare) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Jussi Viitala (@nullstare) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "raylib.h"
+#include "raymath.h"
+#include "rlgl.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#define MAP_SIZE 16
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);  // Enable Multi Sampling Anti Aliasing 4x (if available)
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - lightmap rendering");
+
+    // Define the camera to look into our 3d world
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 4.0f, 6.0f, 8.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    Mesh mesh = GenMeshPlane((float)MAP_SIZE, (float)MAP_SIZE, 1, 1);
+
+    // GenMeshPlane doesn't generate texcoords2 so we will upload them separately
+    mesh.texcoords2 = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
+
+    // X                          // Y
+    mesh.texcoords2[0] = 0.0f;    mesh.texcoords2[1] = 0.0f;
+    mesh.texcoords2[2] = 1.0f;    mesh.texcoords2[3] = 0.0f;
+    mesh.texcoords2[4] = 0.0f;    mesh.texcoords2[5] = 1.0f;
+    mesh.texcoords2[6] = 1.0f;    mesh.texcoords2[7] = 1.0f;
+
+    // Load a new texcoords2 attributes buffer
+    mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false);
+    rlEnableVertexArray(mesh.vaoId);
+
+    // Index 5 is for texcoords2
+    rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0);
+    rlEnableVertexAttribute(5);
+    rlDisableVertexArray();
+
+    // Load lightmap shader
+    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lightmap.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/lightmap.fs", GLSL_VERSION));
+
+    Texture texture = LoadTexture("resources/cubicmap_atlas.png");
+    Texture light = LoadTexture("resources/spark_flame.png");
+
+    GenTextureMipmaps(&texture);
+    SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR);
+
+    RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE);
+
+    Material material = LoadMaterialDefault();
+    material.shader = shader;
+    material.maps[MATERIAL_MAP_ALBEDO].texture = texture;
+    material.maps[MATERIAL_MAP_METALNESS].texture = lightmap.texture;
+
+    // Drawing to lightmap
+    BeginTextureMode(lightmap);
+        ClearBackground(BLACK);
+
+        BeginBlendMode(BLEND_ADDITIVE);
+            DrawTexturePro(
+                light,
+                (Rectangle){ 0, 0, (float)light.width, (float)light.height },
+                (Rectangle){ 0, 0, 2.0f*MAP_SIZE, 2.0f*MAP_SIZE },
+                (Vector2){ (float)MAP_SIZE, (float)MAP_SIZE },
+                0.0,
+                RED
+            );
+            DrawTexturePro(
+                light,
+                (Rectangle){ 0, 0, (float)light.width, (float)light.height },
+                (Rectangle){ (float)MAP_SIZE*0.8f, (float)MAP_SIZE/2.0f, 2.0f*MAP_SIZE, 2.0f*MAP_SIZE },
+                (Vector2){ (float)MAP_SIZE, (float)MAP_SIZE },
+                0.0,
+                BLUE
+            );
+            DrawTexturePro(
+                light,
+                (Rectangle){ 0, 0, (float)light.width, (float)light.height },
+                (Rectangle){ (float)MAP_SIZE*0.8f, (float)MAP_SIZE*0.8f, (float)MAP_SIZE, (float)MAP_SIZE },
+                (Vector2){ (float)MAP_SIZE/2.0f, (float)MAP_SIZE/2.0f },
+                0.0,
+                GREEN
+            );
+        BeginBlendMode(BLEND_ALPHA);
+    EndTextureMode();
+
+    // NOTE: To enable trilinear filtering we need mipmaps available for texture
+    GenTextureMipmaps(&lightmap.texture);
+    SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR);
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                DrawMesh(mesh, material, MatrixIdentity());
+            EndMode3D();
+
+            DrawTexturePro(lightmap.texture, (Rectangle){ 0, 0, -MAP_SIZE, -MAP_SIZE },
+                (Rectangle){ (float)GetRenderWidth() - MAP_SIZE*8 - 10, 10, (float)MAP_SIZE*8, (float)MAP_SIZE*8 },
+                (Vector2){ 0.0, 0.0 }, 0.0, WHITE);
+
+            DrawText(TextFormat("LIGHTMAP: %ix%i pixels", MAP_SIZE, MAP_SIZE), GetRenderWidth() - 130, 20 + MAP_SIZE*8, 10, GREEN);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadMesh(mesh);       // Unload the mesh
+    UnloadShader(shader);   // Unload shader
+    UnloadTexture(texture); // Unload texture
+    UnloadTexture(light);   // Unload texture
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_mandelbrot_set.c b/raylib/examples/shaders/shaders_mandelbrot_set.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_mandelbrot_set.c
@@ -0,0 +1,229 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - mandelbrot set
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3)
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jordi Santonja (@JordSant)
+*   Based on previous work by Josh Colclough (@joshcol9232)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+// A few good interesting places
+const float pointsOfInterest[6][3] =
+{
+    { -1.76826775f, -0.00422996283f, 28435.9238f },
+    { 0.322004497f, -0.0357099883f, 56499.7266f },
+    { -0.748880744f, -0.0562955774f, 9237.59082f },
+    { -1.78385007f, -0.0156200649f, 14599.5283f },
+    { -0.0985441282f, -0.924688697f, 26259.8535f },
+    { 0.317785531f, -0.0322612226f, 29297.9258f },
+};
+
+const int screenWidth = 800;
+const int screenHeight = 450;
+const float zoomSpeed = 1.01f;
+const float offsetSpeedMul = 2.0f;
+
+const float startingZoom = 0.6f;
+const float startingOffset[2] = { -0.5f, 0.0f };
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - mandelbrot set");
+
+    // Load mandelbrot set shader
+    // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/mandelbrot_set.fs", GLSL_VERSION));
+
+    // Create a RenderTexture2D to be used for render to texture
+    RenderTexture2D target = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
+
+    // Offset and zoom to draw the mandelbrot set at. (centered on screen and default size)
+    float offset[2] = { startingOffset[0], startingOffset[1] };
+    float zoom = startingZoom;
+    // Depending on the zoom the mximum number of iterations must be adapted to get more detail as we zzoom in
+    // The solution is not perfect, so a control has been added to increase/decrease the number of iterations with UP/DOWN keys
+#if defined(PLATFORM_DESKTOP)
+    int maxIterations = 333;
+    float maxIterationsMultiplier = 166.5f;
+#else
+    int maxIterations = 43;
+    float maxIterationsMultiplier = 22.0f;
+#endif
+
+    // Get variable (uniform) locations on the shader to connect with the program
+    // NOTE: If uniform variable could not be found in the shader, function returns -1
+    int zoomLoc = GetShaderLocation(shader, "zoom");
+    int offsetLoc = GetShaderLocation(shader, "offset");
+    int maxIterationsLoc = GetShaderLocation(shader, "maxIterations");
+
+    // Upload the shader uniform values!
+    SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT);
+    SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2);
+    SetShaderValue(shader, maxIterationsLoc, &maxIterations, SHADER_UNIFORM_INT);
+
+    bool showControls = true;           // Show controls
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        bool updateShader = false;
+
+        // Press [1 - 6] to reset c to a point of interest
+        if (IsKeyPressed(KEY_ONE) ||
+            IsKeyPressed(KEY_TWO) ||
+            IsKeyPressed(KEY_THREE) ||
+            IsKeyPressed(KEY_FOUR) ||
+            IsKeyPressed(KEY_FIVE) ||
+            IsKeyPressed(KEY_SIX))
+        {
+            int interestIndex = 0;
+            if (IsKeyPressed(KEY_ONE)) interestIndex = 0;
+            else if (IsKeyPressed(KEY_TWO)) interestIndex = 1;
+            else if (IsKeyPressed(KEY_THREE)) interestIndex = 2;
+            else if (IsKeyPressed(KEY_FOUR)) interestIndex = 3;
+            else if (IsKeyPressed(KEY_FIVE)) interestIndex = 4;
+            else if (IsKeyPressed(KEY_SIX)) interestIndex = 5;
+
+            offset[0] = pointsOfInterest[interestIndex][0];
+            offset[1] = pointsOfInterest[interestIndex][1];
+            zoom = pointsOfInterest[interestIndex][2];
+            updateShader = true;
+        }
+
+        // If "R" is pressed, reset zoom and offset
+        if (IsKeyPressed(KEY_R))
+        {
+            offset[0] = startingOffset[0];
+            offset[1] = startingOffset[1];
+            zoom = startingZoom;
+            updateShader = true;
+        }
+
+        if (IsKeyPressed(KEY_F1)) showControls = !showControls;  // Toggle whether or not to show controls
+
+        // Change number of max iterations with UP and DOWN keys
+        // WARNING: Increasing the number of max iterations greatly impacts performance
+        if (IsKeyPressed(KEY_UP))
+        {
+            maxIterationsMultiplier *= 1.4f;
+            updateShader = true;
+        }
+        else if (IsKeyPressed(KEY_DOWN))
+        {
+            maxIterationsMultiplier /= 1.4f;
+            updateShader = true;
+        }
+
+        // If either left or right button is pressed, zoom in/out
+        if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT))
+        {
+            // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out
+            zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : (1.0f/zoomSpeed);
+
+            const Vector2 mousePos = GetMousePosition();
+            Vector2 offsetVelocity;
+            // Find the velocity at which to change the camera. Take the distance of the mouse
+            // From the center of the screen as the direction, and adjust magnitude based on the current zoom
+            offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom;
+            offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom;
+
+            // Apply move velocity to camera
+            offset[0] += GetFrameTime()*offsetVelocity.x;
+            offset[1] += GetFrameTime()*offsetVelocity.y;
+
+            updateShader = true;
+        }
+
+        // In case a parameter has been changed, update the shader values
+        if (updateShader)
+        {
+            // As we zoom in, increase the number of max iterations to get more detail
+            // Aproximate formula, but it works-ish
+            maxIterations = (int)(sqrtf(2.0f*sqrtf(fabsf(1.0f - sqrtf(37.5f*zoom))))*maxIterationsMultiplier);
+
+            // Update the shader uniform values!
+            SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT);
+            SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2);
+            SetShaderValue(shader, maxIterationsLoc, &maxIterations, SHADER_UNIFORM_INT);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        // Using a render texture to draw Mandelbrot set
+        BeginTextureMode(target);       // Enable drawing to texture
+            ClearBackground(BLACK);     // Clear the render texture
+
+            // Draw a rectangle in shader mode to be used as shader canvas
+            // NOTE: Rectangle uses font white character texture coordinates,
+            // So shader can not be applied here directly because input vertexTexCoord
+            // Do not represent full screen coordinates (space where want to apply shader)
+            DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLACK);
+        EndTextureMode();
+
+        BeginDrawing();
+            ClearBackground(BLACK);     // Clear screen background
+
+            // Draw the saved texture and rendered mandelbrot set with shader
+            // NOTE: We do not invert texture on Y, already considered inside shader
+            BeginShaderMode(shader);
+                // WARNING: If FLAG_WINDOW_HIGHDPI is enabled, HighDPI monitor scaling should be considered
+                // When rendering the RenderTexture2D to fit in the HighDPI scaled Window
+                DrawTextureEx(target.texture, (Vector2){ 0.0f, 0.0f }, 0.0f, 1.0f, WHITE);
+            EndShaderMode();
+
+            if (showControls)
+            {
+                DrawText("Press Mouse buttons right/left to zoom in/out and move", 10, 15, 10, RAYWHITE);
+                DrawText("Press F1 to toggle these controls", 10, 30, 10, RAYWHITE);
+                DrawText("Press [1 - 6] to change point of interest", 10, 45, 10, RAYWHITE);
+                DrawText("Press UP | DOWN to change number of iterations", 10, 60, 10, RAYWHITE);
+                DrawText("Press R to recenter the camera", 10, 75, 10, RAYWHITE);
+            }
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);               // Unload shader
+    UnloadRenderTexture(target);        // Unload render texture
+
+    CloseWindow();                      // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_mesh_instancing.c b/raylib/examples/shaders/shaders_mesh_instancing.c
--- a/raylib/examples/shaders/shaders_mesh_instancing.c
+++ b/raylib/examples/shaders/shaders_mesh_instancing.c
@@ -1,19 +1,20 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Mesh instancing
+*   raylib [shaders] example - mesh instancing
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 3.7, last time updated with raylib 4.2
 *
-*   Example contributed by @seanpringle and reviewed by Max (@moliad) and Ramon Santamaria (@raysan5)
+*   Example contributed by seanpringle (@seanpringle) and reviewed by Max (@moliad) and Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 @seanpringle, Max (@moliad) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 seanpringle (@seanpringle), Max (@moliad) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
-
 #include "raylib.h"
 #include "raymath.h"
 
@@ -61,9 +62,9 @@
     {
         Matrix translation = MatrixTranslate((float)GetRandomValue(-50, 50), (float)GetRandomValue(-50, 50), (float)GetRandomValue(-50, 50));
         Vector3 axis = Vector3Normalize((Vector3){ (float)GetRandomValue(0, 360), (float)GetRandomValue(0, 360), (float)GetRandomValue(0, 360) });
-        float angle = (float)GetRandomValue(0, 10)*DEG2RAD;
+        float angle = (float)GetRandomValue(0, 180)*DEG2RAD;
         Matrix rotation = MatrixRotate(axis, angle);
-        
+
         transforms[i] = MatrixMultiply(rotation, translation);
     }
 
@@ -73,7 +74,6 @@
     // Get shader locations
     shader.locs[SHADER_LOC_MATRIX_MVP] = GetShaderLocation(shader, "mvp");
     shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
-    shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocationAttrib(shader, "instanceTransform");
 
     // Set shader value: ambient light level
     int ambientLoc = GetShaderLocation(shader, "ambient");
diff --git a/raylib/examples/shaders/shaders_model_shader.c b/raylib/examples/shaders/shaders_model_shader.c
--- a/raylib/examples/shaders/shaders_model_shader.c
+++ b/raylib/examples/shaders/shaders_model_shader.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Model shader
+*   raylib [shaders] example - model shader
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -55,7 +57,7 @@
     // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
     Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/grayscale.fs", GLSL_VERSION));
 
-    model.materials[0].shader = shader;                     // Set shader effect to 3d model
+    model.materials[0].shader = shader;                 // Set shader effect to 3d model
     model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Bind texture to model
 
     Vector3 position = { 0.0f, 0.0f, 0.0f };    // Set model position
@@ -69,7 +71,7 @@
     {
         // Update
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+        UpdateCamera(&camera, CAMERA_FREE);
         //----------------------------------------------------------------------------------
 
         // Draw
diff --git a/raylib/examples/shaders/shaders_multi_sample2d.c b/raylib/examples/shaders/shaders_multi_sample2d.c
--- a/raylib/examples/shaders/shaders_multi_sample2d.c
+++ b/raylib/examples/shaders/shaders_multi_sample2d.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Multiple sample2D with default batch system
+*   raylib [shaders] example - multi sample2d
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2020-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -36,7 +38,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib - multiple sample2D");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - multi sample2d");
 
     Image imRed = GenImageColor(800, 450, (Color){ 255, 0, 0, 255 });
     Texture texRed = LoadTextureFromImage(imRed);
@@ -80,16 +82,18 @@
 
             BeginShaderMode(shader);
 
-                // WARNING: Additional samplers are enabled for all draw calls in the batch,
-                // EndShaderMode() forces batch drawing and consequently resets active textures
-                // to let other sampler2D to be activated on consequent drawings (if required)
+                // WARNING: Additional textures (sampler2D) are enabled for ALL draw calls in the batch,
+                // but EndShaderMode() forces batch drawing and resets active textures, this way
+                // other textures (sampler2D) can be activated on consequent drawings (if required)
+                // The downside of this approach is that SetShaderValue() must be called inside the loop,
+                // to be set again after every EndShaderMode() reset
                 SetShaderValueTexture(shader, texBlueLoc, texBlue);
 
-                // We are drawing texRed using default sampler2D texture0 but
-                // an additional texture units is enabled for texBlue (sampler2D texture1)
+                // We are drawing texRed using default [sampler2D texture0] but
+                // an additional texture units is enabled for texBlue [sampler2D texture1]
                 DrawTexture(texRed, 0, 0, WHITE);
 
-            EndShaderMode();
+            EndShaderMode(); // Texture sampler2D is reseted, needs to be set again for next frame
 
             DrawText("Use KEY_LEFT/KEY_RIGHT to move texture mixing in shader!", 80, GetScreenHeight() - 40, 20, RAYWHITE);
 
diff --git a/raylib/examples/shaders/shaders_normalmap_rendering.c b/raylib/examples/shaders/shaders_normalmap_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_normalmap_rendering.c
@@ -0,0 +1,173 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - normalmap rendering
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
+*        OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jeremy Montgomery (@Sir_Irk) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jeremy Montgomery (@Sir_Irk) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - normalmap rendering");
+
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 0.0f, 2.0f, -4.0f };
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    camera.fovy = 45.0f;
+    camera.projection = CAMERA_PERSPECTIVE;
+
+    // Load basic normal map lighting shader
+    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/normalmap.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/normalmap.fs", GLSL_VERSION));
+
+    // Get some required shader locations
+    shader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(shader, "normalMap");
+    shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+
+    // NOTE: "matModel" location name is automatically assigned on shader loading,
+    // no need to get the location again if using that uniform name
+    // shader.locs[SHADER_LOC_MATRIX_MODEL] = GetShaderLocation(shader, "matModel");
+
+    // This example uses just 1 point light
+    Vector3 lightPosition = { 0.0f, 1.0f, 0.0f };
+    int lightPosLoc = GetShaderLocation(shader, "lightPos");
+
+    // Load a plane model that has proper normals and tangents
+    Model plane = LoadModel("resources/models/plane.glb");
+
+    // Set the plane model's shader and texture maps
+    plane.materials[0].shader = shader;
+    plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture("resources/tiles_diffuse.png");
+    plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture("resources/tiles_normal.png");
+
+    // Generate Mipmaps and use TRILINEAR filtering to help with texture aliasing
+    GenTextureMipmaps(&plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture);
+    GenTextureMipmaps(&plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture);
+
+    SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture, TEXTURE_FILTER_TRILINEAR);
+    SetTextureFilter(plane.materials[0].maps[MATERIAL_MAP_NORMAL].texture, TEXTURE_FILTER_TRILINEAR);
+
+    // Specular exponent AKA shininess of the material
+    float specularExponent = 8.0f;
+    int specularExponentLoc = GetShaderLocation(shader, "specularExponent");
+
+    // Allow toggling the normal map on and off for comparison purposes
+    int useNormalMap = 1;
+    int useNormalMapLoc = GetShaderLocation(shader, "useNormalMap");
+
+    SetTargetFPS(60); // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose()) // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Move the light around on the X and Z axis using WASD keys
+        Vector3 direction = { 0 };
+        if (IsKeyDown(KEY_W)) direction = Vector3Add(direction, (Vector3){ 0.0f, 0.0f, 1.0f });
+        if (IsKeyDown(KEY_S)) direction = Vector3Add(direction, (Vector3){ 0.0f, 0.0f, -1.0f });
+        if (IsKeyDown(KEY_D)) direction = Vector3Add(direction, (Vector3){ -1.0f, 0.0f, 0.0f });
+        if (IsKeyDown(KEY_A)) direction = Vector3Add(direction, (Vector3){ 1.0f, 0.0f, 0.0f });
+
+        direction = Vector3Normalize(direction);
+        lightPosition = Vector3Add(lightPosition, Vector3Scale(direction, GetFrameTime()*3.0f));
+
+        // Increase/Decrease the specular exponent(shininess)
+        if (IsKeyDown(KEY_UP)) specularExponent = Clamp(specularExponent + 40.0f*GetFrameTime(), 2.0f, 128.0f);
+        if (IsKeyDown(KEY_DOWN)) specularExponent = Clamp(specularExponent - 40.0f*GetFrameTime(), 2.0f, 128.0f);
+
+        // Toggle normal map on and off
+        if (IsKeyPressed(KEY_N)) useNormalMap = !useNormalMap;
+
+        // Spin plane model at a constant rate
+        plane.transform = MatrixRotateY((float)GetTime()*0.5f);
+
+        // Update shader values
+        float lightPos[3] = {lightPosition.x, lightPosition.y, lightPosition.z};
+        SetShaderValue(shader, lightPosLoc, lightPos, SHADER_UNIFORM_VEC3);
+
+        float camPos[3] = {camera.position.x, camera.position.y, camera.position.z};
+        SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], camPos, SHADER_UNIFORM_VEC3);
+
+        SetShaderValue(shader, specularExponentLoc, &specularExponent, SHADER_UNIFORM_FLOAT);
+
+        SetShaderValue(shader, useNormalMapLoc, &useNormalMap, SHADER_UNIFORM_INT);
+        //--------------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+
+                BeginShaderMode(shader);
+
+                    DrawModel(plane, Vector3Zero(), 2.0f, WHITE);
+
+                EndShaderMode();
+
+                // Draw sphere to show light position
+                DrawSphereWires(lightPosition, 0.2f, 8, 8, ORANGE);
+
+            EndMode3D();
+
+            Color textColor = (useNormalMap) ? DARKGREEN : RED;
+            const char *toggleStr = (useNormalMap) ? "On" : "Off";
+            DrawText(TextFormat("Use key [N] to toggle normal map: %s", toggleStr), 10, 10, 10, textColor);
+
+            int yOffset = 24;
+            DrawText("Use keys [W][A][S][D] to move the light", 10, 10 + yOffset*1, 10, BLACK);
+            DrawText("Use keys [Up][Down] to change specular exponent", 10, 10 + yOffset*2, 10, BLACK);
+            DrawText(TextFormat("Specular Exponent: %.2f", specularExponent), 10, 10 + yOffset*3, 10, BLUE);
+
+            DrawFPS(screenWidth - 90, 10);
+
+        EndDrawing();
+        //--------------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);
+    UnloadModel(plane);
+
+    CloseWindow(); // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_palette_switch.c b/raylib/examples/shaders/shaders_palette_switch.c
--- a/raylib/examples/shaders/shaders_palette_switch.c
+++ b/raylib/examples/shaders/shaders_palette_switch.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Color palette switch
+*   raylib [shaders] example - palette switch
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -16,7 +18,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Marco Lizza (@MarcoLizza) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Marco Lizza (@MarcoLizza) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -32,6 +34,9 @@
 #define COLORS_PER_PALETTE      8
 #define VALUES_PER_COLOR        3
 
+//------------------------------------------------------------------------------------
+// Global Variables Definition
+//------------------------------------------------------------------------------------
 static const int palettes[MAX_PALETTES][COLORS_PER_PALETTE*VALUES_PER_COLOR] = {
     {   // 3-BIT RGB
         0, 0, 0,
@@ -81,7 +86,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - color palette switch");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - palette switch");
 
     // Load shader to be used on some parts drawing
     // NOTE 1: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version
diff --git a/raylib/examples/shaders/shaders_postprocessing.c b/raylib/examples/shaders/shaders_postprocessing.c
--- a/raylib/examples/shaders/shaders_postprocessing.c
+++ b/raylib/examples/shaders/shaders_postprocessing.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Apply a postprocessing shader to a scene
+*   raylib [shaders] example - postprocessing
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -44,6 +46,9 @@
     //FX_FXAA
 } PostproShader;
 
+//------------------------------------------------------------------------------------
+// Global Variables Definition
+//------------------------------------------------------------------------------------
 static const char *postproShaderText[] = {
     "GRAYSCALE",
     "POSTERIZATION",
@@ -72,7 +77,7 @@
 
     SetConfigFlags(FLAG_MSAA_4X_HINT);      // Enable Multi Sampling Anti Aliasing 4x (if available)
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing shader");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - postprocessing");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -139,7 +144,7 @@
                 DrawGrid(10, 1.0f);     // Draw a grid
             EndMode3D();                // End 3d mode drawing, returns to orthographic 2d mode
         EndTextureMode();               // End drawing to texture (now we have a texture available for next passes)
-        
+
         BeginDrawing();
             ClearBackground(RAYWHITE);  // Clear screen background
 
diff --git a/raylib/examples/shaders/shaders_raymarching.c b/raylib/examples/shaders/shaders_raymarching.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_raymarching.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Raymarching shapes generation
-*
-*   NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330
-*         is currently supported. OpenGL ES 2.0 platforms are not supported at the moment.
-*
-*   Example originally created with raylib 2.0, last time updated with raylib 4.2
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment
-    #define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching shapes");
-
-    Camera camera = { 0 };
-    camera.position = (Vector3){ 2.5f, 2.5f, 3.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.7f };      // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
-    camera.fovy = 65.0f;                                // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
-
-    // Load raymarching shader
-    // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
-    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/raymarching.fs", GLSL_VERSION));
-
-    // Get shader locations for required uniforms
-    int viewEyeLoc = GetShaderLocation(shader, "viewEye");
-    int viewCenterLoc = GetShaderLocation(shader, "viewCenter");
-    int runTimeLoc = GetShaderLocation(shader, "runTime");
-    int resolutionLoc = GetShaderLocation(shader, "resolution");
-
-    float resolution[2] = { (float)screenWidth, (float)screenHeight };
-    SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
-
-    float runTime = 0.0f;
-
-    DisableCursor();                    // Limit cursor to relative movement inside the window
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
-
-        float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
-        float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z };
-
-        float deltaTime = GetFrameTime();
-        runTime += deltaTime;
-
-        // Set shader required uniform values
-        SetShaderValue(shader, viewEyeLoc, cameraPos, SHADER_UNIFORM_VEC3);
-        SetShaderValue(shader, viewCenterLoc, cameraTarget, SHADER_UNIFORM_VEC3);
-        SetShaderValue(shader, runTimeLoc, &runTime, SHADER_UNIFORM_FLOAT);
-
-        // Check if screen is resized
-        if (IsWindowResized())
-        {
-            resolution[0] = (float)GetScreenWidth();
-            resolution[1] = (float)GetScreenHeight();
-            SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            // We only draw a white full-screen rectangle,
-            // frame is generated in shader using raymarching
-            BeginShaderMode(shader);
-                DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), WHITE);
-            EndShaderMode();
-
-            DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", GetScreenWidth() - 280, GetScreenHeight() - 20, 10, BLACK);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadShader(shader);           // Unload shader
-
-    CloseWindow();                  // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_raymarching_rendering.c b/raylib/examples/shaders/shaders_raymarching_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_raymarching_rendering.c
@@ -0,0 +1,119 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - raymarching rendering
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   NOTE: This example requires raylib OpenGL 3.3 for shaders support and only #version 330
+*         is currently supported. OpenGL ES 2.0 platforms are not supported at the moment
+*
+*   Example originally created with raylib 2.0, last time updated with raylib 4.2
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2018-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE);
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - raymarching rendering");
+
+    Camera camera = { 0 };
+    camera.position = (Vector3){ 2.5f, 2.5f, 3.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.7f };      // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
+    camera.fovy = 65.0f;                                // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;             // Camera projection type
+
+    // Load raymarching shader
+    // NOTE: Defining 0 (NULL) for vertex shader forces usage of internal default vertex shader
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/raymarching.fs", GLSL_VERSION));
+
+    // Get shader locations for required uniforms
+    int viewEyeLoc = GetShaderLocation(shader, "viewEye");
+    int viewCenterLoc = GetShaderLocation(shader, "viewCenter");
+    int runTimeLoc = GetShaderLocation(shader, "runTime");
+    int resolutionLoc = GetShaderLocation(shader, "resolution");
+
+    float resolution[2] = { (float)screenWidth, (float)screenHeight };
+    SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
+
+    float runTime = 0.0f;
+
+    DisableCursor();                    // Limit cursor to relative movement inside the window
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, CAMERA_FIRST_PERSON);
+
+        float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z };
+        float cameraTarget[3] = { camera.target.x, camera.target.y, camera.target.z };
+
+        float deltaTime = GetFrameTime();
+        runTime += deltaTime;
+
+        // Set shader required uniform values
+        SetShaderValue(shader, viewEyeLoc, cameraPos, SHADER_UNIFORM_VEC3);
+        SetShaderValue(shader, viewCenterLoc, cameraTarget, SHADER_UNIFORM_VEC3);
+        SetShaderValue(shader, runTimeLoc, &runTime, SHADER_UNIFORM_FLOAT);
+
+        // Check if screen is resized
+        if (IsWindowResized())
+        {
+            resolution[0] = (float)GetScreenWidth();
+            resolution[1] = (float)GetScreenHeight();
+            SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // We only draw a white full-screen rectangle,
+            // frame is generated in shader using raymarching
+            BeginShaderMode(shader);
+                DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), WHITE);
+            EndShaderMode();
+
+            DrawText("(c) Raymarching shader by Iñigo Quilez. MIT License.", GetScreenWidth() - 280, GetScreenHeight() - 20, 10, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);           // Unload shader
+
+    CloseWindow();                  // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_rounded_rectangle.c b/raylib/examples/shaders/shaders_rounded_rectangle.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_rounded_rectangle.c
@@ -0,0 +1,230 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - rounded rectangle
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Anstro Pleuton (@anstropleuton) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Anstro Pleuton (@anstropleuton)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Rounded rectangle data
+typedef struct {
+    Vector4 cornerRadius; // Individual corner radius (top-left, top-right, bottom-left, bottom-right)
+
+    // Shadow variables
+    float shadowRadius;
+    Vector2 shadowOffset;
+    float shadowScale;
+
+    // Border variables
+    float borderThickness; // Inner-border thickness
+
+    // Shader locations
+    int rectangleLoc;
+    int radiusLoc;
+    int colorLoc;
+    int shadowRadiusLoc;
+    int shadowOffsetLoc;
+    int shadowScaleLoc;
+    int shadowColorLoc;
+    int borderThicknessLoc;
+    int borderColorLoc;
+} RoundedRectangle;
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+// Create a rounded rectangle and set uniform locations
+static RoundedRectangle CreateRoundedRectangle(Vector4 cornerRadius, float shadowRadius, Vector2 shadowOffset, float shadowScale, float borderThickness, Shader shader);
+
+// Update rounded rectangle uniforms
+static void UpdateRoundedRectangle(RoundedRectangle rec, Shader shader);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - rounded rectangle");
+
+    // Load the shader
+    Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/base.vs", GLSL_VERSION),
+                               TextFormat("resources/shaders/glsl%i/rounded_rectangle.fs", GLSL_VERSION));
+
+    // Create a rounded rectangle
+    RoundedRectangle roundedRectangle = CreateRoundedRectangle(
+        (Vector4){ 5.0f, 10.0f, 15.0f, 20.0f },     // Corner radius
+        20.0f,                                      // Shadow radius
+        (Vector2){ 0.0f, -5.0f },                   // Shadow offset
+        0.95f,                                      // Shadow scale
+        5.0f,                                       // Border thickness
+        shader                                      // Shader
+    );
+
+    // Update shader uniforms
+    UpdateRoundedRectangle(roundedRectangle, shader);
+
+    const Color rectangleColor = BLUE;
+    const Color shadowColor = DARKBLUE;
+    const Color borderColor = SKYBLUE;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw rectangle box with rounded corners using shader
+            Rectangle rec = { 50, 70, 110, 60 };
+            DrawRectangleLines((int)rec.x - 20, (int)rec.y - 20, (int)rec.width + 40, (int)rec.height + 40, DARKGRAY);
+            DrawText("Rounded rectangle", (int)rec.x - 20, (int)rec.y - 35, 10, DARKGRAY);
+
+            // Flip Y axis to match shader coordinate system
+            rec.y = screenHeight - rec.y - rec.height;
+            SetShaderValue(shader, roundedRectangle.rectangleLoc, (float[]){ rec.x, rec.y, rec.width, rec.height }, SHADER_UNIFORM_VEC4);
+
+            // Only rectangle color
+            SetShaderValue(shader, roundedRectangle.colorLoc, (float[]) { rectangleColor.r/255.0f, rectangleColor.g/255.0f, rectangleColor.b/255.0f, rectangleColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.shadowColorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.borderColorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+
+            BeginShaderMode(shader);
+                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
+            EndShaderMode();
+
+            // Draw rectangle shadow using shader
+            rec = (Rectangle){ 50, 200, 110, 60 };
+            DrawRectangleLines((int)rec.x - 20, (int)rec.y - 20, (int)rec.width + 40, (int)rec.height + 40, DARKGRAY);
+            DrawText("Rounded rectangle shadow", (int)rec.x - 20, (int)rec.y - 35, 10, DARKGRAY);
+
+            rec.y = screenHeight - rec.y - rec.height;
+            SetShaderValue(shader, roundedRectangle.rectangleLoc, (float[]){ rec.x, rec.y, rec.width, rec.height }, SHADER_UNIFORM_VEC4);
+
+            // Only shadow color
+            SetShaderValue(shader, roundedRectangle.colorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.shadowColorLoc, (float[]) { shadowColor.r/255.0f, shadowColor.g/255.0f, shadowColor.b/255.0f, shadowColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.borderColorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+
+            BeginShaderMode(shader);
+                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
+            EndShaderMode();
+
+            // Draw rectangle's border using shader
+            rec = (Rectangle){ 50, 330, 110, 60 };
+            DrawRectangleLines((int)rec.x - 20, (int)rec.y - 20, (int)rec.width + 40, (int)rec.height + 40, DARKGRAY);
+            DrawText("Rounded rectangle border", (int)rec.x - 20, (int)rec.y - 35, 10, DARKGRAY);
+
+            rec.y = screenHeight - rec.y - rec.height;
+            SetShaderValue(shader, roundedRectangle.rectangleLoc, (float[]){ rec.x, rec.y, rec.width, rec.height }, SHADER_UNIFORM_VEC4);
+
+            // Only border color
+            SetShaderValue(shader, roundedRectangle.colorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.shadowColorLoc, (float[]) { 0.0f, 0.0f, 0.0f, 0.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.borderColorLoc, (float[]) { borderColor.r/255.0f, borderColor.g/255.0f, borderColor.b/255.0f, borderColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+
+            BeginShaderMode(shader);
+                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
+            EndShaderMode();
+
+            // Draw one more rectangle with all three colors
+            rec = (Rectangle){ 240, 80, 500, 300 };
+            DrawRectangleLines((int)rec.x - 30, (int)rec.y - 30, (int)rec.width + 60, (int)rec.height + 60, DARKGRAY);
+            DrawText("Rectangle with all three combined", (int)rec.x - 30, (int)rec.y - 45, 10, DARKGRAY);
+
+            rec.y = screenHeight - rec.y - rec.height;
+            SetShaderValue(shader, roundedRectangle.rectangleLoc, (float[]){ rec.x, rec.y, rec.width, rec.height }, SHADER_UNIFORM_VEC4);
+
+            // All three colors
+            SetShaderValue(shader, roundedRectangle.colorLoc, (float[]) { rectangleColor.r/255.0f, rectangleColor.g/255.0f, rectangleColor.b/255.0f, rectangleColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.shadowColorLoc, (float[]) { shadowColor.r/255.0f, shadowColor.g/255.0f, shadowColor.b/255.0f, shadowColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+            SetShaderValue(shader, roundedRectangle.borderColorLoc, (float[]) { borderColor.r/255.0f, borderColor.g/255.0f, borderColor.b/255.0f, borderColor.a/255.0f }, SHADER_UNIFORM_VEC4);
+
+            BeginShaderMode(shader);
+                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
+            EndShaderMode();
+
+            DrawText("(c) Rounded rectangle SDF by Iñigo Quilez. MIT License.", screenWidth - 300, screenHeight - 20, 10, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader); // Unload shader
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definitions
+//------------------------------------------------------------------------------------
+
+// Create a rounded rectangle and set uniform locations
+RoundedRectangle CreateRoundedRectangle(Vector4 cornerRadius, float shadowRadius, Vector2 shadowOffset, float shadowScale, float borderThickness, Shader shader)
+{
+    RoundedRectangle rec;
+    rec.cornerRadius = cornerRadius;
+    rec.shadowRadius = shadowRadius;
+    rec.shadowOffset = shadowOffset;
+    rec.shadowScale = shadowScale;
+    rec.borderThickness = borderThickness;
+
+    // Get shader uniform locations
+    rec.rectangleLoc = GetShaderLocation(shader, "rectangle");
+    rec.radiusLoc = GetShaderLocation(shader, "radius");
+    rec.colorLoc = GetShaderLocation(shader, "color");
+    rec.shadowRadiusLoc = GetShaderLocation(shader, "shadowRadius");
+    rec.shadowOffsetLoc = GetShaderLocation(shader, "shadowOffset");
+    rec.shadowScaleLoc = GetShaderLocation(shader, "shadowScale");
+    rec.shadowColorLoc = GetShaderLocation(shader, "shadowColor");
+    rec.borderThicknessLoc = GetShaderLocation(shader, "borderThickness");
+    rec.borderColorLoc = GetShaderLocation(shader, "borderColor");
+
+    UpdateRoundedRectangle(rec, shader);
+
+    return rec;
+}
+
+// Update rounded rectangle uniforms
+void UpdateRoundedRectangle(RoundedRectangle rec, Shader shader)
+{
+    SetShaderValue(shader, rec.radiusLoc, (float[]){ rec.cornerRadius.x, rec.cornerRadius.y, rec.cornerRadius.z, rec.cornerRadius.w }, SHADER_UNIFORM_VEC4);
+    SetShaderValue(shader, rec.shadowRadiusLoc, &rec.shadowRadius, SHADER_UNIFORM_FLOAT);
+    SetShaderValue(shader, rec.shadowOffsetLoc, (float[]){ rec.shadowOffset.x, rec.shadowOffset.y }, SHADER_UNIFORM_VEC2);
+    SetShaderValue(shader, rec.shadowScaleLoc, &rec.shadowScale, SHADER_UNIFORM_FLOAT);
+    SetShaderValue(shader, rec.borderThicknessLoc, &rec.borderThickness, SHADER_UNIFORM_FLOAT);
+}
diff --git a/raylib/examples/shaders/shaders_shadowmap.c b/raylib/examples/shaders/shaders_shadowmap.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_shadowmap.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Shadowmap
-*
-*   Example originally created with raylib 5.0, last time updated with raylib 5.0
-*
-*   Example contributed by @TheManTheMythTheGameDev and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-********************************************************************************************/
-
-#include "raylib.h"
-#include "raymath.h"
-#include "rlgl.h"
-
-#if defined(PLATFORM_DESKTOP)
-#define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-#define GLSL_VERSION            120
-#endif
-
-#define SHADOWMAP_RESOLUTION 1024
-
-RenderTexture2D LoadShadowmapRenderTexture(int width, int height);
-void UnloadShadowmapRenderTexture(RenderTexture2D target);
-void DrawScene(Model cube, Model robot);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT);
-    // Shadows are a HUGE topic, and this example shows an extremely simple implementation of the shadowmapping algorithm,
-    // which is the industry standard for shadows. This algorithm can be extended in a ridiculous number of ways to improve
-    // realism and also adapt it for different scenes. This is pretty much the simplest possible implementation.
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap");
-
-    Camera3D cam = (Camera3D){ 0 };
-    cam.position = (Vector3){ 10.0f, 10.0f, 10.0f };
-    cam.target = Vector3Zero();
-    cam.projection = CAMERA_PERSPECTIVE;
-    cam.up = (Vector3){ 0.0f, 1.0f, 0.0f };
-    cam.fovy = 45.0f;
-
-    Shader shadowShader = LoadShader(TextFormat("resources/shaders/glsl%i/shadowmap.vs", GLSL_VERSION),
-                                     TextFormat("resources/shaders/glsl%i/shadowmap.fs", GLSL_VERSION));
-    shadowShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shadowShader, "viewPos");
-    Vector3 lightDir = Vector3Normalize((Vector3){ 0.35f, -1.0f, -0.35f });
-    Color lightColor = WHITE;
-    Vector4 lightColorNormalized = ColorNormalize(lightColor);
-    int lightDirLoc = GetShaderLocation(shadowShader, "lightDir");
-    int lightColLoc = GetShaderLocation(shadowShader, "lightColor");
-    SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
-    SetShaderValue(shadowShader, lightColLoc, &lightColorNormalized, SHADER_UNIFORM_VEC4);
-    int ambientLoc = GetShaderLocation(shadowShader, "ambient");
-    float ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
-    SetShaderValue(shadowShader, ambientLoc, ambient, SHADER_UNIFORM_VEC4);
-    int lightVPLoc = GetShaderLocation(shadowShader, "lightVP");
-    int shadowMapLoc = GetShaderLocation(shadowShader, "shadowMap");
-    int shadowMapResolution = SHADOWMAP_RESOLUTION;
-    SetShaderValue(shadowShader, GetShaderLocation(shadowShader, "shadowMapResolution"), &shadowMapResolution, SHADER_UNIFORM_INT);
-
-    Model cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
-    cube.materials[0].shader = shadowShader;
-    Model robot = LoadModel("resources/models/robot.glb");
-    for (int i = 0; i < robot.materialCount; i++)
-    {
-        robot.materials[i].shader = shadowShader;
-    }
-    int animCount = 0;
-    ModelAnimation* robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount);
-
-    RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
-    // For the shadowmapping algorithm, we will be rendering everything from the light's point of view
-    Camera3D lightCam = (Camera3D){ 0 };
-    lightCam.position = Vector3Scale(lightDir, -15.0f);
-    lightCam.target = Vector3Zero();
-    // Use an orthographic projection for directional lights
-    lightCam.projection = CAMERA_ORTHOGRAPHIC;
-    lightCam.up = (Vector3){ 0.0f, 1.0f, 0.0f };
-    lightCam.fovy = 20.0f;
-
-    SetTargetFPS(60);
-    //--------------------------------------------------------------------------------------
-    int fc = 0;
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        float dt = GetFrameTime();
-
-        Vector3 cameraPos = cam.position;
-        SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3);
-        UpdateCamera(&cam, CAMERA_ORBITAL);
-
-        fc++;
-        fc %= (robotAnimations[0].frameCount);
-        UpdateModelAnimation(robot, robotAnimations[0], fc);
-
-        const float cameraSpeed = 0.05f;
-        if (IsKeyDown(KEY_LEFT))
-        {
-            if (lightDir.x < 0.6f)
-                lightDir.x += cameraSpeed * 60.0f * dt;
-        }
-        if (IsKeyDown(KEY_RIGHT))
-        {
-            if (lightDir.x > -0.6f)
-                lightDir.x -= cameraSpeed * 60.0f * dt;
-        }
-        if (IsKeyDown(KEY_UP))
-        {
-            if (lightDir.z < 0.6f)
-                lightDir.z += cameraSpeed * 60.0f * dt;
-        }
-        if (IsKeyDown(KEY_DOWN))
-        {
-            if (lightDir.z > -0.6f)
-                lightDir.z -= cameraSpeed * 60.0f * dt;
-        }
-        lightDir = Vector3Normalize(lightDir);
-        lightCam.position = Vector3Scale(lightDir, -15.0f);
-        SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-        // First, render all objects into the shadowmap
-        // The idea is, we record all the objects' depths (as rendered from the light source's point of view) in a buffer
-        // Anything that is "visible" to the light is in light, anything that isn't is in shadow
-        // We can later use the depth buffer when rendering everything from the player's point of view
-        // to determine whether a given point is "visible" to the light
-
-        // Record the light matrices for future use!
-        Matrix lightView;
-        Matrix lightProj;
-        BeginTextureMode(shadowMap);
-        ClearBackground(WHITE);
-        BeginMode3D(lightCam);
-            lightView = rlGetMatrixModelview();
-            lightProj = rlGetMatrixProjection();
-            DrawScene(cube, robot);
-        EndMode3D();
-        EndTextureMode();
-        Matrix lightViewProj = MatrixMultiply(lightView, lightProj);
-
-        ClearBackground(RAYWHITE);
-
-        SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj);
-
-        rlEnableShader(shadowShader.id);
-        int slot = 10; // Can be anything 0 to 15, but 0 will probably be taken up
-        rlActiveTextureSlot(10);
-        rlEnableTexture(shadowMap.depth.id);
-        rlSetUniform(shadowMapLoc, &slot, SHADER_UNIFORM_INT, 1);
-
-        BeginMode3D(cam);
-
-            // Draw the same exact things as we drew in the shadowmap!
-            DrawScene(cube, robot);
-        
-        EndMode3D();
-
-        DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 320, screenHeight - 20, 10, GRAY);
-        DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED);
-
-        EndDrawing();
-
-        if (IsKeyPressed(KEY_F))
-        {
-            TakeScreenshot("shaders_shadowmap.png");
-        }
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-
-    UnloadShader(shadowShader);
-    UnloadModel(cube);
-    UnloadModel(robot);
-    UnloadModelAnimations(robotAnimations, animCount);
-    UnloadShadowmapRenderTexture(shadowMap);
-
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-RenderTexture2D LoadShadowmapRenderTexture(int width, int height)
-{
-    RenderTexture2D target = { 0 };
-
-    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
-    target.texture.width = width;
-    target.texture.height = height;
-
-    if (target.id > 0)
-    {
-        rlEnableFramebuffer(target.id);
-
-        // Create depth texture
-        // We don't need a color texture for the shadowmap
-        target.depth.id = rlLoadTextureDepth(width, height, false);
-        target.depth.width = width;
-        target.depth.height = height;
-        target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
-        target.depth.mipmaps = 1;
-
-        // Attach depth texture to FBO
-        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
-
-        // Check if fbo is complete with attachments (valid)
-        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
-
-        rlDisableFramebuffer();
-    }
-    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
-
-    return target;
-}
-
-// Unload shadowmap render texture from GPU memory (VRAM)
-void UnloadShadowmapRenderTexture(RenderTexture2D target)
-{
-    if (target.id > 0)
-    {
-        // NOTE: Depth texture/renderbuffer is automatically
-        // queried and deleted before deleting framebuffer
-        rlUnloadFramebuffer(target.id);
-    }
-}
-
-void DrawScene(Model cube, Model robot)
-{
-    DrawModelEx(cube, Vector3Zero(), (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 10.0f, 1.0f, 10.0f }, BLUE);
-    DrawModelEx(cube, (Vector3) { 1.5f, 1.0f, -1.5f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, Vector3One(), WHITE);
-    DrawModelEx(robot, (Vector3) { 0.0f, 0.5f, 0.0f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 1.0f, 1.0f, 1.0f }, RED);
-}
diff --git a/raylib/examples/shaders/shaders_shadowmap_rendering.c b/raylib/examples/shaders/shaders_shadowmap_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_shadowmap_rendering.c
@@ -0,0 +1,256 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - shadowmap rendering
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.0, last time updated with raylib 5.0
+*
+*   Example contributed by TheManTheMythTheGameDev (@TheManTheMythTheGameDev) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2023-2025 TheManTheMythTheGameDev (@TheManTheMythTheGameDev)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+#include "rlgl.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#define SHADOWMAP_RESOLUTION 1024
+
+static RenderTexture2D LoadShadowmapRenderTexture(int width, int height);
+static void UnloadShadowmapRenderTexture(RenderTexture2D target);
+static void DrawScene(Model cube, Model robot);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    // Shadows are a HUGE topic, and this example shows an extremely simple implementation of the shadowmapping algorithm,
+    // which is the industry standard for shadows. This algorithm can be extended in a ridiculous number of ways to improve
+    // realism and also adapt it for different scenes. This is pretty much the simplest possible implementation
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shadowmap rendering");
+
+    Camera3D camera = (Camera3D){ 0 };
+    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f };
+    camera.target = Vector3Zero();
+    camera.projection = CAMERA_PERSPECTIVE;
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    camera.fovy = 45.0f;
+
+    Shader shadowShader = LoadShader(TextFormat("resources/shaders/glsl%i/shadowmap.vs", GLSL_VERSION),
+                                     TextFormat("resources/shaders/glsl%i/shadowmap.fs", GLSL_VERSION));
+    shadowShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shadowShader, "viewPos");
+
+    Vector3 lightDir = Vector3Normalize((Vector3){ 0.35f, -1.0f, -0.35f });
+    Color lightColor = WHITE;
+    Vector4 lightColorNormalized = ColorNormalize(lightColor);
+    int lightDirLoc = GetShaderLocation(shadowShader, "lightDir");
+    int lightColLoc = GetShaderLocation(shadowShader, "lightColor");
+    SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
+    SetShaderValue(shadowShader, lightColLoc, &lightColorNormalized, SHADER_UNIFORM_VEC4);
+    int ambientLoc = GetShaderLocation(shadowShader, "ambient");
+    float ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f};
+    SetShaderValue(shadowShader, ambientLoc, ambient, SHADER_UNIFORM_VEC4);
+    int lightVPLoc = GetShaderLocation(shadowShader, "lightVP");
+    int shadowMapLoc = GetShaderLocation(shadowShader, "shadowMap");
+    int shadowMapResolution = SHADOWMAP_RESOLUTION;
+    SetShaderValue(shadowShader, GetShaderLocation(shadowShader, "shadowMapResolution"), &shadowMapResolution, SHADER_UNIFORM_INT);
+
+    Model cube = LoadModelFromMesh(GenMeshCube(1.0f, 1.0f, 1.0f));
+    cube.materials[0].shader = shadowShader;
+    Model robot = LoadModel("resources/models/robot.glb");
+    for (int i = 0; i < robot.materialCount; i++) robot.materials[i].shader = shadowShader;
+    int animCount = 0;
+    ModelAnimation *robotAnimations = LoadModelAnimations("resources/models/robot.glb", &animCount);
+
+    RenderTexture2D shadowMap = LoadShadowmapRenderTexture(SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
+
+    // For the shadowmapping algorithm, we will be rendering everything from the light's point of view
+    Camera3D lightCamera = { 0 };
+    lightCamera.position = Vector3Scale(lightDir, -15.0f);
+    lightCamera.target = Vector3Zero();
+    lightCamera.projection = CAMERA_ORTHOGRAPHIC; // Use an orthographic projection for directional lights
+    lightCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    lightCamera.fovy = 20.0f;
+
+    int frameCounter = 0;
+
+    // Store the light matrices
+    Matrix lightView = { 0 };
+    Matrix lightProj = { 0 };
+    Matrix lightViewProj = { 0 };
+    int textureActiveSlot = 10; // Can be anything 0 to 15, but 0 will probably be taken up
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float deltaTime = GetFrameTime();
+
+        Vector3 cameraPos = camera.position;
+        SetShaderValue(shadowShader, shadowShader.locs[SHADER_LOC_VECTOR_VIEW], &cameraPos, SHADER_UNIFORM_VEC3);
+        UpdateCamera(&camera, CAMERA_ORBITAL);
+
+        frameCounter++;
+        frameCounter %= (robotAnimations[0].frameCount);
+        UpdateModelAnimation(robot, robotAnimations[0], frameCounter);
+
+        // Move light with arrow keys
+        const float cameraSpeed = 0.05f;
+        if (IsKeyDown(KEY_LEFT))
+        {
+            if (lightDir.x < 0.6f) lightDir.x += cameraSpeed*60.0f*deltaTime;
+        }
+        if (IsKeyDown(KEY_RIGHT))
+        {
+            if (lightDir.x > -0.6f) lightDir.x -= cameraSpeed*60.0f*deltaTime;
+        }
+        if (IsKeyDown(KEY_UP))
+        {
+            if (lightDir.z < 0.6f) lightDir.z += cameraSpeed*60.0f*deltaTime;
+        }
+        if (IsKeyDown(KEY_DOWN))
+        {
+            if (lightDir.z > -0.6f) lightDir.z -= cameraSpeed*60.0f*deltaTime;
+        }
+
+        lightDir = Vector3Normalize(lightDir);
+        lightCamera.position = Vector3Scale(lightDir, -15.0f);
+        SetShaderValue(shadowShader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        // PASS 01: Render all objects into the shadowmap render texture
+        // We record all the objects' depths (as rendered from the light source's point of view) in a buffer
+        // Anything that is "visible" to the light is in light, anything that isn't is in shadow
+        // We can later use the depth buffer when rendering everything from the player's point of view
+        // to determine whether a given point is "visible" to the light
+        BeginTextureMode(shadowMap);
+            ClearBackground(WHITE);
+
+            BeginMode3D(lightCamera);
+                lightView = rlGetMatrixModelview();
+                lightProj = rlGetMatrixProjection();
+                DrawScene(cube, robot);
+            EndMode3D();
+
+        EndTextureMode();
+        lightViewProj = MatrixMultiply(lightView, lightProj);
+
+        // PASS 02: Draw the scene into main framebuffer, using the generated shadowmap
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            SetShaderValueMatrix(shadowShader, lightVPLoc, lightViewProj);
+            rlEnableShader(shadowShader.id);
+
+            rlActiveTextureSlot(textureActiveSlot);
+            rlEnableTexture(shadowMap.depth.id);
+            rlSetUniform(shadowMapLoc, &textureActiveSlot, SHADER_UNIFORM_INT, 1);
+
+            BeginMode3D(camera);
+                DrawScene(cube, robot); // Draw the same exact things as we drew in the shadowmap!
+            EndMode3D();
+
+            DrawText("Use the arrow keys to rotate the light!", 10, 10, 30, RED);
+            DrawText("Shadows in raylib using the shadowmapping algorithm!", screenWidth - 280, screenHeight - 20, 10, GRAY);
+
+        EndDrawing();
+
+        if (IsKeyPressed(KEY_F)) TakeScreenshot("shaders_shadowmap.png");
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shadowShader);
+    UnloadModel(cube);
+    UnloadModel(robot);
+    UnloadModelAnimations(robotAnimations, animCount);
+    UnloadShadowmapRenderTexture(shadowMap);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Load render texture for shadowmap projection
+// NOTE: Load framebuffer with only a texture depth attachment,
+// no color attachment required for shadowmap
+static RenderTexture2D LoadShadowmapRenderTexture(int width, int height)
+{
+    RenderTexture2D target = { 0 };
+
+    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
+    target.texture.width = width;
+    target.texture.height = height;
+
+    if (target.id > 0)
+    {
+        rlEnableFramebuffer(target.id);
+
+        // Create depth texture
+        // NOTE: No need a color texture attachment for the shadowmap
+        target.depth.id = rlLoadTextureDepth(width, height, false);
+        target.depth.width = width;
+        target.depth.height = height;
+        target.depth.format = 19; // DEPTH_COMPONENT_24BIT?
+        target.depth.mipmaps = 1;
+
+        // Attach depth texture to FBO
+        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
+
+        // Check if fbo is complete with attachments (valid)
+        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
+
+        rlDisableFramebuffer();
+    }
+    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
+
+    return target;
+}
+
+// Unload shadowmap render texture from GPU memory (VRAM)
+static void UnloadShadowmapRenderTexture(RenderTexture2D target)
+{
+    if (target.id > 0)
+    {
+        // NOTE: Depth texture/renderbuffer is automatically
+        // queried and deleted before deleting framebuffer
+        rlUnloadFramebuffer(target.id);
+    }
+}
+
+// Draw full scene projecting shadows
+// NOTE: Required  to be called several time to generate shadowmap
+static void DrawScene(Model cube, Model robot)
+{
+    DrawModelEx(cube, Vector3Zero(), (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 10.0f, 1.0f, 10.0f }, BLUE);
+    DrawModelEx(cube, (Vector3) { 1.5f, 1.0f, -1.5f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, Vector3One(), WHITE);
+    DrawModelEx(robot, (Vector3) { 0.0f, 0.5f, 0.0f }, (Vector3) { 0.0f, 1.0f, 0.0f }, 0.0f, (Vector3) { 1.0f, 1.0f, 1.0f }, RED);
+}
diff --git a/raylib/examples/shaders/shaders_shapes_textures.c b/raylib/examples/shaders/shaders_shapes_textures.c
--- a/raylib/examples/shaders/shaders_shapes_textures.c
+++ b/raylib/examples/shaders/shaders_shapes_textures.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Apply a shader to some shape or texture
+*   raylib [shaders] example - shapes textures
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -14,7 +16,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -36,7 +38,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes and texture shaders");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shapes textures");
 
     Texture2D fudesumi = LoadTexture("resources/fudesumi.png");
 
diff --git a/raylib/examples/shaders/shaders_simple_mask.c b/raylib/examples/shaders/shaders_simple_mask.c
--- a/raylib/examples/shaders/shaders_simple_mask.c
+++ b/raylib/examples/shaders/shaders_simple_mask.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Simple shader mask
+*   raylib [shaders] example - simple mask
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.7
 *
 *   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************
 *
@@ -39,7 +41,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - simple shader mask");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - simple mask");
 
     // Define the camera to look into our 3d world
     Camera camera = { 0 };
@@ -95,7 +97,7 @@
         // Update
         //----------------------------------------------------------------------------------
         UpdateCamera(&camera, CAMERA_FIRST_PERSON);
-        
+
         framesCounter++;
         rotation.x += 0.01f;
         rotation.y += 0.005f;
diff --git a/raylib/examples/shaders/shaders_spotlight.c b/raylib/examples/shaders/shaders_spotlight.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_spotlight.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Simple shader mask
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 3.7
-*
-*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************
-*
-*   The shader makes alpha holes in the forground to give the appearance of a top
-*   down look at a spotlight casting a pool of light...
-*
-*   The right hand side of the screen there is just enough light to see whats
-*   going on without the spot light, great for a stealth type game where you
-*   have to avoid the spotlights.
-*
-*   The left hand side of the screen is in pitch dark except for where the spotlights are.
-*
-*   Although this example doesn't scale like the letterbox example, you could integrate
-*   the two techniques, but by scaling the actual colour of the render texture rather
-*   than using alpha as a mask.
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "raymath.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-#define MAX_SPOTS         3        // NOTE: It must be the same as define in shader
-#define MAX_STARS       400
-
-// Spot data
-typedef struct Spot {
-    Vector2 position;
-    Vector2 speed;
-    float inner;
-    float radius;
-
-    // Shader locations
-    unsigned int positionLoc;
-    unsigned int innerLoc;
-    unsigned int radiusLoc;
-} Spot;
-
-// Stars in the star field have a position and velocity
-typedef struct Star {
-    Vector2 position;
-    Vector2 speed;
-} Star;
-
-static void UpdateStar(Star *s);
-static void ResetStar(Star *s);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - shader spotlight");
-    HideCursor();
-
-    Texture texRay = LoadTexture("resources/raysan.png");
-
-    Star stars[MAX_STARS] = { 0 };
-
-    for (int n = 0; n < MAX_STARS; n++) ResetStar(&stars[n]);
-
-    // Progress all the stars on, so they don't all start in the centre
-    for (int m = 0; m < screenWidth/2.0; m++)
-    {
-        for (int n = 0; n < MAX_STARS; n++) UpdateStar(&stars[n]);
-    }
-
-    int frameCounter = 0;
-
-    // Use default vert shader
-    Shader shdrSpot = LoadShader(0, TextFormat("resources/shaders/glsl%i/spotlight.fs", GLSL_VERSION));
-
-    // Get the locations of spots in the shader
-    Spot spots[MAX_SPOTS];
-
-    for (int i = 0; i < MAX_SPOTS; i++)
-    {
-        char posName[32] = "spots[x].pos\0";
-        char innerName[32] = "spots[x].inner\0";
-        char radiusName[32] = "spots[x].radius\0";
-
-        posName[6] = '0' + i;
-        innerName[6] = '0' + i;
-        radiusName[6] = '0' + i;
-
-        spots[i].positionLoc = GetShaderLocation(shdrSpot, posName);
-        spots[i].innerLoc = GetShaderLocation(shdrSpot, innerName);
-        spots[i].radiusLoc = GetShaderLocation(shdrSpot, radiusName);
-
-    }
-
-    // Tell the shader how wide the screen is so we can have
-    // a pitch black half and a dimly lit half.
-    unsigned int wLoc = GetShaderLocation(shdrSpot, "screenWidth");
-    float sw = (float)GetScreenWidth();
-    SetShaderValue(shdrSpot, wLoc, &sw, SHADER_UNIFORM_FLOAT);
-
-    // Randomize the locations and velocities of the spotlights
-    // and initialize the shader locations
-    for (int i = 0; i < MAX_SPOTS; i++)
-    {
-        spots[i].position.x = (float)GetRandomValue(64, screenWidth - 64);
-        spots[i].position.y = (float)GetRandomValue(64, screenHeight - 64);
-        spots[i].speed = (Vector2){ 0, 0 };
-
-        while ((fabs(spots[i].speed.x) + fabs(spots[i].speed.y)) < 2)
-        {
-            spots[i].speed.x = GetRandomValue(-400, 40) / 10.0f;
-            spots[i].speed.y = GetRandomValue(-400, 40) / 10.0f;
-        }
-
-        spots[i].inner = 28.0f * (i + 1);
-        spots[i].radius = 48.0f * (i + 1);
-
-        SetShaderValue(shdrSpot, spots[i].positionLoc, &spots[i].position.x, SHADER_UNIFORM_VEC2);
-        SetShaderValue(shdrSpot, spots[i].innerLoc, &spots[i].inner, SHADER_UNIFORM_FLOAT);
-        SetShaderValue(shdrSpot, spots[i].radiusLoc, &spots[i].radius, SHADER_UNIFORM_FLOAT);
-    }
-
-    SetTargetFPS(60);               // Set  to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        frameCounter++;
-
-        // Move the stars, resetting them if the go offscreen
-        for (int n = 0; n < MAX_STARS; n++) UpdateStar(&stars[n]);
-
-        // Update the spots, send them to the shader
-        for (int i = 0; i < MAX_SPOTS; i++)
-        {
-            if (i == 0)
-            {
-                Vector2 mp = GetMousePosition();
-                spots[i].position.x = mp.x;
-                spots[i].position.y = screenHeight - mp.y;
-            }
-            else
-            {
-                spots[i].position.x += spots[i].speed.x;
-                spots[i].position.y += spots[i].speed.y;
-
-                if (spots[i].position.x < 64) spots[i].speed.x = -spots[i].speed.x;
-                if (spots[i].position.x > (screenWidth - 64)) spots[i].speed.x = -spots[i].speed.x;
-                if (spots[i].position.y < 64) spots[i].speed.y = -spots[i].speed.y;
-                if (spots[i].position.y > (screenHeight - 64)) spots[i].speed.y = -spots[i].speed.y;
-            }
-
-            SetShaderValue(shdrSpot, spots[i].positionLoc, &spots[i].position.x, SHADER_UNIFORM_VEC2);
-        }
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(DARKBLUE);
-
-            // Draw stars and bobs
-            for (int n = 0; n < MAX_STARS; n++)
-            {
-                // Single pixel is just too small these days!
-                DrawRectangle((int)stars[n].position.x, (int)stars[n].position.y, 2, 2, WHITE);
-            }
-
-            for (int i = 0; i < 16; i++)
-            {
-                DrawTexture(texRay,
-                    (int)((screenWidth/2.0f) + cos((frameCounter + i*8)/51.45f)*(screenWidth/2.2f) - 32),
-                    (int)((screenHeight/2.0f) + sin((frameCounter + i*8)/17.87f)*(screenHeight/4.2f)), WHITE);
-            }
-
-            // Draw spot lights
-            BeginShaderMode(shdrSpot);
-                // Instead of a blank rectangle you could render here
-                // a render texture of the full screen used to do screen
-                // scaling (slight adjustment to shader would be required
-                // to actually pay attention to the colour!)
-                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
-            EndShaderMode();
-
-            DrawFPS(10, 10);
-
-            DrawText("Move the mouse!", 10, 30, 20, GREEN);
-            DrawText("Pitch Black", (int)(screenWidth*0.2f), screenHeight/2, 20, GREEN);
-            DrawText("Dark", (int)(screenWidth*.66f), screenHeight/2, 20, GREEN);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texRay);
-    UnloadShader(shdrSpot);
-
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-
-static void ResetStar(Star *s)
-{
-    s->position = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
-
-    do
-    {
-        s->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f;
-        s->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f;
-
-    } while (!(fabs(s->speed.x) + (fabs(s->speed.y) > 1)));
-
-    s->position = Vector2Add(s->position, Vector2Multiply(s->speed, (Vector2){ 8.0f, 8.0f }));
-}
-
-static void UpdateStar(Star *s)
-{
-    s->position = Vector2Add(s->position, s->speed);
-
-    if ((s->position.x < 0) || (s->position.x > GetScreenWidth()) ||
-        (s->position.y < 0) || (s->position.y > GetScreenHeight()))
-    {
-        ResetStar(s);
-    }
-}
-
-
diff --git a/raylib/examples/shaders/shaders_spotlight_rendering.c b/raylib/examples/shaders/shaders_spotlight_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_spotlight_rendering.c
@@ -0,0 +1,261 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - spotlight rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 3.7
+*
+*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************
+*
+*   The shader makes alpha holes in the forground to give the appearance of a top
+*   down look at a spotlight casting a pool of light...
+*
+*   The right hand side of the screen there is just enough light to see whats
+*   going on without the spot light, great for a stealth type game where you
+*   have to avoid the spotlights
+*
+*   The left hand side of the screen is in pitch dark except for where the spotlights are
+*
+*   Although this example doesn't scale like the letterbox example, you could integrate
+*   the two techniques, but by scaling the actual colour of the render texture rather
+*   than using alpha as a mask
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+#define MAX_SPOTS         3        // NOTE: It must be the same as define in shader
+#define MAX_STARS       400
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Spot data
+typedef struct Spot {
+    Vector2 position;
+    Vector2 speed;
+    float inner;
+    float radius;
+
+    // Shader locations
+    unsigned int positionLoc;
+    unsigned int innerLoc;
+    unsigned int radiusLoc;
+} Spot;
+
+// Stars in the star field have a position and velocity
+typedef struct Star {
+    Vector2 position;
+    Vector2 speed;
+} Star;
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+static void UpdateStar(Star *star);
+static void ResetStar(Star *star);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - spotlight rendering");
+    HideCursor();
+
+    Texture texRay = LoadTexture("resources/raysan.png");
+
+    Star stars[MAX_STARS] = { 0 };
+
+    for (int n = 0; n < MAX_STARS; n++) ResetStar(&stars[n]);
+
+    // Progress all the stars on, so they don't all start in the centre
+    for (int m = 0; m < screenWidth/2.0; m++)
+    {
+        for (int n = 0; n < MAX_STARS; n++) UpdateStar(&stars[n]);
+    }
+
+    int frameCounter = 0;
+
+    // Use default vert shader
+    Shader shdrSpot = LoadShader(0, TextFormat("resources/shaders/glsl%i/spotlight.fs", GLSL_VERSION));
+
+    // Get the locations of spots in the shader
+    Spot spots[MAX_SPOTS];
+
+    for (int i = 0; i < MAX_SPOTS; i++)
+    {
+        char posName[32] = "spots[x].pos\0";
+        char innerName[32] = "spots[x].inner\0";
+        char radiusName[32] = "spots[x].radius\0";
+
+        posName[6] = '0' + i;
+        innerName[6] = '0' + i;
+        radiusName[6] = '0' + i;
+
+        spots[i].positionLoc = GetShaderLocation(shdrSpot, posName);
+        spots[i].innerLoc = GetShaderLocation(shdrSpot, innerName);
+        spots[i].radiusLoc = GetShaderLocation(shdrSpot, radiusName);
+    }
+
+    // Tell the shader how wide the screen is so we can have
+    // a pitch black half and a dimly lit half
+    unsigned int wLoc = GetShaderLocation(shdrSpot, "screenWidth");
+    float sw = (float)GetScreenWidth();
+    SetShaderValue(shdrSpot, wLoc, &sw, SHADER_UNIFORM_FLOAT);
+
+    // Randomize the locations and velocities of the spotlights
+    // and initialize the shader locations
+    for (int i = 0; i < MAX_SPOTS; i++)
+    {
+        spots[i].position.x = (float)GetRandomValue(64, screenWidth - 64);
+        spots[i].position.y = (float)GetRandomValue(64, screenHeight - 64);
+        spots[i].speed = (Vector2){ 0, 0 };
+
+        while ((fabs(spots[i].speed.x) + fabs(spots[i].speed.y)) < 2)
+        {
+            spots[i].speed.x = GetRandomValue(-400, 40)/25.0f;
+            spots[i].speed.y = GetRandomValue(-400, 40)/25.0f;
+        }
+
+        spots[i].inner = 28.0f*(i + 1);
+        spots[i].radius = 48.0f*(i + 1);
+
+        SetShaderValue(shdrSpot, spots[i].positionLoc, &spots[i].position.x, SHADER_UNIFORM_VEC2);
+        SetShaderValue(shdrSpot, spots[i].innerLoc, &spots[i].inner, SHADER_UNIFORM_FLOAT);
+        SetShaderValue(shdrSpot, spots[i].radiusLoc, &spots[i].radius, SHADER_UNIFORM_FLOAT);
+    }
+
+    SetTargetFPS(60);               // Set  to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        frameCounter++;
+
+        // Move the stars, resetting them if the go offscreen
+        for (int n = 0; n < MAX_STARS; n++) UpdateStar(&stars[n]);
+
+        // Update the spots, send them to the shader
+        for (int i = 0; i < MAX_SPOTS; i++)
+        {
+            if (i == 0)
+            {
+                Vector2 mp = GetMousePosition();
+                spots[i].position.x = mp.x;
+                spots[i].position.y = screenHeight - mp.y;
+            }
+            else
+            {
+                spots[i].position.x += spots[i].speed.x;
+                spots[i].position.y += spots[i].speed.y;
+
+                if (spots[i].position.x < 64) spots[i].speed.x = -spots[i].speed.x;
+                if (spots[i].position.x > (screenWidth - 64)) spots[i].speed.x = -spots[i].speed.x;
+                if (spots[i].position.y < 64) spots[i].speed.y = -spots[i].speed.y;
+                if (spots[i].position.y > (screenHeight - 64)) spots[i].speed.y = -spots[i].speed.y;
+            }
+
+            SetShaderValue(shdrSpot, spots[i].positionLoc, &spots[i].position.x, SHADER_UNIFORM_VEC2);
+        }
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(DARKBLUE);
+
+            // Draw stars and bobs
+            for (int n = 0; n < MAX_STARS; n++)
+            {
+                // Single pixel is just too small these days!
+                DrawRectangle((int)stars[n].position.x, (int)stars[n].position.y, 2, 2, WHITE);
+            }
+
+            for (int i = 0; i < 16; i++)
+            {
+                DrawTexture(texRay,
+                    (int)((screenWidth/2.0f) + cos((frameCounter + i*8)/51.45f)*(screenWidth/2.2f) - 32),
+                    (int)((screenHeight/2.0f) + sin((frameCounter + i*8)/17.87f)*(screenHeight/4.2f)), WHITE);
+            }
+
+            // Draw spot lights
+            BeginShaderMode(shdrSpot);
+                // Instead of a blank rectangle you could render here
+                // a render texture of the full screen used to do screen
+                // scaling (slight adjustment to shader would be required
+                // to actually pay attention to the colour!)
+                DrawRectangle(0, 0, screenWidth, screenHeight, WHITE);
+            EndShaderMode();
+
+            DrawFPS(10, 10);
+
+            DrawText("Move the mouse!", 10, 30, 20, GREEN);
+            DrawText("Pitch Black", (int)(screenWidth*0.2f), screenHeight/2, 20, GREEN);
+            DrawText("Dark", (int)(screenWidth*.66f), screenHeight/2, 20, GREEN);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texRay);
+    UnloadShader(shdrSpot);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+static void ResetStar(Star *star)
+{
+    star->position = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
+
+    star->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f;
+    star->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f;
+
+    while (!(fabs(star->speed.x) + (fabs(star->speed.y) > 1)))
+    {
+        star->speed.x = (float)GetRandomValue(-1000, 1000)/100.0f;
+        star->speed.y = (float)GetRandomValue(-1000, 1000)/100.0f;
+    }
+
+    star->position = Vector2Add(star->position, Vector2Multiply(star->speed, (Vector2){ 8.0f, 8.0f }));
+}
+
+static void UpdateStar(Star *star)
+{
+    star->position = Vector2Add(star->position, star->speed);
+
+    if ((star->position.x < 0) || (star->position.x > GetScreenWidth()) ||
+        (star->position.y < 0) || (star->position.y > GetScreenHeight())) ResetStar(star);
+}
diff --git a/raylib/examples/shaders/shaders_texture_drawing.c b/raylib/examples/shaders/shaders_texture_drawing.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_texture_drawing.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [textures] example - Texture drawing
-*
-*   NOTE: This example illustrates how to draw into a blank texture using a shader
-*
-*   Example originally created with raylib 2.0, last time updated with raylib 3.7
-*
-*   Example contributed by Michał Ciesielski and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Michał Ciesielski and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#if defined(PLATFORM_DESKTOP)
-    #define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-    #define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing");
-
-    Image imBlank = GenImageColor(1024, 1024, BLANK);
-    Texture2D texture = LoadTextureFromImage(imBlank);  // Load blank texture to fill on shader
-    UnloadImage(imBlank);
-
-    // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version
-    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/cubes_panning.fs", GLSL_VERSION));
-
-    float time = 0.0f;
-    int timeLoc = GetShaderLocation(shader, "uTime");
-    SetShaderValue(shader, timeLoc, &time, SHADER_UNIFORM_FLOAT);
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    // -------------------------------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        time = (float)GetTime();
-        SetShaderValue(shader, timeLoc, &time, SHADER_UNIFORM_FLOAT);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginShaderMode(shader);    // Enable our custom shader for next shapes/textures drawings
-                DrawTexture(texture, 0, 0, WHITE);  // Drawing BLANK texture, all magic happens on shader
-            EndShaderMode();            // Disable our custom shader, return to default shader
-
-            DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, MAROON);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadShader(shader);
-    UnloadTexture(texture);
-
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shaders/shaders_texture_outline.c b/raylib/examples/shaders/shaders_texture_outline.c
--- a/raylib/examples/shaders/shaders_texture_outline.c
+++ b/raylib/examples/shaders/shaders_texture_outline.c
@@ -1,18 +1,20 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Apply an shdrOutline to a texture
+*   raylib [shaders] example - texture outline
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   Example originally created with raylib 4.0, last time updated with raylib 4.0
 *
-*   Example contributed by Samuel Skiff (@GoldenThumbs) and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Serenity Skiff (@GoldenThumbs) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Samuel SKiff (@GoldenThumbs) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Serenity Skiff (@GoldenThumbs) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -34,7 +36,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Apply an outline to a texture");
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture outline");
 
     Texture2D texture = LoadTexture("resources/fudesumi.png");
 
diff --git a/raylib/examples/shaders/shaders_texture_rendering.c b/raylib/examples/shaders/shaders_texture_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shaders/shaders_texture_rendering.c
@@ -0,0 +1,86 @@
+/*******************************************************************************************
+*
+*   raylib [shaders] example - texture rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 2.0, last time updated with raylib 3.7
+*
+*   Example contributed by Michał Ciesielski (@ciessielski) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Michał Ciesielski (@ciessielski) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture rendering");
+
+    Image imBlank = GenImageColor(1024, 1024, BLANK);
+    Texture2D texture = LoadTextureFromImage(imBlank);  // Load blank texture to fill on shader
+    UnloadImage(imBlank);
+
+    // NOTE: Using GLSL 330 shader version, on OpenGL ES 2.0 use GLSL 100 shader version
+    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/cubes_panning.fs", GLSL_VERSION));
+
+    float time = 0.0f;
+    int timeLoc = GetShaderLocation(shader, "uTime");
+    SetShaderValue(shader, timeLoc, &time, SHADER_UNIFORM_FLOAT);
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    // -------------------------------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        time = (float)GetTime();
+        SetShaderValue(shader, timeLoc, &time, SHADER_UNIFORM_FLOAT);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginShaderMode(shader);    // Enable our custom shader for next shapes/textures drawings
+                DrawTexture(texture, 0, 0, WHITE);  // Drawing BLANK texture, all rendering magic happens on shader
+            EndShaderMode();            // Disable our custom shader, return to default shader
+
+            DrawText("BACKGROUND is PAINTED and ANIMATED on SHADER!", 10, 10, 20, MAROON);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadShader(shader);
+    UnloadTexture(texture);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shaders/shaders_texture_tiling.c b/raylib/examples/shaders/shaders_texture_tiling.c
--- a/raylib/examples/shaders/shaders_texture_tiling.c
+++ b/raylib/examples/shaders/shaders_texture_tiling.c
@@ -2,14 +2,18 @@
 *
 *   raylib [shaders] example - texture tiling
 *
-*   Example demonstrates how to tile a texture on a 3D model using raylib.
+*   Example complexity rating: [★★☆☆] 2/4
 *
+*   Example demonstrates how to tile a texture on a 3D model using raylib
+*
+*   Example originally created with raylib 4.5, last time updated with raylib 4.5
+*
 *   Example contributed by Luis Almeida (@luis605) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Luis Almeida (@luis605)
+*   Copyright (c) 2023-2025 Luis Almeida (@luis605)
 *
 ********************************************************************************************/
 
@@ -44,7 +48,7 @@
     // Load a cube model
     Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f);
     Model model = LoadModelFromMesh(cube);
-    
+
     // Load a texture and assign to cube model
     Texture2D texture = LoadTexture("resources/cubicmap_atlas.png");
     model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture;
@@ -73,17 +77,17 @@
         // Draw
         //----------------------------------------------------------------------------------
         BeginDrawing();
-        
+
             ClearBackground(RAYWHITE);
 
             BeginMode3D(camera);
-            
+
                 BeginShaderMode(shader);
                     DrawModel(model, (Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, WHITE);
                 EndShaderMode();
 
                 DrawGrid(10, 1.0f);
-                
+
             EndMode3D();
 
             DrawText("Use mouse to rotate the camera", 10, 10, 20, DARKGRAY);
@@ -100,6 +104,6 @@
 
     CloseWindow();              // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
-    
+
     return 0;
 }
diff --git a/raylib/examples/shaders/shaders_texture_waves.c b/raylib/examples/shaders/shaders_texture_waves.c
--- a/raylib/examples/shaders/shaders_texture_waves.c
+++ b/raylib/examples/shaders/shaders_texture_waves.c
@@ -1,9 +1,11 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Texture Waves
+*   raylib [shaders] example - texture waves
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: This example requires raylib OpenGL 3.3 or ES2 versions for shaders support,
-*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version.
+*         OpenGL 1.1 does not support shaders, recompile raylib to OpenGL 3.3 version
 *
 *   NOTE: Shaders used in this example are #version 330 (OpenGL 3.3), to test this example
 *         on OpenGL ES 2.0 platforms (Android, Raspberry Pi, HTML5), use #version 100 shaders
@@ -16,7 +18,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Anata (@anatagawa) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Anata (@anatagawa) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/shaders/shaders_vertex_displacement.c b/raylib/examples/shaders/shaders_vertex_displacement.c
--- a/raylib/examples/shaders/shaders_vertex_displacement.c
+++ b/raylib/examples/shaders/shaders_vertex_displacement.c
@@ -1,15 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [shaders] example - Vertex displacement
+*   raylib [shaders] example - vertex displacement
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 4.5
 *
-*   Example contributed by <Alex ZH> (@ZzzhHe) and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Alex ZH (@ZzzhHe) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 <Alex ZH> (@ZzzhHe)
+*   Copyright (c) 2023-2025 Alex ZH (@ZzzhHe)
 *
 ********************************************************************************************/
 
@@ -50,7 +52,7 @@
     Shader shader = LoadShader(
         TextFormat("resources/shaders/glsl%i/vertex_displacement.vs", GLSL_VERSION),
         TextFormat("resources/shaders/glsl%i/vertex_displacement.fs", GLSL_VERSION));
-    
+
     // Load perlin noise texture
     Image perlinNoiseImage = GenImagePerlinNoise(512, 512, 0, 0, 1.0f);
     Texture perlinNoiseMap = LoadTextureFromImage(perlinNoiseImage);
@@ -62,7 +64,7 @@
     rlActiveTextureSlot(1);
     rlEnableTexture(perlinNoiseMap.id);
     rlSetUniformSampler(perlinNoiseMapLoc, 1);
-    
+
     // Create a plane mesh and model
     Mesh planeMesh = GenMeshPlane(50, 50, 50, 50);
     Model planeModel = LoadModelFromMesh(planeMesh);
diff --git a/raylib/examples/shaders/shaders_write_depth.c b/raylib/examples/shaders/shaders_write_depth.c
deleted file mode 100644
--- a/raylib/examples/shaders/shaders_write_depth.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shaders] example - Depth buffer writing
-*
-*   Example originally created with raylib 4.2, last time updated with raylib 4.2
-*
-*   Example contributed by Buğra Alptekin Sarı (@BugraAlptekinSari) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2022-2024 Buğra Alptekin Sarı (@BugraAlptekinSari)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "rlgl.h"
-
-#if defined(PLATFORM_DESKTOP)
-#define GLSL_VERSION            330
-#else   // PLATFORM_ANDROID, PLATFORM_WEB
-#define GLSL_VERSION            100
-#endif
-
-//------------------------------------------------------------------------------------
-// Declare custom functions required for the example
-//------------------------------------------------------------------------------------
-// Load custom render texture, create a writable depth texture buffer
-static RenderTexture2D LoadRenderTextureDepthTex(int width, int height);
-
-// Unload render texture from GPU memory (VRAM)
-static void UnloadRenderTextureDepthTex(RenderTexture2D target);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shaders] example - write depth buffer");
-
-    // The shader inverts the depth buffer by writing into it by `gl_FragDepth = 1 - gl_FragCoord.z;`
-    Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/write_depth.fs", GLSL_VERSION));
-
-    // Use Customized function to create writable depth texture buffer
-    RenderTexture2D target = LoadRenderTextureDepthTex(screenWidth, screenHeight);
-
-    // Define the camera to look into our 3d world
-    Camera camera = {
-        .position = (Vector3){ 2.0f, 2.0f, 3.0f },    // Camera position
-        .target = (Vector3){ 0.0f, 0.5f, 0.0f },      // Camera looking at point
-        .up = (Vector3){ 0.0f, 1.0f, 0.0f },          // Camera up vector (rotation towards target)
-        .fovy = 45.0f,                                // Camera field-of-view Y
-        .projection = CAMERA_PERSPECTIVE              // Camera projection type
-    };
-    
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, CAMERA_ORBITAL);
-        //----------------------------------------------------------------------------------
-        
-        // Draw
-        //----------------------------------------------------------------------------------
-        
-        // Draw into our custom render texture (framebuffer)
-        BeginTextureMode(target);
-            ClearBackground(WHITE);
-            
-            BeginMode3D(camera);
-                BeginShaderMode(shader);
-                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, RED);
-                    DrawCubeV((Vector3){ 0.0f, 0.5f, 1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, PURPLE);
-                    DrawCubeWiresV((Vector3){ 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, DARKGREEN);
-                    DrawCubeV((Vector3) { 0.0f, 0.5f, -1.0f }, (Vector3){ 1.0f, 1.0f, 1.0f }, YELLOW);
-                    DrawGrid(10, 1.0f);
-                EndShaderMode();
-            EndMode3D();
-        EndTextureMode();
-
-        // Draw into screen our custom render texture 
-        BeginDrawing();
-            ClearBackground(RAYWHITE);
-        
-            DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)screenWidth, (float)-screenHeight }, (Vector2) { 0, 0 }, WHITE);
-            DrawFPS(10, 10);
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadRenderTextureDepthTex(target);
-    UnloadShader(shader);
-
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-//------------------------------------------------------------------------------------
-// Define custom functions required for the example
-//------------------------------------------------------------------------------------
-// Load custom render texture, create a writable depth texture buffer
-RenderTexture2D LoadRenderTextureDepthTex(int width, int height)
-{
-    RenderTexture2D target = { 0 };
-
-    target.id = rlLoadFramebuffer(); // Load an empty framebuffer
-
-    if (target.id > 0)
-    {
-        rlEnableFramebuffer(target.id);
-
-        // Create color texture (default to RGBA)
-        target.texture.id = rlLoadTexture(0, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
-        target.texture.width = width;
-        target.texture.height = height;
-        target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
-        target.texture.mipmaps = 1;
-
-        // Create depth texture buffer (instead of raylib default renderbuffer)
-        target.depth.id = rlLoadTextureDepth(width, height, false);
-        target.depth.width = width;
-        target.depth.height = height;
-        target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
-        target.depth.mipmaps = 1;
-
-        // Attach color texture and depth texture to FBO
-        rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
-        rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_TEXTURE2D, 0);
-
-        // Check if fbo is complete with attachments (valid)
-        if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
-
-        rlDisableFramebuffer();
-    }
-    else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
-
-    return target;
-}
-
-// Unload render texture from GPU memory (VRAM)
-void UnloadRenderTextureDepthTex(RenderTexture2D target)
-{
-    if (target.id > 0)
-    {
-        // Color texture attached to FBO is deleted
-        rlUnloadTexture(target.texture.id);
-        rlUnloadTexture(target.depth.id);
-
-        // NOTE: Depth texture is automatically
-        // queried and deleted before deleting framebuffer
-        rlUnloadFramebuffer(target.id);
-    }
-}
diff --git a/raylib/examples/shapes/raygui.h b/raylib/examples/shapes/raygui.h
--- a/raylib/examples/shapes/raygui.h
+++ b/raylib/examples/shapes/raygui.h
@@ -4,7 +4,7 @@
 *
 *   DESCRIPTION:
 *       raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also
-*       available as a standalone library, as long as input and drawing functions are provided.
+*       available as a standalone library, as long as input and drawing functions are provided
 *
 *   FEATURES:
 *       - Immediate-mode gui, minimal retained data
@@ -27,7 +27,7 @@
 *       - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for
 *         font atlas recs and glyphs, freeing that memory is (usually) up to the user,
 *         no unload function is explicitly provided... but note that GuiLoadStyleDefault() unloads
-*         by default any previously loaded font (texture, recs, glyphs).
+*         by default any previously loaded font (texture, recs, glyphs)
 *       - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions
 *
 *   CONTROLS PROVIDED:
@@ -65,7 +65,7 @@
 *       - MessageBox    --> Window, Label, Button
 *       - TextInputBox  --> Window, Label, TextBox, Button
 *
-*     It also provides a set of functions for styling the controls based on its properties (size, color).
+*     It also provides a set of functions for styling the controls based on its properties (size, color)
 *
 *
 *   RAYGUI STYLE (guiStyle):
@@ -77,11 +77,11 @@
 *
 *           static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)];
 *
-*       guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB
+*       guiStyle size is by default: 16*(16 + 8) = 384 int = 384*4 bytes = 1536 bytes = 1.5 KB
 *
 *       Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style
 *       used for all controls, when any of those base values is set, it is automatically populated to all
-*       controls, so, specific control values overwriting generic style should be set after base values.
+*       controls, so, specific control values overwriting generic style should be set after base values
 *
 *       After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those
 *       properties are actually common to all controls and can not be overwritten individually (like BASE ones)
@@ -100,7 +100,7 @@
 *       Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon
 *       requires 8 integers (16*16/32) to be stored in memory.
 *
-*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set.
+*       When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set
 *
 *       The global icons array size is fixed and depends on the number of icons and size:
 *
@@ -112,20 +112,20 @@
 *
 *   RAYGUI LAYOUT:
 *       raygui currently does not provide an auto-layout mechanism like other libraries,
-*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it.
+*       layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it
 *
 *       TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout
 *
 *   CONFIGURATION:
 *       #define RAYGUI_IMPLEMENTATION
-*           Generates the implementation of the library into the included file.
+*           Generates the implementation of the library into the included file
 *           If not defined, the library is in header only mode and can be included in other headers
-*           or source files without problems. But only ONE file should hold the implementation.
+*           or source files without problems. But only ONE file should hold the implementation
 *
 *       #define RAYGUI_STANDALONE
 *           Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined
 *           internally in the library and input management and drawing functions must be provided by
-*           the user (check library implementation for further details).
+*           the user (check library implementation for further details)
 *
 *       #define RAYGUI_NO_ICONS
 *           Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB)
@@ -141,12 +141,17 @@
 *           Draw text bounds rectangles for debug
 *
 *   VERSIONS HISTORY:
-*       4.5-dev (Sep-2024)    Current dev version...
+*       5.0 (xx-Nov-2025) ADDED: Support up to 32 controls (v500)
 *                         ADDED: guiControlExclusiveMode and guiControlExclusiveRec for exclusive modes
 *                         ADDED: GuiValueBoxFloat()
 *                         ADDED: GuiDropdonwBox() properties: DROPDOWN_ARROW_HIDDEN, DROPDOWN_ROLL_UP
 *                         ADDED: GuiListView() property: LIST_ITEMS_BORDER_WIDTH
+*                         ADDED: GuiLoadIconsFromMemory()
 *                         ADDED: Multiple new icons
+*                         REMOVED: GuiSpinner() from controls list, using BUTTON + VALUEBOX properties
+*                         REMOVED: GuiSliderPro(), functionality was redundant
+*                         REVIEWED: Controls using text labels to use LABEL properties
+*                         REVIEWED: Replaced sprintf() by snprintf() for more safety
 *                         REVIEWED: GuiTabBar(), close tab with mouse middle button
 *                         REVIEWED: GuiScrollPanel(), scroll speed proportional to content
 *                         REVIEWED: GuiDropdownBox(), support roll up and hidden arrow
@@ -156,6 +161,8 @@
 *                         REVIEWED: GuiIconText(), increase buffer size and reviewed padding
 *                         REVIEWED: GuiDrawText(), improved wrap mode drawing
 *                         REVIEWED: GuiScrollBar(), minor tweaks
+*                         REVIEWED: GuiProgressBar(), improved borders computing
+*                         REVIEWED: GuiTextBox(), multiple improvements: autocursor and more
 *                         REVIEWED: Functions descriptions, removed wrong return value reference
 *                         REDESIGNED: GuiColorPanel(), improved HSV <-> RGBA convertion
 *
@@ -259,16 +266,16 @@
 *       1.4 (15-Jun-2017) Rewritten all GUI functions (removed useless ones)
 *       1.3 (12-Jun-2017) Complete redesign of style system
 *       1.1 (01-Jun-2017) Complete review of the library
-*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria.
-*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria.
-*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria.
+*       1.0 (07-Jun-2016) Converted to header-only by Ramon Santamaria
+*       0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria
+*       0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria
 *
 *   DEPENDENCIES:
-*       raylib 5.0  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
+*       raylib 5.6-dev  - Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing
 *
 *   STANDALONE MODE:
 *       By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled
-*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs.
+*       with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs
 *
 *       The following functions should be redefined for a custom backend:
 *
@@ -309,7 +316,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -334,7 +341,7 @@
 #define RAYGUI_VERSION_MAJOR 4
 #define RAYGUI_VERSION_MINOR 5
 #define RAYGUI_VERSION_PATCH 0
-#define RAYGUI_VERSION  "4.5-dev"
+#define RAYGUI_VERSION  "5.0-dev"
 
 #if !defined(RAYGUI_STANDALONE)
     #include "raylib.h"
@@ -358,17 +365,6 @@
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-// Allow custom memory allocators
-#ifndef RAYGUI_MALLOC
-    #define RAYGUI_MALLOC(sz)       malloc(sz)
-#endif
-#ifndef RAYGUI_CALLOC
-    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
-#endif
-#ifndef RAYGUI_FREE
-    #define RAYGUI_FREE(p)          free(p)
-#endif
-
 // Simple log system to avoid printf() calls if required
 // NOTE: Avoiding those calls, also avoids const strings memory usage
 #define RAYGUI_SUPPORT_LOG_INFO
@@ -421,14 +417,17 @@
 
     // TODO: Texture2D type is very coupled to raylib, required by Font type
     // It should be redesigned to be provided by user
-    typedef struct Texture2D {
+    typedef struct Texture {
         unsigned int id;        // OpenGL texture id
         int width;              // Texture base width
         int height;             // Texture base height
         int mipmaps;            // Mipmap levels, 1 by default
         int format;             // Data format (PixelFormat type)
-    } Texture2D;
+    } Texture;
 
+    // Texture2D, same as Texture
+    typedef Texture Texture2D;
+
     // Image, pixel data stored in CPU memory (RAM)
     typedef struct Image {
         void *data;             // Image raw data
@@ -527,7 +526,7 @@
     DROPDOWNBOX,
     TEXTBOX,        // Used also for: TEXTBOXMULTI
     VALUEBOX,
-    SPINNER,        // Uses: BUTTON, VALUEBOX
+    CONTROL11,
     LISTVIEW,
     COLORPICKER,
     SCROLLBAR,
@@ -549,12 +548,12 @@
     BORDER_COLOR_DISABLED,      // Control border color in STATE_DISABLED
     BASE_COLOR_DISABLED,        // Control base color in STATE_DISABLED
     TEXT_COLOR_DISABLED,        // Control text color in STATE_DISABLED
-    BORDER_WIDTH,               // Control border size, 0 for no border
+    BORDER_WIDTH = 12,          // Control border size, 0 for no border
     //TEXT_SIZE,                  // Control text size (glyphs max height) -> GLOBAL for all controls
     //TEXT_SPACING,               // Control text spacing between glyphs -> GLOBAL for all controls
-    //TEXT_LINE_SPACING           // Control text spacing between lines -> GLOBAL for all controls
-    TEXT_PADDING,               // Control text padding, not considering border
-    TEXT_ALIGNMENT,             // Control text horizontal alignment inside control text bound (after border and padding)
+    //TEXT_LINE_SPACING,          // Control text spacing between lines -> GLOBAL for all controls
+    TEXT_PADDING = 13,          // Control text padding, not considering border
+    TEXT_ALIGNMENT = 14,        // Control text horizontal alignment inside control text bound (after border and padding)
     //TEXT_WRAP_MODE              // Control text wrap-mode inside text bounds -> GLOBAL for all controls
 } GuiControlProperty;
 
@@ -641,18 +640,22 @@
     TEXT_READONLY = 16,         // TextBox in read-only mode: 0-text editable, 1-text no-editable
 } GuiTextBoxProperty;
 
-// Spinner
+// ValueBox/Spinner
 typedef enum {
-    SPIN_BUTTON_WIDTH = 16,     // Spinner left/right buttons width
-    SPIN_BUTTON_SPACING,        // Spinner buttons separation
-} GuiSpinnerProperty;
+    SPINNER_BUTTON_WIDTH = 16,  // Spinner left/right buttons width
+    SPINNER_BUTTON_SPACING,     // Spinner buttons separation
+} GuiValueBoxProperty;
 
+// Control11
+//typedef enum { } GuiControl11Property;
+
 // ListView
 typedef enum {
     LIST_ITEMS_HEIGHT = 16,     // ListView items height
     LIST_ITEMS_SPACING,         // ListView items separation
     SCROLLBAR_WIDTH,            // ListView scrollbar size (usually width)
     SCROLLBAR_SIDE,             // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
+    LIST_ITEMS_BORDER_NORMAL,   // ListView items border enabled in normal state
     LIST_ITEMS_BORDER_WIDTH     // ListView items border width
 } GuiListViewProperty;
 
@@ -717,6 +720,9 @@
 RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position
 #endif
 
+// Utility functions
+RAYGUIAPI int GuiGetTextWidth(const char *text);                // Get text width considering gui style and icon size (if required)
+
 // Controls
 //----------------------------------------------------------------------------------------------------------
 // Container/separator controls, useful for controls organization
@@ -998,34 +1004,34 @@
     ICON_LAYERS2                  = 225,
     ICON_MLAYERS                  = 226,
     ICON_MAPS                     = 227,
-    ICON_228                      = 228,
-    ICON_229                      = 229,
-    ICON_230                      = 230,
-    ICON_231                      = 231,
-    ICON_232                      = 232,
-    ICON_233                      = 233,
-    ICON_234                      = 234,
-    ICON_235                      = 235,
-    ICON_236                      = 236,
-    ICON_237                      = 237,
-    ICON_238                      = 238,
-    ICON_239                      = 239,
-    ICON_240                      = 240,
-    ICON_241                      = 241,
-    ICON_242                      = 242,
-    ICON_243                      = 243,
-    ICON_244                      = 244,
-    ICON_245                      = 245,
-    ICON_246                      = 246,
-    ICON_247                      = 247,
-    ICON_248                      = 248,
-    ICON_249                      = 249,
+    ICON_HOT                      = 228,
+    ICON_LABEL                    = 229,
+    ICON_NAME_ID                  = 230,
+    ICON_SLICING                  = 231,
+    ICON_MANUAL_CONTROL           = 232,
+    ICON_COLLISION                = 233,
+    ICON_CIRCLE_ADD               = 234,
+    ICON_CIRCLE_ADD_FILL          = 235,
+    ICON_CIRCLE_WARNING           = 236,
+    ICON_CIRCLE_WARNING_FILL      = 237,
+    ICON_BOX_MORE                 = 238,
+    ICON_BOX_MORE_FILL            = 239,
+    ICON_BOX_MINUS                = 240,
+    ICON_BOX_MINUS_FILL           = 241,
+    ICON_UNION                    = 242,
+    ICON_INTERSECTION             = 243,
+    ICON_DIFFERENCE               = 244,
+    ICON_SPHERE                   = 245,
+    ICON_CYLINDER                 = 246,
+    ICON_CONE                     = 247,
+    ICON_ELLIPSOID                = 248,
+    ICON_CAPSULE                  = 249,
     ICON_250                      = 250,
     ICON_251                      = 251,
     ICON_252                      = 252,
     ICON_253                      = 253,
     ICON_254                      = 254,
-    ICON_255                      = 255,
+    ICON_255                      = 255
 } GuiIconName;
 #endif
 
@@ -1045,12 +1051,25 @@
 
 #if defined(RAYGUI_IMPLEMENTATION)
 
-#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
-#include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+#include <ctype.h>              // required for: isspace() [GuiTextBox()]
+#include <stdio.h>              // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), snprintf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()]
 #include <string.h>             // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy()
 #include <stdarg.h>             // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()]
 #include <math.h>               // Required for: roundf() [GuiColorPicker()]
 
+// Allow custom memory allocators
+#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
+    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
+        #error "RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized"
+    #endif
+#else
+    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
+
+    #define RAYGUI_MALLOC(sz)       malloc(sz)
+    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
+    #define RAYGUI_FREE(p)          free(p)
+#endif
+
 #ifdef __cplusplus
     #define RAYGUI_CLITERAL(name) name
 #else
@@ -1059,7 +1078,7 @@
 
 // Check if two rectangles are equal, used to validate a slider bounds as an id
 #ifndef CHECK_BOUNDS_ID
-    #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height))
+    #define CHECK_BOUNDS_ID(src, dst) (((int)src.x == (int)dst.x) && ((int)src.y == (int)dst.y) && ((int)src.width == (int)dst.width) && ((int)src.height == (int)dst.height))
 #endif
 
 #if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS)
@@ -1316,28 +1335,28 @@
     0x07fe0000, 0x1c020402, 0x74021402, 0x54025402, 0x54025402, 0x500857fe, 0x40205ff8, 0x00007fe0,      // ICON_LAYERS2
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x422a422a, 0x422e422a, 0x40384e28, 0x00007fe0,      // ICON_MLAYERS
     0x0ffe0000, 0x3ffa0802, 0x7fea200a, 0x402a402a, 0x5b2a512a, 0x512e552a, 0x40385128, 0x00007fe0,      // ICON_MAPS
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_228
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_229
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_230
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_231
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_232
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_233
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_234
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_235
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_236
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_237
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_238
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_239
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_240
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_241
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_242
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_243
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_244
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_245
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_246
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_247
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_248
-    0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_249
+    0x04200000, 0x1cf00c60, 0x11f019f0, 0x0f3807b8, 0x1e3c0f3c, 0x1c1c1e1c, 0x1e3c1c1c, 0x00000f70,      // ICON_HOT
+    0x00000000, 0x20803f00, 0x2a202e40, 0x20082e10, 0x08021004, 0x02040402, 0x00900108, 0x00000060,      // ICON_LABEL
+    0x00000000, 0x042007e0, 0x47e27c3e, 0x4ffa4002, 0x47fa4002, 0x4ffa4002, 0x7ffe4002, 0x00000000,      // ICON_NAME_ID
+    0x7fe00000, 0x402e4020, 0x43ce5e0a, 0x40504078, 0x438e4078, 0x402e5e0a, 0x7fe04020, 0x00000000,      // ICON_SLICING
+    0x00000000, 0x40027ffe, 0x47c24002, 0x55425d42, 0x55725542, 0x50125552, 0x10105016, 0x00001ff0,      // ICON_MANUAL_CONTROL
+    0x7ffe0000, 0x43c24002, 0x48124422, 0x500a500a, 0x500a500a, 0x44224812, 0x400243c2, 0x00007ffe,      // ICON_COLLISION
+    0x03c00000, 0x10080c30, 0x21842184, 0x4ff24182, 0x41824ff2, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_ADD
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x700e7e7e, 0x7e7e700e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_ADD_FILL
+    0x03c00000, 0x10080c30, 0x21842184, 0x41824182, 0x40024182, 0x21842184, 0x0c301008, 0x000003c0,      // ICON_CIRCLE_WARNING
+    0x03c00000, 0x1ff80ff0, 0x3e7c3e7c, 0x7e7e7e7e, 0x7ffe7e7e, 0x3e7c3e7c, 0x0ff01ff8, 0x000003c0,      // ICON_CIRCLE_WARNING_FILL
+    0x00000000, 0x10041ffc, 0x10841004, 0x13e41084, 0x10841084, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE
+    0x00000000, 0x1ffc1ffc, 0x1f7c1ffc, 0x1c1c1f7c, 0x1f7c1f7c, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MORE_FILL
+    0x00000000, 0x1ffc1ffc, 0x1ffc1ffc, 0x1c1c1ffc, 0x1ffc1ffc, 0x1ffc1ffc, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS
+    0x00000000, 0x10041ffc, 0x10041004, 0x13e41004, 0x10041004, 0x10041004, 0x00001ffc, 0x00000000,      // ICON_BOX_MINUS_FILL
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x55766eba, 0x55766eaa, 0x55606ffe, 0x55606aa0, 0x00007fe0,      // ICON_UNION
+    0x07fe0000, 0x04020402, 0x7fe20402, 0x456246a2, 0x456246a2, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_INTERSECTION
+    0x07fe0000, 0x055606aa, 0x7ff606aa, 0x4436442a, 0x4436442a, 0x402047fe, 0x40204020, 0x00007fe0,      // ICON_DIFFERENCE
+    0x03c00000, 0x10080c30, 0x20042004, 0x60064002, 0x47e2581a, 0x20042004, 0x0c301008, 0x000003c0,      // ICON_SPHERE
+    0x03e00000, 0x08080410, 0x0c180808, 0x08080be8, 0x08080808, 0x08080808, 0x04100808, 0x000003e0,      // ICON_CYLINDER
+    0x00800000, 0x01400140, 0x02200220, 0x04100410, 0x08080808, 0x1c1c13e4, 0x08081004, 0x000007f0,      // ICON_CONE
+    0x00000000, 0x07e00000, 0x20841918, 0x40824082, 0x40824082, 0x19182084, 0x000007e0, 0x00000000,      // ICON_ELLIPSOID
+    0x00000000, 0x00000000, 0x20041ff8, 0x40024002, 0x40024002, 0x1ff82004, 0x00000000, 0x00000000,      // ICON_CAPSULE
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_250
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_251
     0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,      // ICON_252
@@ -1362,7 +1381,7 @@
 #define RAYGUI_MAX_PROPS_EXTENDED        8      // Maximum number of extended properties
 
 //----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
 //----------------------------------------------------------------------------------
 // Gui control property style color element
 typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement;
@@ -1386,8 +1405,7 @@
 
 static int textBoxCursorIndex = 0;              // Cursor index, shared by all GuiTextBox*()
 //static int blinkCursorFrameCounter = 0;       // Frame counter for cursor blinking
-static int autoCursorCooldownCounter = 0;       // Cooldown frame counter for automatic cursor movement on key-down
-static int autoCursorDelayCounter = 0;          // Delay frame counter for automatic cursor movement
+static int autoCursorCounter = 0;               // Frame counter for automatic repeated cursor movement on key-down (cooldown and delay)
 
 //----------------------------------------------------------------------------------
 // Style data array for all gui style properties (allocated on data segment by default)
@@ -1479,11 +1497,10 @@
 #endif      // RAYGUI_STANDALONE
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize);    // Load style from memory (binary only)
 
-static int GetTextWidth(const char *text);                      // Gui get text width using gui font and style
 static Rectangle GetTextBounds(int control, Rectangle bounds);  // Get text bounds considering control bounds
 static const char *GetTextIcon(const char *text, int *iconId);  // Get text icon if provided and move text cursor
 
@@ -1588,6 +1605,10 @@
         #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT        24
     #endif
 
+    #if !defined(RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT)
+        #define RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT      18
+    #endif
+
     int result = 0;
     //GuiState state = guiState;
 
@@ -1596,9 +1617,10 @@
     Rectangle statusBar = { bounds.x, bounds.y, bounds.width, (float)statusBarHeight };
     if (bounds.height < statusBarHeight*2.0f) bounds.height = statusBarHeight*2.0f;
 
+    const float vPadding = statusBarHeight/2.0f - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT/2.0f;
     Rectangle windowPanel = { bounds.x, bounds.y + (float)statusBarHeight - 1, bounds.width, bounds.height - (float)statusBarHeight + 1 };
-    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - 20,
-                                 statusBar.y + statusBarHeight/2.0f - 18.0f/2.0f, 18, 18 };
+    Rectangle closeButtonRec = { statusBar.x + statusBar.width - GuiGetStyle(STATUSBAR, BORDER_WIDTH) - RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT - vPadding,
+                                 statusBar.y + vPadding, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT, RAYGUI_WINDOWBOX_CLOSEBUTTON_HEIGHT };
 
     // Update control
     //--------------------------------------------------------------------
@@ -1639,9 +1661,9 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)));
-    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)));
-    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
+    GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)));
 
     GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text);
     //--------------------------------------------------------------------
@@ -1652,7 +1674,7 @@
 // Line control
 int GuiLine(Rectangle bounds, const char *text)
 {
-    #if !defined(RAYGUI_LINE_ORIGIN_SIZE)
+    #if !defined(RAYGUI_LINE_MARGIN_TEXT)
         #define RAYGUI_LINE_MARGIN_TEXT  12
     #endif
     #if !defined(RAYGUI_LINE_TEXT_PADDING)
@@ -1662,7 +1684,7 @@
     int result = 0;
     GuiState state = guiState;
 
-    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR));
+    Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR));
 
     // Draw control
     //--------------------------------------------------------------------
@@ -1670,7 +1692,7 @@
     else
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = bounds.height;
         textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT;
         textBounds.y = bounds.y;
@@ -1710,8 +1732,8 @@
     //--------------------------------------------------------------------
     if (text != NULL) GuiStatusBar(statusBar, text);  // Draw panel header as status bar
 
-    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)),
-                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)));
+    GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BORDER_COLOR_DISABLED : (int)LINE_COLOR)),
+                     GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? (int)BASE_COLOR_DISABLED : (int)BACKGROUND_COLOR)));
     //--------------------------------------------------------------------
 
     return result;
@@ -1721,7 +1743,7 @@
 // NOTE: Using GuiToggle() for the TABS
 int GuiTabBar(Rectangle bounds, const char **text, int count, int *active)
 {
-    #define RAYGUI_TABBAR_ITEM_WIDTH    160
+    #define RAYGUI_TABBAR_ITEM_WIDTH    148
 
     int result = -1;
     //GuiState state = guiState;
@@ -1754,12 +1776,12 @@
             if (i == (*active))
             {
                 toggle = true;
-                GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+                GuiToggle(tabBounds, text[i], &toggle);
             }
             else
             {
                 toggle = false;
-                GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle);
+                GuiToggle(tabBounds, text[i], &toggle);
                 if (toggle) *active = i;
             }
 
@@ -2010,7 +2032,7 @@
     bool pressed = false;
 
     // NOTE: We force bounds.width to be all text
-    float textWidth = (float)GetTextWidth(text);
+    float textWidth = (float)GuiGetTextWidth(text);
     if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2;
 
     // Update control
@@ -2148,8 +2170,10 @@
 
     // Get substrings items from text (items pointers)
     int itemCount = 0;
-    const char **items = GuiTextSplit(text, ';', &itemCount, NULL);
+    const char **items = NULL;
 
+    if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL);
+
     Rectangle slider = {
         0,      // Calculated later depending on the active toggle
         bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
@@ -2195,7 +2219,7 @@
     if (text != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(text);
+        textBounds.width = (float)GuiGetTextWidth(text);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = slider.x + slider.width/2 - textBounds.width/2;
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2220,7 +2244,7 @@
 
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2473,7 +2497,7 @@
 int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode)
 {
     #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)
-        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  40        // Frames to wait for autocursor movement
+        #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN  20        // Frames to wait for autocursor movement
     #endif
     #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY)
         #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY      1        // Frames delay for autocursor movement
@@ -2486,10 +2510,10 @@
     int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE);
 
     Rectangle textBounds = GetTextBounds(TEXTBOX, bounds);
-    int textLength = (int)strlen(text);     // Get current text length
+    int textLength = (text != NULL)? (int)strlen(text) : 0; // Get current text length
     int thisCursorIndex = textBoxCursorIndex;
     if (thisCursorIndex > textLength) thisCursorIndex = textLength;
-    int textWidth = GetTextWidth(text) - GetTextWidth(text + thisCursorIndex);
+    int textWidth = GuiGetTextWidth(text) - GuiGetTextWidth(text + thisCursorIndex);
     int textIndexOffset = 0;    // Text index offset to start drawing in the box
 
     // Cursor rectangle
@@ -2510,15 +2534,6 @@
     mouseCursor.x = -1;
     mouseCursor.width = 1;
 
-    // Auto-cursor movement logic
-    // NOTE: Cursor moves automatically when key down after some time
-    if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++;
-    else
-    {
-        autoCursorCooldownCounter = 0;      // GLOBAL: Cursor cooldown counter
-        autoCursorDelayCounter = 0;         // GLOBAL: Cursor delay counter
-    }
-
     // Blink-cursor frame counter
     //if (!autoCursorMode) blinkCursorFrameCounter++;
     //else blinkCursorFrameCounter = 0;
@@ -2536,6 +2551,13 @@
 
         if (editMode)
         {
+            // GLOBAL: Auto-cursor movement logic
+            // NOTE: Keystrokes are handled repeatedly when button is held down for some time
+            if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCounter++;
+            else autoCursorCounter = 0;
+
+            bool autoCursorShouldTrigger = (autoCursorCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) && ((autoCursorCounter % RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0);
+
             state = STATE_PRESSED;
 
             if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
@@ -2549,7 +2571,7 @@
 
                 textIndexOffset += nextCodepointSize;
 
-                textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex);
+                textWidth = GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex);
             }
 
             int codepoint = GetCharPressed();       // Get Unicode codepoint
@@ -2559,10 +2581,43 @@
             int codepointSize = 0;
             const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize);
 
-            // Add codepoint to text, at current cursor position
-            // NOTE: Make sure we do not overflow buffer size
-            if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            // Handle text paste action
+            if (IsKeyPressed(KEY_V) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
+                const char *pasteText = GetClipboardText();
+                if (pasteText != NULL)
+                {
+                    int pasteLength = 0;
+                    int pasteCodepoint;
+                    int pasteCodepointSize;
+
+                    // Count how many codepoints to copy, stopping at the first unwanted control character
+                    while (true)
+                    {
+                        pasteCodepoint = GetCodepointNext(pasteText + pasteLength, &pasteCodepointSize);
+                        if (textLength + pasteLength + pasteCodepointSize >= textSize) break;
+                        if (!(multiline && (pasteCodepoint == (int)'\n')) && !(pasteCodepoint >= 32)) break;
+                        pasteLength += pasteCodepointSize;
+                    }
+
+                    if (pasteLength > 0)
+                    {
+                        // Move forward data from cursor position
+                        for (int i = textLength + pasteLength; i > textBoxCursorIndex; i--) text[i] = text[i - pasteLength];
+
+                        // Paste data in at cursor
+                        for (int i = 0; i < pasteLength; i++) text[textBoxCursorIndex + i] = pasteText[i];
+
+                        textBoxCursorIndex += pasteLength;
+                        textLength += pasteLength;
+                        text[textLength] = '\0';
+                    }
+                }
+            }
+            else if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < textSize))
+            {
+                // Adding codepoint to text, at current cursor position
+
                 // Move forward data from cursor position
                 for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize];
 
@@ -2582,80 +2637,187 @@
             // Move cursor to end
             if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength;
 
-            // Delete codepoint from text, after current cursor position
-            if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+            // Delete related codepoints from text, after current cursor position
+            if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_DELETE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                autoCursorDelayCounter++;
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
 
-                if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
                 {
-                    int nextCodepointSize = 0;
-                    GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
-
-                    // Move backward text from cursor position
-                    for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize];
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff))))
+                        break;
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
 
-                    textLength -= codepointSize;
-                    if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength;
+                // Check whitespace to delete (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
 
-                    // Make sure text last character is EOL
-                    text[textLength] = '\0';
+                    offset += nextCodepointSize;
+                    accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
                 }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = offset; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
             }
 
-            // Delete codepoint from text, before current cursor position
-            if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))))
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && autoCursorShouldTrigger)))
             {
-                autoCursorDelayCounter++;
+                // Delete single codepoint from text, after current cursor position
 
-                if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
-                {
-                    int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
 
-                    // Move backward text from cursor position
-                    for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize];
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex + nextCodepointSize; i <= textLength; i++) text[i - nextCodepointSize] = text[i];
 
-                    // TODO Check: >= cursor+codepointsize and <= length-codepointsize
+                textLength -= nextCodepointSize;
+            }
 
-                    // Prevent cursor index from decrementing past 0
-                    if (textBoxCursorIndex > 0)
-                    {
-                        textBoxCursorIndex -= codepointSize;
-                        textLength -= codepointSize;
-                    }
+            // Delete related codepoints from text, before current cursor position
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_BACKSPACE) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
 
-                    // Make sure text last character is EOL
-                    text[textLength] = '\0';
+                // Check whitespace to delete (ASCII only)
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
                 }
+
+                // Check characters of the same type to delete (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    accCodepointSize += prevCodepointSize;
+                }
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - accCodepointSize] = text[i];
+
+                textLength -= accCodepointSize;
+                textBoxCursorIndex -= accCodepointSize;
             }
 
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && autoCursorShouldTrigger)))
+            {
+                // Delete single codepoint from text, before current cursor position
+
+                int prevCodepointSize = 0;
+
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+
+                // Move text after cursor forward (including final null terminator)
+                for (int i = textBoxCursorIndex; i <= textLength; i++) text[i - prevCodepointSize] = text[i];
+
+                textLength -= prevCodepointSize;
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+
             // Move cursor position with keys
-            if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+            if ((textBoxCursorIndex > 0) && IsKeyPressed(KEY_LEFT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
             {
-                autoCursorDelayCounter++;
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int prevCodepointSize;
+                int prevCodepoint;
 
-                if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                // Check whitespace to skip (ASCII only)
+                while (offset > 0)
                 {
-                    int prevCodepointSize = 0;
-                    GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if (!isspace(prevCodepoint & 0xff)) break;
 
-                    if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize;
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
                 }
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                bool puctuation = ispunct(prevCodepoint & 0xff);
+                while (offset > 0)
+                {
+                    prevCodepoint = GetCodepointPrevious(text + offset, &prevCodepointSize);
+                    if ((puctuation && !ispunct(prevCodepoint & 0xff)) || (!puctuation && (isspace(prevCodepoint & 0xff) || ispunct(prevCodepoint & 0xff)))) break;
+
+                    offset -= prevCodepointSize;
+                    //accCodepointSize += prevCodepointSize;
+                }
+
+                textBoxCursorIndex = offset;
             }
-            else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))
+            else if ((textBoxCursorIndex > 0) && (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && autoCursorShouldTrigger)))
             {
-                autoCursorDelayCounter++;
+                int prevCodepointSize = 0;
+                GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize);
 
-                if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0)      // Delay every movement some frames
+                textBoxCursorIndex -= prevCodepointSize;
+            }
+            else if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_RIGHT) && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL)))
+            {
+                int offset = textBoxCursorIndex;
+                //int accCodepointSize = 0;
+                int nextCodepointSize;
+                int nextCodepoint;
+
+                // Check characters of the same type to skip (either ASCII punctuation or anything non-whitespace)
+                // Not using isalnum() since it only works on ASCII characters
+                nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                bool puctuation = ispunct(nextCodepoint & 0xff);
+                while (offset < textLength)
                 {
-                    int nextCodepointSize = 0;
-                    GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
+                    if ((puctuation && !ispunct(nextCodepoint & 0xff)) || (!puctuation && (isspace(nextCodepoint & 0xff) || ispunct(nextCodepoint & 0xff)))) break;
 
-                    if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize;
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
                 }
+
+                // Check whitespace to skip (ASCII only)
+                while (offset < textLength)
+                {
+                    if (!isspace(nextCodepoint & 0xff)) break;
+
+                    offset += nextCodepointSize;
+                    //accCodepointSize += nextCodepointSize;
+                    nextCodepoint = GetCodepointNext(text + offset, &nextCodepointSize);
+                }
+
+                textBoxCursorIndex = offset;
             }
+            else if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && autoCursorShouldTrigger)))
+            {
+                int nextCodepointSize = 0;
+                GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize);
 
+                textBoxCursorIndex += nextCodepointSize;
+            }
+
             // Move cursor position with mouse
             if (CheckCollisionPointRec(mousePosition, textBounds))     // Mouse hover text
             {
@@ -2665,7 +2827,7 @@
                 float widthToMouseX = 0;
                 int mouseCursorIndex = 0;
 
-                for (int i = textIndexOffset; i < textLength; i++)
+                for (int i = textIndexOffset; i < textLength; i += codepointSize)
                 {
                     codepoint = GetCodepointNext(&text[i], &codepointSize);
                     codepointIndex = GetGlyphIndex(guiFont, codepoint);
@@ -2684,7 +2846,7 @@
                 }
 
                 // Check if mouse cursor is at the last position
-                int textEndWidth = GetTextWidth(text + textIndexOffset);
+                int textEndWidth = GuiGetTextWidth(text + textIndexOffset);
                 if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2))
                 {
                     mouseCursor.x = textBounds.x + textEndWidth;
@@ -2701,7 +2863,7 @@
             else mouseCursor.x = -1;
 
             // Recalculate cursor position.y depending on textBoxCursorIndex
-            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
+            cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GuiGetTextWidth(text + textIndexOffset) - GuiGetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING);
             //if (multiline) cursor.y = GetTextLines()
 
             // Finish text editing on ENTER or mouse click outside bounds
@@ -2709,6 +2871,7 @@
                 (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)))
             {
                 textBoxCursorIndex = 0;     // GLOBAL: Reset the shared cursor index
+                autoCursorCounter = 0;      // GLOBAL: Reset counter for repeated keystrokes
                 result = 1;
             }
         }
@@ -2721,6 +2884,7 @@
                 if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
                 {
                     textBoxCursorIndex = textLength;   // GLOBAL: Place cursor index to the end of current text
+                    autoCursorCounter = 0;             // GLOBAL: Reset counter for repeated keystrokes
                     result = 1;
                 }
             }
@@ -2789,19 +2953,22 @@
 
     int tempValue = *value;
 
-    Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y,
-                          bounds.width - 2*(GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING)), bounds.height };
-    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
-    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.y, (float)GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle valueBoxBounds = {
+        bounds.x + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING),
+        bounds.y,
+        bounds.width - 2*(GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH) + GuiGetStyle(VALUEBOX, SPINNER_BUTTON_SPACING)), bounds.height };
+    Rectangle leftButtonBound = { (float)bounds.x, (float)bounds.y, (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
+    Rectangle rightButtonBound = { (float)bounds.x + bounds.width - GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.y,
+        (float)GuiGetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH), (float)bounds.height };
 
     Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-        textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING);
+        textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
-        if (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SPINNER, TEXT_PADDING);
+        if (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_LEFT) textBounds.x = bounds.x - textBounds.width - GuiGetStyle(VALUEBOX, TEXT_PADDING);
     }
 
     // Update control
@@ -2835,20 +3002,20 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode);
+    result = GuiValueBox(valueBoxBounds, NULL, &tempValue, minValue, maxValue, editMode);
 
     // Draw value selector custom buttons
     // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values
     int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH);
     int tempTextAlign = GuiGetStyle(BUTTON, TEXT_ALIGNMENT);
-    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(SPINNER, BORDER_WIDTH));
+    GuiSetStyle(BUTTON, BORDER_WIDTH, GuiGetStyle(VALUEBOX, BORDER_WIDTH));
     GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
 
     GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign);
     GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth);
 
     // Draw text label if provided
-    GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
+    GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     //--------------------------------------------------------------------
 
     *value = tempValue;
@@ -2866,13 +3033,13 @@
     int result = 0;
     GuiState state = guiState;
 
-    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
-    sprintf(textValue, "%i", *value);
+    char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = { 0 };
+    snprintf(textValue, RAYGUI_VALUEBOX_MAX_CHARS + 1, "%i", *value);
 
     Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -2884,7 +3051,6 @@
     if ((state != STATE_DISABLED) && !guiLocked && !guiControlExclusiveMode)
     {
         Vector2 mousePoint = GetMousePosition();
-
         bool valueHasChanged = false;
 
         if (editMode)
@@ -2893,32 +3059,55 @@
 
             int keyCount = (int)strlen(textValue);
 
-            // Only allow keys in range [48..57]
-            if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
             {
-                if (GetTextWidth(textValue) < bounds.width)
+                if (textValue[0] == '-')
                 {
-                    int key = GetCharPressed();
-                    if ((key >= 48) && (key <= 57))
+                    for (int i = 0 ; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
+                {
+                    if (keyCount == 0)
                     {
-                        textValue[keyCount] = (char)key;
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
                         keyCount++;
-                        valueHasChanged = true;
                     }
+
+                    for (int i = keyCount ; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
                 }
             }
 
-            // Delete text
-            if (keyCount > 0)
+            // Add new digit to text value
+            if ((keyCount >= 0) && (keyCount < RAYGUI_VALUEBOX_MAX_CHARS) && (GuiGetTextWidth(textValue) < bounds.width))
             {
-                if (IsKeyPressed(KEY_BACKSPACE))
+                int key = GetCharPressed();
+                
+                // Only allow keys in range [48..57]
+                if ((key >= 48) && (key <= 57))
                 {
-                    keyCount--;
-                    textValue[keyCount] = '\0';
+                    textValue[keyCount] = (char)key;
+                    keyCount++;
                     valueHasChanged = true;
                 }
             }
 
+            // Delete text
+            if ((keyCount > 0) && IsKeyPressed(KEY_BACKSPACE))
+            {
+                keyCount--;
+                textValue[keyCount] = '\0';
+                valueHasChanged = true;
+            }
+
             if (valueHasChanged) *value = TextToInteger(textValue);
 
             // NOTE: We are not clamp values until user input finishes
@@ -2956,11 +3145,14 @@
     GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor);
     GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))));
 
-    // Draw cursor
+    // Draw cursor rectangle
     if (editMode)
     {
         // NOTE: ValueBox internal text is always centered
-        Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) };
+        Rectangle cursor = { bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
+            bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + 2,
+            2, bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2 - 4 };
+        if (cursor.height > bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2;
         GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
     }
 
@@ -2983,12 +3175,12 @@
     GuiState state = guiState;
 
     //char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0";
-    //sprintf(textValue, "%2.2f", *value);
+    //snprintf(textValue, sizeof(textValue), "%2.2f", *value);
 
-    Rectangle textBounds = {0};
+    Rectangle textBounds = { 0 };
     if (text != NULL)
     {
-        textBounds.width = (float)GetTextWidth(text) + 2;
+        textBounds.width = (float)GuiGetTextWidth(text) + 2;
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -3009,10 +3201,37 @@
 
             int keyCount = (int)strlen(textValue);
 
+            // Add or remove minus symbol
+            if (IsKeyPressed(KEY_MINUS))
+            {
+                if (textValue[0] == '-')
+                {
+                    for (int i = 0; i < keyCount; i++) textValue[i] = textValue[i + 1];
+
+                    keyCount--;
+                    valueHasChanged = true;
+                }
+                else if (keyCount < (RAYGUI_VALUEBOX_MAX_CHARS - 1))
+                {
+                    if (keyCount == 0)
+                    {
+                        textValue[0] = '0';
+                        textValue[1] = '\0';
+                        keyCount++;
+                    }
+
+                    for (int i = keyCount; i > -1; i--) textValue[i + 1] = textValue[i];
+
+                    textValue[0] = '-';
+                    keyCount++;
+                    valueHasChanged = true;
+                }
+            }
+
             // Only allow keys in range [48..57]
             if (keyCount < RAYGUI_VALUEBOX_MAX_CHARS)
             {
-                if (GetTextWidth(textValue) < bounds.width)
+                if (GuiGetTextWidth(textValue) < bounds.width)
                 {
                     int key = GetCharPressed();
                     if (((key >= 48) && (key <= 57)) ||
@@ -3067,7 +3286,7 @@
     if (editMode)
     {
         // NOTE: ValueBox internal text is always centered
-        Rectangle cursor = {bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1,
+        Rectangle cursor = {bounds.x + GuiGetTextWidth(textValue)/2 + bounds.width/2 + 1,
                             bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4,
                             bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH)};
         GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)));
@@ -3084,7 +3303,7 @@
 
 // Slider control with pro parameters
 // NOTE: Other GuiSlider*() controls use this one
-int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth)
+int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
 {
     int result = 0;
     GuiState state = guiState;
@@ -3093,6 +3312,8 @@
     if (value == NULL) value = &temp;
     float oldValue = *value;
 
+    int sliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+
     Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING),
                          0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) };
 
@@ -3110,7 +3331,7 @@
                 {
                     state = STATE_PRESSED;
                     // Get equivalent value and slider position from mousePosition.x
-                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
                 }
             }
             else
@@ -3130,7 +3351,7 @@
                 if (!CheckCollisionPointRec(mousePoint, slider))
                 {
                     // Get equivalent value and slider position from mousePosition.x
-                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width-sliderWidth)) + minValue;
+                    *value = (maxValue - minValue)*((mousePoint.x - bounds.x - sliderWidth/2)/(bounds.width - sliderWidth)) + minValue;
                 }
             }
             else state = STATE_FOCUSED;
@@ -3169,44 +3390,45 @@
     if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)));
     else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)));
     else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED)));
+    else if (state == STATE_DISABLED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_DISABLED)));
 
     // Draw left/right text if provided
     if (textLeft != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textLeft);
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
 
     if (textRight != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textRight);
+        textBounds.width = (float)GuiGetTextWidth(textRight);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))));
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
     //--------------------------------------------------------------------
 
     return result;
 }
 
-// Slider control extended, returns selected value and has text
-int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
-{
-    return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH));
-}
-
 // Slider Bar control extended, returns selected value
 int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue)
 {
-    return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0);
+    int result = 0;
+    int preSliderWidth = GuiGetStyle(SLIDER, SLIDER_WIDTH);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, 0);
+    result = GuiSlider(bounds, textLeft, textRight, value, minValue, maxValue);
+    GuiSetStyle(SLIDER, SLIDER_WIDTH, preSliderWidth);
+
+    return result;
 }
 
 // Progress Bar control extended, shows current progress value
@@ -3221,14 +3443,14 @@
     // Progress bar
     Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH),
                            bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0,
-                           bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) };
+                           bounds.height - GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) -1 };
 
     // Update control
     //--------------------------------------------------------------------
     if (*value > maxValue) *value = maxValue;
 
     // WARNING: Working with floats could lead to rounding issues
-    if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue)? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f);
+    if ((state != STATE_DISABLED)) progress.width = ((float)*value/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH));
     //--------------------------------------------------------------------
 
     // Draw control
@@ -3246,15 +3468,15 @@
             GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
             GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
         }
-        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+        else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
 
-        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
+        if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1}, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED)));
         else
         {
             // Draw borders not yet reached by value
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
-            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + bounds.height - 1, bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
+            GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height+GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)-1 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL)));
         }
 
         // Draw slider internal progress bar (depends on state)
@@ -3265,23 +3487,23 @@
     if (textLeft != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textLeft);
+        textBounds.width = (float)GuiGetTextWidth(textLeft);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+        GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
 
     if (textRight != NULL)
     {
         Rectangle textBounds = { 0 };
-        textBounds.width = (float)GetTextWidth(textRight);
+        textBounds.width = (float)GuiGetTextWidth(textRight);
         textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
         textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING);
         textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
 
-        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))));
+        GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3))));
     }
     //--------------------------------------------------------------------
 
@@ -3426,16 +3648,16 @@
 
     // Draw control
     //--------------------------------------------------------------------
-    GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
+    GuiDrawRectangle(bounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)));     // Draw background
 
     // Draw visible items
     for (int i = 0; ((i < visibleItems) && (text != NULL)); i++)
     {
-        GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
+        if (GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_NORMAL)) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_NORMAL)), BLANK);
 
         if (state == STATE_DISABLED)
         {
-            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
+            if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)));
 
             GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)));
         }
@@ -3444,18 +3666,18 @@
             if (((startIndex + i) == itemSelected) && (active != NULL))
             {
                 // Draw item selected
-                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)));
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)));
             }
             else if (((startIndex + i) == itemFocused)) // && (focus != NULL))  // NOTE: We want items focused, despite not returned!
             {
                 // Draw item focused
-                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
+                GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)));
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)));
             }
             else
             {
-                // Draw item normal
+                // Draw item normal (no rectangle)
                 GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)));
             }
         }
@@ -3495,22 +3717,22 @@
     return result;
 }
 
-// Color Panel control - Color (RGBA) variant.
+// Color Panel control - Color (RGBA) variant
 int GuiColorPanel(Rectangle bounds, const char *text, Color *color)
 {
     int result = 0;
 
     Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f };
     Vector3 hsv = ConvertRGBtoHSV(vcolor);
-    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv.
+    Vector3 prevHsv = hsv; // workaround to see if GuiColorPanelHSV modifies the hsv
 
     GuiColorPanelHSV(bounds, text, &hsv);
 
-    // Check if the hsv was changed, only then change the color.
-    // This is required, because the Color->HSV->Color conversion has precision errors.
-    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value.
-    // Otherwise GuiColorPanel would often modify it's color without user input.
-    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check.
+    // Check if the hsv was changed, only then change the color
+    // This is required, because the Color->HSV->Color conversion has precision errors
+    // Thus the assignment from HSV to Color should only be made, if the HSV has a new user-entered value
+    // Otherwise GuiColorPanel would often modify it's color without user input
+    // TODO: GuiColorPanelHSV could return 1 if the slider was dragged, to simplify this check
     if (hsv.x != prevHsv.x || hsv.y != prevHsv.y || hsv.z != prevHsv.z)
     {
         Vector3 rgb = ConvertHSVtoRGB(hsv);
@@ -3534,7 +3756,10 @@
 
     int result = 0;
     GuiState state = guiState;
-    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
+    Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2,
+        (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW),
+        (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT),
+        (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 };
 
     // Update control
     //--------------------------------------------------------------------
@@ -3581,7 +3806,6 @@
 
     // Draw control
     //--------------------------------------------------------------------
-
     // Draw alpha bar: checked background
     if (state != STATE_DISABLED)
     {
@@ -3719,7 +3943,7 @@
     Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height };
     //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) };
 
-    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used.
+    // NOTE: this conversion can cause low hue-resolution, if the r, g and b value are very similar, which causes the hue bar to shift around when only the GuiColorPanel is used
     Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f });
 
     GuiColorBarHue(boundsHue, NULL, &hsv.x);
@@ -3732,8 +3956,8 @@
     return result;
 }
 
-// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering.
-// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB.
+// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering
+// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB
 // NOTE: It's divided in multiple controls:
 //      int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv)
 //      int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha)
@@ -3881,7 +4105,7 @@
     buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount;
     buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT;
 
-    int textWidth = GetTextWidth(message) + 2;
+    //int textWidth = GuiGetTextWidth(message) + 2;
 
     Rectangle textBounds = { 0 };
     textBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING;
@@ -3945,7 +4169,7 @@
     Rectangle textBounds = { 0 };
     if (message != NULL)
     {
-        int textSize = GetTextWidth(message) + 2;
+        int textSize = GuiGetTextWidth(message) + 2;
 
         textBounds.x = bounds.x + bounds.width/2 - textSize/2;
         textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2;
@@ -4185,7 +4409,7 @@
 
             if (fileDataSize > 0)
             {
-                unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char));
+                unsigned char *fileData = (unsigned char *)RAYGUI_CALLOC(fileDataSize, sizeof(unsigned char));
                 fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile);
 
                 GuiLoadStyleFromMemory(fileData, fileDataSize);
@@ -4247,8 +4471,6 @@
     GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
     GuiSetStyle(VALUEBOX, TEXT_PADDING, 0);
     GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
-    GuiSetStyle(SPINNER, TEXT_PADDING, 0);
-    GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
     GuiSetStyle(STATUSBAR, TEXT_PADDING, 8);
     GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT);
 
@@ -4263,8 +4485,8 @@
     GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2);
     GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16);
     GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2);
-    GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24);
-    GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_WIDTH, 24);
+    GuiSetStyle(VALUEBOX, SPINNER_BUTTON_SPACING, 2);
     GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0);
     GuiSetStyle(SCROLLBAR, ARROWS_VISIBLE, 0);
     GuiSetStyle(SCROLLBAR, ARROWS_SIZE, 6);
@@ -4274,6 +4496,7 @@
     GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12);
     GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28);
     GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2);
+    GuiSetStyle(LISTVIEW, LIST_ITEMS_BORDER_WIDTH, 1);
     GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12);
     GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE);
     GuiSetStyle(COLORPICKER, COLOR_SELECTOR_SIZE, 8);
@@ -4286,8 +4509,8 @@
     {
         // Unload previous font texture
         UnloadTexture(guiFont.texture);
-        RL_FREE(guiFont.recs);
-        RL_FREE(guiFont.glyphs);
+        RAYGUI_FREE(guiFont.recs);
+        RAYGUI_FREE(guiFont.glyphs);
         guiFont.recs = NULL;
         guiFont.glyphs = NULL;
 
@@ -4316,7 +4539,7 @@
     if (text != NULL)
     {
         memset(buffer, 0, 1024);
-        sprintf(buffer, "#%03i#", iconId);
+        snprintf(buffer, 1024, "#%03i#", iconId);
 
         for (int i = 5; i < 1024; i++)
         {
@@ -4328,7 +4551,7 @@
     }
     else
     {
-        sprintf(iconBuffer, "#%03i#", iconId);
+        snprintf(iconBuffer, 16, "#%03i#", iconId);
 
         return iconBuffer;
     }
@@ -4394,17 +4617,17 @@
         {
             if (loadIconsName)
             {
-                guiIconsName = (char **)RAYGUI_MALLOC(iconCount*sizeof(char **));
+                guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
                 for (int i = 0; i < iconCount; i++)
                 {
-                    guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH);
+                    guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
                     fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile);
                 }
             }
             else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR);
 
             // Read icons data directly over internal icons array
-            fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile);
+            fread(guiIconsPtr, sizeof(unsigned int), (int)iconCount*((int)iconSize*(int)iconSize/32), rgiFile);
         }
 
         fclose(rgiFile);
@@ -4413,6 +4636,56 @@
     return guiIconsName;
 }
 
+// Load icons from memory
+// WARNING: Binary files only
+char **GuiLoadIconsFromMemory(const unsigned char *fileData, int dataSize, bool loadIconsName)
+{
+    unsigned char *fileDataPtr = (unsigned char *)fileData;
+    char **guiIconsName = NULL;
+
+    char signature[5] = { 0 };
+    short version = 0;
+    short reserved = 0;
+    short iconCount = 0;
+    short iconSize = 0;
+
+    memcpy(signature, fileDataPtr, 4);
+    memcpy(&version, fileDataPtr + 4, sizeof(short));
+    memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short));
+    memcpy(&iconCount, fileDataPtr + 4 + 2 + 2, sizeof(short));
+    memcpy(&iconSize, fileDataPtr + 4 + 2 + 2 + 2, sizeof(short));
+    fileDataPtr += 12;
+
+    if ((signature[0] == 'r') &&
+        (signature[1] == 'G') &&
+        (signature[2] == 'I') &&
+        (signature[3] == ' '))
+    {
+        if (loadIconsName)
+        {
+            guiIconsName = (char **)RAYGUI_CALLOC(iconCount, sizeof(char *));
+            for (int i = 0; i < iconCount; i++)
+            {
+                guiIconsName[i] = (char *)RAYGUI_CALLOC(RAYGUI_ICON_MAX_NAME_LENGTH, sizeof(char));
+                memcpy(guiIconsName[i], fileDataPtr, RAYGUI_ICON_MAX_NAME_LENGTH);
+                fileDataPtr += RAYGUI_ICON_MAX_NAME_LENGTH;
+            }
+        }
+        else
+        {
+            // Skip icon name data if not required
+            fileDataPtr += iconCount*RAYGUI_ICON_MAX_NAME_LENGTH;
+        }
+
+        int iconDataSize = iconCount*((int)iconSize*(int)iconSize/32)*(int)sizeof(unsigned int);
+        guiIconsPtr = (unsigned int *)RAYGUI_CALLOC(iconDataSize, 1);
+
+        memcpy(guiIconsPtr, fileDataPtr, iconDataSize);
+    }
+
+    return guiIconsName;
+}
+
 // Draw selected icon using rectangles pixel-by-pixel
 void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color)
 {
@@ -4440,12 +4713,73 @@
     if (scale >= 1) guiIconScale = scale;
 }
 
+// Get text width considering gui style and icon size (if required)
+int GuiGetTextWidth(const char *text)
+{
+    #if !defined(ICON_TEXT_PADDING)
+        #define ICON_TEXT_PADDING   4
+    #endif
+
+    Vector2 textSize = { 0 };
+    int textIconOffset = 0;
+
+    if ((text != NULL) && (text[0] != '\0'))
+    {
+        if (text[0] == '#')
+        {
+            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
+            {
+                if (text[i] == '#')
+                {
+                    textIconOffset = i;
+                    break;
+                }
+            }
+        }
+
+        text += textIconOffset;
+
+        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
+        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
+
+        // Custom MeasureText() implementation
+        if ((guiFont.texture.id > 0) && (text != NULL))
+        {
+            // Get size in bytes of text, considering end of line and line break
+            int size = 0;
+            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
+            {
+                if ((text[i] != '\0') && (text[i] != '\n')) size++;
+                else break;
+            }
+
+            float scaleFactor = fontSize/(float)guiFont.baseSize;
+            textSize.y = (float)guiFont.baseSize*scaleFactor;
+            float glyphWidth = 0.0f;
+
+            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
+            {
+                int codepoint = GetCodepointNext(&text[i], &codepointSize);
+                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
+
+                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
+                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
+
+                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
+            }
+        }
+
+        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
+    }
+
+    return (int)textSize.x;
+}
+
 #endif      // !RAYGUI_NO_ICONS
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
-
 // Load style from memory
 // WARNING: Binary files only
 static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize)
@@ -4485,7 +4819,7 @@
                 // NOTE: All DEFAULT properties should be defined first in the file
                 GuiSetStyle(0, (int)propertyId, propertyValue);
 
-                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue);
+                if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int j = 1; j < RAYGUI_MAX_CONTROLS; j++) GuiSetStyle(j, (int)propertyId, propertyValue);
             }
             else GuiSetStyle((int)controlId, (int)propertyId, propertyValue);
         }
@@ -4531,7 +4865,7 @@
             {
                 // Compressed font atlas image data (DEFLATE), it requires DecompressData()
                 int dataUncompSize = 0;
-                unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize);
+                unsigned char *compData = (unsigned char *)RAYGUI_CALLOC(fontImageCompSize, sizeof(unsigned char));
                 memcpy(compData, fileDataPtr, fontImageCompSize);
                 fileDataPtr += fontImageCompSize;
 
@@ -4545,7 +4879,7 @@
             else
             {
                 // Font atlas image data is not compressed
-                imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize);
+                imFont.data = (unsigned char *)RAYGUI_CALLOC(fontImageUncompSize, sizeof(unsigned char));
                 memcpy(imFont.data, fileDataPtr, fontImageUncompSize);
                 fileDataPtr += fontImageUncompSize;
             }
@@ -4573,7 +4907,7 @@
                 if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize))
                 {
                     // Recs data is compressed, uncompress it
-                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize);
+                    unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_CALLOC(recsDataCompressedSize, sizeof(unsigned char));
 
                     memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize);
                     fileDataPtr += recsDataCompressedSize;
@@ -4615,7 +4949,7 @@
                 if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize))
                 {
                     // Glyphs data is compressed, uncompress it
-                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize);
+                    unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_CALLOC(glyphsDataCompressedSize, sizeof(unsigned char));
 
                     memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize);
                     fileDataPtr += glyphsDataCompressedSize;
@@ -4668,68 +5002,6 @@
     }
 }
 
-// Gui get text width considering icon
-static int GetTextWidth(const char *text)
-{
-    #if !defined(ICON_TEXT_PADDING)
-        #define ICON_TEXT_PADDING   4
-    #endif
-
-    Vector2 textSize = { 0 };
-    int textIconOffset = 0;
-
-    if ((text != NULL) && (text[0] != '\0'))
-    {
-        if (text[0] == '#')
-        {
-            for (int i = 1; (i < 5) && (text[i] != '\0'); i++)
-            {
-                if (text[i] == '#')
-                {
-                    textIconOffset = i;
-                    break;
-                }
-            }
-        }
-
-        text += textIconOffset;
-
-        // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly
-        float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE);
-
-        // Custom MeasureText() implementation
-        if ((guiFont.texture.id > 0) && (text != NULL))
-        {
-            // Get size in bytes of text, considering end of line and line break
-            int size = 0;
-            for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++)
-            {
-                if ((text[i] != '\0') && (text[i] != '\n')) size++;
-                else break;
-            }
-
-            float scaleFactor = fontSize/(float)guiFont.baseSize;
-            textSize.y = (float)guiFont.baseSize*scaleFactor;
-            float glyphWidth = 0.0f;
-
-            for (int i = 0, codepointSize = 0; i < size; i += codepointSize)
-            {
-                int codepoint = GetCodepointNext(&text[i], &codepointSize);
-                int codepointIndex = GetGlyphIndex(guiFont, codepoint);
-
-                if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor);
-                else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor);
-
-                textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING));
-            }
-        }
-
-        if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE + ICON_TEXT_PADDING);
-    }
-
-    return (int)textSize.x;
-}
-
 // Get text bounds considering control bounds
 static Rectangle GetTextBounds(int control, Rectangle bounds)
 {
@@ -4750,7 +5022,7 @@
         case SLIDER:
         case CHECKBOX:
         case VALUEBOX:
-        case SPINNER:
+        case CONTROL11:
             // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER
         default:
         {
@@ -4796,7 +5068,8 @@
 }
 
 // Get text divided into lines (by line-breaks '\n')
-const char **GetTextLines(const char *text, int *count)
+// WARNING: It returns pointers to new lines but it does not add NULL ('\0') terminator!
+static const char **GetTextLines(const char *text, int *count)
 {
     #define RAYGUI_MAX_TEXT_LINES   128
 
@@ -4806,25 +5079,18 @@
     int textSize = (int)strlen(text);
 
     lines[0] = text;
-    int len = 0;
     *count = 1;
-    //int lineSize = 0;   // Stores current line size, not returned
 
     for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++)
     {
         if (text[i] == '\n')
         {
-            //lineSize = len;
             k++;
-            lines[k] = &text[i + 1];     // WARNING: next value is valid?
-            len = 0;
+            lines[k] = &text[i + 1]; // WARNING: next value is valid?
             *count += 1;
         }
-        else len++;
     }
 
-    //lines[*count - 1].size = len;
-
     return lines;
 }
 
@@ -4900,8 +5166,8 @@
         float textBoundsWidthOffset = 0.0f;
 
         // NOTE: We get text size after icon has been processed
-        // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed?
-        int textSizeX = GetTextWidth(lines[i]);
+        // WARNING: GuiGetTextWidth() also processes text icon to get width! -> Really needed?
+        int textSizeX = GuiGetTextWidth(lines[i]);
 
         // If text requires an icon, add size to measure
         if (iconId >= 0)
@@ -4964,7 +5230,7 @@
         float textOffsetX = 0.0f;
         float glyphWidth = 0;
 
-        int ellipsisWidth = GetTextWidth("...");
+        int ellipsisWidth = GuiGetTextWidth("...");
         bool textOverflow = false;
         for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize)
         {
@@ -5108,13 +5374,13 @@
 
         if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width);
 
-        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL);
+        GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, NULL);
 
         int textPadding = GuiGetStyle(LABEL, TEXT_PADDING);
         int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT);
         GuiSetStyle(LABEL, TEXT_PADDING, 0);
         GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER);
-        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr);
+        GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.0f }, guiTooltipPtr);
         GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment);
         GuiSetStyle(LABEL, TEXT_PADDING, textPadding);
     }
@@ -5168,7 +5434,7 @@
             buffer[i] = '\0';   // Set an end of string at this point
 
             counter++;
-            if (counter > RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
+            if (counter >= RAYGUI_TEXTSPLIT_MAX_ITEMS) break;
         }
     }
 
@@ -5490,10 +5756,10 @@
 {
     Color color;
 
-    color.r = (unsigned char)(hexValue >> 24) & 0xFF;
-    color.g = (unsigned char)(hexValue >> 16) & 0xFF;
-    color.b = (unsigned char)(hexValue >> 8) & 0xFF;
-    color.a = (unsigned char)hexValue & 0xFF;
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
 
     return color;
 }
@@ -5526,7 +5792,7 @@
 
     va_list args;
     va_start(args, text);
-    vsprintf(buffer, text, args);
+    vsnprintf(buffer, RAYGUI_TEXTFORMAT_MAX_SIZE, text, args);
     va_end(args);
 
     return buffer;
@@ -5695,7 +5961,7 @@
     }
     else if (0xe0 == (0xf0 & ptr[0]))
     {
-        // 3 byte UTF-8 codepoint */
+        // 3 byte UTF-8 codepoint
         if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
         codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
         *codepointSize = 3;
diff --git a/raylib/examples/shapes/shapes_ball_physics.c b/raylib/examples/shapes/shapes_ball_physics.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_ball_physics.c
@@ -0,0 +1,221 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - ball physics
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#define MAX_BALLS 5000 // Maximum quantity of balls
+
+typedef struct Ball {
+    Vector2 pos;       // Position
+    Vector2 vel;       // Velocity
+    Vector2 ppos;      // Previous position
+    float radius;
+    float friction;   
+    float elasticity;
+    Color color;
+    bool grabbed;
+} Ball;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics");
+
+    Ball balls[MAX_BALLS] = {{
+        .pos = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f },
+        .vel = { 200, 200 },
+        .ppos = { 0 },
+        .radius = 40,
+        .friction = 0.99f,
+        .elasticity = 0.9f,
+        .color = BLUE,
+        .grabbed = false
+    }};
+    
+    int ballCount = 1;
+    Ball *grabbedBall = NULL;   // A pointer to the current ball that is grabbed
+    Vector2 pressOffset = {0};  // Mouse press offset relative to the ball that grabbedd
+
+    float gravity = 100;        // World gravity
+
+    SetTargetFPS(60);           // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float delta = GetFrameTime();
+        Vector2 mousePos = GetMousePosition();
+
+        // Checks if a ball was grabbed
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+        {
+            for (int i = ballCount - 1; i >= 0; i--)
+            {
+                Ball *ball = &balls[i];
+                pressOffset.x = mousePos.x - ball->pos.x;
+                pressOffset.y = mousePos.y - ball->pos.y;
+
+                // If the distance between the ball position and the mouse press position
+                // is less than or equal to the ball radius, the event occurred inside the ball
+                if (hypot(pressOffset.x, pressOffset.y) <= ball->radius)
+                {
+                    ball->grabbed = true;
+                    grabbedBall = ball;
+                    break;
+                }
+            }
+        }
+
+        // Releases any ball the was grabbed
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+        {
+            if (grabbedBall != NULL)
+            {
+                grabbedBall->grabbed = false;
+                grabbedBall = NULL;
+            }
+        }
+
+        // Creates a new ball
+        if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) || (IsKeyDown(KEY_LEFT_CONTROL) && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)))
+        {
+            if (ballCount < MAX_BALLS)
+            {
+                balls[ballCount++] = (Ball){
+                    .pos = mousePos,
+                    .vel = { (float)GetRandomValue(-300, 300), (float)GetRandomValue(-300, 300) },
+                    .ppos = { 0 },
+                    .radius = 20.0f + (float)GetRandomValue(0, 30),
+                    .friction = 0.99f,
+                    .elasticity = 0.9f,
+                    .color = { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 },
+                    .grabbed = false
+                };
+            }
+        }
+
+        // Shake balls
+        if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+        {
+            for (int i = 0; i < ballCount; i++)
+            {
+                if (!balls[i].grabbed) balls[i].vel = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) };
+            }
+        }
+
+        // Changes gravity
+        gravity += GetMouseWheelMove()*5;
+
+        // Updates each ball state
+        for (int i = 0; i < ballCount; i++)
+        {
+            Ball *ball = &balls[i];
+
+            // The ball is not grabbed
+            if (!ball->grabbed) 
+            {
+                // Ball repositioning using the velocity
+                ball->pos.x += ball->vel.x * delta;
+                ball->pos.y += ball->vel.y * delta;
+
+                // Does the ball hit the screen right boundary?
+                if ((ball->pos.x + ball->radius) >= screenWidth) 
+                {
+                    ball->pos.x = screenWidth - ball->radius; // Ball repositioning
+                    ball->vel.x = -ball->vel.x*ball->elasticity;  // Elasticity makes the ball lose 10% of its velocity on hit
+                } 
+                // Does the ball hit the screen left boundary?
+                else if ((ball->pos.x - ball->radius) <= 0)
+                { 
+                    ball->pos.x = ball->radius;
+                    ball->vel.x = -ball->vel.x*ball->elasticity;
+                }
+
+                // The same for y axis
+                if ((ball->pos.y + ball->radius) >= screenHeight) 
+                {
+                    ball->pos.y = screenHeight - ball->radius;
+                    ball->vel.y = -ball->vel.y*ball->elasticity;
+                } 
+                else if ((ball->pos.y - ball->radius) <= 0) 
+                { 
+                    ball->pos.y = ball->radius;
+                    ball->vel.y = -ball->vel.y*ball->elasticity;
+                }
+
+                // Friction makes the ball lose 1% of its velocity each frame
+                ball->vel.x = ball->vel.x*ball->friction;
+                // Gravity affects only the y axis
+                ball->vel.y = ball->vel.y*ball->friction + gravity;
+            }
+            else
+            {
+                // Ball repositioning using the mouse position
+                ball->pos.x = mousePos.x - pressOffset.x;
+                ball->pos.y = mousePos.y - pressOffset.y;
+
+                // While the ball is grabbed, recalculates its velocity
+                ball->vel.x = (ball->pos.x - ball->ppos.x)/delta;
+                ball->vel.y = (ball->pos.y - ball->ppos.y)/delta;
+                ball->ppos = ball->pos;
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < ballCount; i++)
+            {
+                DrawCircleV(balls[i].pos, balls[i].radius, balls[i].color);
+                DrawCircleLinesV(balls[i].pos, balls[i].radius, BLACK);
+            }
+
+            DrawText("grab a ball by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
+            DrawText("right click to create new balls (keep left control pressed to create a lot)", 10, 30, 10, DARKGRAY);
+            DrawText("use mouse wheel to change gravity", 10, 50, 10, DARKGRAY);
+            DrawText("middle click to shake", 10, 70, 10, DARKGRAY);
+            DrawText(TextFormat("BALL COUNT: %d", ballCount), 10, GetScreenHeight() - 70, 20, BLACK);
+            DrawText(TextFormat("GRAVITY: %.2f", gravity), 10, GetScreenHeight() - 40, 20, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_basic_shapes.c b/raylib/examples/shapes/shapes_basic_shapes.c
--- a/raylib/examples/shapes/shapes_basic_shapes.c
+++ b/raylib/examples/shapes/shapes_basic_shapes.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - Draw basic shapes 2d (rectangle, circle, line...)
+*   raylib [shapes] example - basic shapes
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes drawing");
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - basic shapes");
 
     float rotation = 0.0f;
 
@@ -50,6 +52,8 @@
             DrawCircle(screenWidth/5, 120, 35, DARKBLUE);
             DrawCircleGradient(screenWidth/5, 220, 60, GREEN, SKYBLUE);
             DrawCircleLines(screenWidth/5, 340, 80, DARKBLUE);
+            DrawEllipse(screenWidth/5, 120, 25, 20, YELLOW);
+            DrawEllipseLines(screenWidth/5, 120, 30, 25, YELLOW);
 
             // Rectangle shapes and lines
             DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED);
diff --git a/raylib/examples/shapes/shapes_bouncing_ball.c b/raylib/examples/shapes/shapes_bouncing_ball.c
--- a/raylib/examples/shapes/shapes_bouncing_ball.c
+++ b/raylib/examples/shapes/shapes_bouncing_ball.c
@@ -2,12 +2,16 @@
 *
 *   raylib [shapes] example - bouncing ball
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
+*   Example contributed by Ramon Santamaria (@raysan5), reviewed by Jopestpe (@jopestpe)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -29,7 +33,9 @@
     Vector2 ballPosition = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
     Vector2 ballSpeed = { 5.0f, 4.0f };
     int ballRadius = 20;
+    float gravity = 0.2f;
 
+    bool useGravity = true;
     bool pause = 0;
     int framesCounter = 0;
 
@@ -41,6 +47,7 @@
     {
         // Update
         //-----------------------------------------------------
+        if (IsKeyPressed(KEY_G)) useGravity = !useGravity;
         if (IsKeyPressed(KEY_SPACE)) pause = !pause;
 
         if (!pause)
@@ -48,9 +55,11 @@
             ballPosition.x += ballSpeed.x;
             ballPosition.y += ballSpeed.y;
 
+            if (useGravity) ballSpeed.y += gravity;
+
             // Check walls collision for bouncing
             if ((ballPosition.x >= (GetScreenWidth() - ballRadius)) || (ballPosition.x <= ballRadius)) ballSpeed.x *= -1.0f;
-            if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -1.0f;
+            if ((ballPosition.y >= (GetScreenHeight() - ballRadius)) || (ballPosition.y <= ballRadius)) ballSpeed.y *= -0.95f;
         }
         else framesCounter++;
         //-----------------------------------------------------
@@ -63,6 +72,9 @@
 
             DrawCircleV(ballPosition, (float)ballRadius, MAROON);
             DrawText("PRESS SPACE to PAUSE BALL MOVEMENT", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
+
+            if (useGravity) DrawText("GRAVITY: ON (Press G to disable)", 10, GetScreenHeight() - 50, 20, DARKGREEN);
+            else DrawText("GRAVITY: OFF (Press G to enable)", 10, GetScreenHeight() - 50, 20, RED);
 
             // On pause, we draw a blinking message
             if (pause && ((framesCounter/30)%2)) DrawText("PAUSED", 350, 200, 30, GRAY);
diff --git a/raylib/examples/shapes/shapes_bullet_hell.c b/raylib/examples/shapes/shapes_bullet_hell.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_bullet_hell.c
@@ -0,0 +1,248 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - bullet hell
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Zero (@zerohorsepower) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Zero (@zerohorsepower)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>         // Required for: calloc(), free()
+#include <math.h>           // Required for: cosf(), sinf()
+
+#define MAX_BULLETS 500000      // Max bullets to be processed
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct Bullet {
+    Vector2 position;       // Bullet position on screen
+    Vector2 acceleration;   // Amount of pixels to be incremented to position every frame
+    bool disabled;          // Skip processing and draw case out of screen
+    Color color;            // Bullet color
+} Bullet;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell");
+
+    // Bullets definition
+    Bullet *bullets = (Bullet *)RL_CALLOC(MAX_BULLETS, sizeof(Bullet)); // Bullets array
+    int bulletCount = 0;
+    int bulletDisabledCount = 0; // Used to calculate how many bullets are on screen
+    int bulletRadius = 10;
+    float bulletSpeed = 3.0f;
+    int bulletRows = 6;
+    Color bulletColor[2] = { RED, BLUE };
+
+    // Spawner variables
+    float baseDirection = 0;
+    int angleIncrement = 5; // After spawn all bullet rows, increment this value on the baseDirection for next the frame
+    float spawnCooldown = 2;
+    float spawnCooldownTimer = spawnCooldown;
+
+    // Magic circle
+    float magicCircleRotation = 0;
+
+    // Used on performance drawing
+    RenderTexture bulletTexture = LoadRenderTexture(24, 24);
+
+    // Draw circle to bullet texture, then draw bullet using DrawTexture()
+    // NOTE: This is done to improve the performance, since DrawCircle() is very slow
+    BeginTextureMode(bulletTexture);
+        DrawCircle(12, 12, (float)bulletRadius, WHITE);
+        DrawCircleLines(12, 12, (float)bulletRadius, BLACK);
+    EndTextureMode();
+
+    bool drawInPerformanceMode = true; // Switch between DrawCircle() and DrawTexture()
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Reset the bullet index
+        // New bullets will replace the old ones that are already disabled due to out-of-screen
+        if (bulletCount >= MAX_BULLETS)
+        {
+            bulletCount = 0;
+            bulletDisabledCount = 0;
+        }
+
+        spawnCooldownTimer--;
+        if (spawnCooldownTimer < 0)
+        {
+            spawnCooldownTimer = spawnCooldown;
+
+            // Spawn bullets
+            float degreesPerRow = 360.0f/bulletRows;
+            for (int row = 0; row < bulletRows; row++)
+            {
+                if (bulletCount < MAX_BULLETS)
+                {
+                    bullets[bulletCount].position = (Vector2){(float) screenWidth/2, (float) screenHeight/2};
+                    bullets[bulletCount].disabled = false;
+                    bullets[bulletCount].color = bulletColor[row%2];
+
+                    float bulletDirection = baseDirection + (degreesPerRow*row);
+
+                    // Bullet speed*bullet direction, this will determine how much pixels will be incremented/decremented
+                    // from the bullet position every frame. Since the bullets doesn't change its direction and speed,
+                    // only need to calculate it at the spawning time
+                    // 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise
+                    // Case you want it to be anti-clockwise, add "* -1" at the y acceleration
+                    bullets[bulletCount].acceleration = (Vector2){
+                        bulletSpeed*cosf(bulletDirection*DEG2RAD),
+                        bulletSpeed*sinf(bulletDirection*DEG2RAD)
+                    };
+
+                    bulletCount++;
+                }
+            }
+
+            baseDirection += angleIncrement;
+        }
+
+        // Update bullets position based on its acceleration
+        for (int i = 0; i < bulletCount; i++)
+        {
+            // Only update bullet if inside the screen
+            if (!bullets[i].disabled)
+            {
+                bullets[i].position.x += bullets[i].acceleration.x;
+                bullets[i].position.y += bullets[i].acceleration.y;
+
+                // Disable bullet if out of screen
+                if ((bullets[i].position.x < -bulletRadius*2) ||
+                    (bullets[i].position.x > screenWidth + bulletRadius*2) ||
+                    (bullets[i].position.y < -bulletRadius*2) ||
+                    (bullets[i].position.y > screenHeight + bulletRadius*2))
+                {
+                    bullets[i].disabled = true;
+                    bulletDisabledCount++;
+                }
+            }
+        }
+
+        // Input logic
+        if ((IsKeyPressed(KEY_RIGHT) || IsKeyPressed(KEY_D)) && (bulletRows < 359)) bulletRows++;
+        if ((IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_A)) && (bulletRows > 1)) bulletRows--;
+        if (IsKeyPressed(KEY_UP) || IsKeyPressed(KEY_W)) bulletSpeed += 0.25f;
+        if ((IsKeyPressed(KEY_DOWN) || IsKeyPressed(KEY_S)) && (bulletSpeed > 0.50f)) bulletSpeed -= 0.25f;
+        if (IsKeyPressed(KEY_Z) && (spawnCooldown > 1)) spawnCooldown--;
+        if (IsKeyPressed(KEY_X)) spawnCooldown++;
+        if (IsKeyPressed(KEY_ENTER)) drawInPerformanceMode = !drawInPerformanceMode;
+
+        if (IsKeyDown(KEY_SPACE))
+        {
+            angleIncrement += 1;
+            angleIncrement %= 360;
+        }
+
+        if (IsKeyPressed(KEY_C))
+        {
+            bulletCount = 0;
+            bulletDisabledCount = 0;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw magic circle
+            magicCircleRotation++;
+            DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 },
+                (Vector2){ 60.0f, 60.0f }, magicCircleRotation, PURPLE);
+            DrawRectanglePro((Rectangle){ (float)screenWidth/2, (float)screenHeight/2, 120, 120 },
+                (Vector2){ 60.0f, 60.0f }, magicCircleRotation + 45, PURPLE);
+            DrawCircleLines(screenWidth/2, screenHeight/2, 70, BLACK);
+            DrawCircleLines(screenWidth/2, screenHeight/2, 50, BLACK);
+            DrawCircleLines(screenWidth/2, screenHeight/2, 30, BLACK);
+
+            // Draw bullets
+            if (drawInPerformanceMode)
+            {
+                // Draw bullets using pre-rendered texture containing circle
+                for (int i = 0; i < bulletCount; i++)
+                {
+                    // Do not draw disabled bullets (out of screen)
+                    if (!bullets[i].disabled)
+                    {
+                        DrawTexture(bulletTexture.texture,
+                            (int)(bullets[i].position.x - bulletTexture.texture.width*0.5f),
+                            (int)(bullets[i].position.y - bulletTexture.texture.height*0.5f),
+                            bullets[i].color);
+                    }
+                }
+            }
+            else
+            {
+                // Draw bullets using DrawCircle(), less performant
+                for (int i = 0; i < bulletCount; i++)
+                {
+                    // Do not draw disabled bullets (out of screen)
+                    if (!bullets[i].disabled)
+                    {
+                        DrawCircleV(bullets[i].position, (float)bulletRadius, bullets[i].color);
+                        DrawCircleLinesV(bullets[i].position, (float)bulletRadius, BLACK);
+                    }
+                }
+            }
+
+            // Draw UI
+            DrawRectangle(10, 10, 280, 150, (Color){0,0, 0, 200 });
+            DrawText("Controls:", 20, 20, 10, LIGHTGRAY);
+            DrawText("- Right/Left or A/D: Change rows number", 40, 40, 10, LIGHTGRAY);
+            DrawText("- Up/Down or W/S: Change bullet speed", 40, 60, 10, LIGHTGRAY);
+            DrawText("- Z or X: Change spawn cooldown", 40, 80, 10, LIGHTGRAY);
+            DrawText("- Space (Hold): Change the angle increment", 40, 100, 10, LIGHTGRAY);
+            DrawText("- Enter: Switch draw method (Performance)", 40, 120, 10, LIGHTGRAY);
+            DrawText("- C: Clear bullets", 40, 140, 10, LIGHTGRAY);
+
+            DrawRectangle(610, 10, 170, 30, (Color){0,0, 0, 200 });
+            if (drawInPerformanceMode) DrawText("Draw method: DrawTexture(*)", 620, 20, 10, GREEN);
+            else DrawText("Draw method: DrawCircle(*)", 620, 20, 10, RED);
+
+            DrawRectangle(135, 410, 530, 30, (Color){0,0, 0, 200 });
+            DrawText(TextFormat("[ FPS: %d, Bullets: %d, Rows: %d, Bullet speed: %.2f, Angle increment per frame: %d, Cooldown: %.0f ]",
+                    GetFPS(), bulletCount - bulletDisabledCount, bulletRows, bulletSpeed,  angleIncrement, spawnCooldown),
+                155, 420, 10, GREEN);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTexture(bulletTexture); // Unload bullet texture
+
+    RL_FREE(bullets);     // Free bullets array data
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_circle_sector_drawing.c b/raylib/examples/shapes/shapes_circle_sector_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_circle_sector_drawing.c
@@ -0,0 +1,90 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - circle sector drawing
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 2.5
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - circle sector drawing");
+
+    Vector2 center = {(GetScreenWidth() - 300)/2.0f, GetScreenHeight()/2.0f };
+
+    float outerRadius = 180.0f;
+    float startAngle = 0.0f;
+    float endAngle = 180.0f;
+    float segments = 10.0f;
+    float minSegments = 4;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // NOTE: All variables update happens inside GUI control functions
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
+            DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
+
+            DrawCircleSector(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f));
+            DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.6f));
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, 0, 720);
+            GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, 0, 720);
+
+            GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
+            GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
+            //------------------------------------------------------------------------------
+
+            minSegments = truncf(ceilf((endAngle - startAngle)/90));
+            DrawText(TextFormat("MODE: %s", (segments >= minSegments)? "MANUAL" : "AUTO"), 600, 200, 10, (segments >= minSegments)? MAROON : DARKGRAY);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_clock_of_clocks.c b/raylib/examples/shapes/shapes_clock_of_clocks.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_clock_of_clocks.c
@@ -0,0 +1,205 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - clock of clocks
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
+*
+*   Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 JP Mortiboys (@themushroompirates)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"    // Required for: Lerp()
+#include <time.h>       // Required for: time(), localtime()
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - clock of clocks");
+
+    const Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.75f);
+    const Color handsColor = ColorLerp(YELLOW, RAYWHITE, .25f);
+
+    const float clockFaceSize = 24;
+    const float clockFaceSpacing = 8.0f;
+    const float sectionSpacing = 16.0f;
+
+    const Vector2 TL = (Vector2){   0.0f,  90.0f }; // Top-left corner
+    const Vector2 TR = (Vector2){  90.0f, 180.0f }; // Top-right corner
+    const Vector2 BR = (Vector2){ 180.0f, 270.0f }; // Bottom-right corner
+    const Vector2 BL = (Vector2){   0.0f, 270.0f }; // Bottom-left corner
+    const Vector2 HH = (Vector2){   0.0f, 180.0f }; // Horizontal line
+    const Vector2 VV = (Vector2){  90.0f, 270.0f }; // Vertical line
+    const Vector2 ZZ = (Vector2){ 135.0f, 135.0f }; // Not relevant
+
+    const Vector2 digitAngles[10][24] = {
+        /* 0 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,VV,VV,VV,/* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
+        /* 1 */ { TL,HH,TR,ZZ, /* */ BL,TR,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ TL,BR,BL,TR,/* */ BL,HH,HH,BR },
+        /* 2 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,HH,BR },
+        /* 3 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
+        /* 4 */ { TL,TR,TL,TR, /* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR },
+        /* 5 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
+        /* 6 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
+        /* 7 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR },
+        /* 8 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR },
+        /* 9 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR },
+    };
+
+    // Time for the hands to move to the new position (in seconds); this must be <1s
+    const float handsMoveDuration = 0.5f;
+
+    int prevSeconds = -1;
+    Vector2 currentAngles[6][24] = { 0 };
+    Vector2 srcAngles[6][24] = { 0 };
+    Vector2 dstAngles[6][24] = { 0 };
+
+    float handsMoveTimer = 0.0f;
+    int hourMode = 24;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Get the current time
+        time_t rawtime;
+        struct tm *timeinfo;
+
+        time(&rawtime);
+        timeinfo = localtime(&rawtime);
+
+        if (timeinfo->tm_sec != prevSeconds)
+        {
+            // The time has changed, so we need to move the hands to the new positions
+            prevSeconds = timeinfo->tm_sec;
+
+            // Format the current time so we can access the individual digits
+            const char *clockDigits = TextFormat("%02d%02d%02d", timeinfo->tm_hour%hourMode, timeinfo->tm_min, timeinfo->tm_sec);
+
+            // Fetch where we want all the hands to be
+            for (int digit = 0; digit < 6; digit++)
+            {
+                for (int cell = 0; cell < 24; cell++)
+                {
+                    srcAngles[digit][cell] = currentAngles[digit][cell];
+                    dstAngles[digit][cell] = digitAngles[clockDigits[digit] - '0'][cell];
+
+                    // Quick exception for 12h mode
+                    if ((digit == 0) && (hourMode == 12) && (clockDigits[0] == '0')) dstAngles[digit][cell] = ZZ;
+                    if (srcAngles[digit][cell].x > dstAngles[digit][cell].x) srcAngles[digit][cell].x -= 360.0f;
+                    if (srcAngles[digit][cell].y > dstAngles[digit][cell].y) srcAngles[digit][cell].y -= 360.0f;
+                }
+            }
+
+            // Reset the timer
+            handsMoveTimer = -GetFrameTime();
+        }
+
+        // Now let's animate all the hands if we need to
+        if (handsMoveTimer < handsMoveDuration)
+        {
+            // Increase the timer but don't go above the maximum
+            handsMoveTimer = Clamp(handsMoveTimer + GetFrameTime(), 0, handsMoveDuration);
+
+            // Calculate the%completion of the animation
+            float t = handsMoveTimer/handsMoveDuration;
+
+            // A little cheeky smoothstep
+            t = t*t*(3.0f - 2.0f*t);
+
+            for (int digit = 0; digit < 6; digit++)
+            {
+                for (int cell = 0; cell < 24; cell++)
+                {
+                    currentAngles[digit][cell].x = Lerp(srcAngles[digit][cell].x, dstAngles[digit][cell].x, t);
+                    currentAngles[digit][cell].y = Lerp(srcAngles[digit][cell].y, dstAngles[digit][cell].y, t);
+                }
+            }
+        }
+
+        // Handle input
+        if (IsKeyPressed(KEY_SPACE)) hourMode = 36 - hourMode; // Toggle between 12 and 24 hour mode with space
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(bgColor);
+
+            DrawText(TextFormat("%d-h mode, space to change", hourMode), 10, 30, 20, RAYWHITE);
+
+            float xOffset = 4.0f;
+
+            for (int digit = 0; digit < 6; digit++)
+            {
+                for (int row = 0; row < 6; row++)
+                {
+                    for (int col = 0; col < 4; col++)
+                    {
+                        Vector2 centre = (Vector2){
+                            xOffset + col*(clockFaceSize+clockFaceSpacing) + clockFaceSize*0.5f,
+                            100 + row*(clockFaceSize+clockFaceSpacing) + clockFaceSize*0.5f
+                        };
+
+                        DrawRing(centre, clockFaceSize*0.5f - 2.0f, clockFaceSize*0.5f, 0, 360, 24, DARKGRAY);
+
+                        // Big hand
+                        DrawRectanglePro(
+                            (Rectangle){centre.x, centre.y, clockFaceSize*0.5f+4.0f, 4.0f},
+                            (Vector2){ 2.0f, 2.0f },
+                            currentAngles[digit][row*4+col].x,
+                            handsColor
+                        );
+
+                        // Little hand
+                        DrawRectanglePro(
+                            (Rectangle){centre.x, centre.y, clockFaceSize*0.5f+2.0f, 4.0f},
+                            (Vector2){ 2.0f, 2.0f },
+                            currentAngles[digit][row*4+col].y,
+                            handsColor
+                        );
+                    }
+                }
+
+                xOffset += (clockFaceSize+clockFaceSpacing)*4;
+                if (digit%2 == 1)
+                {
+                    DrawRing((Vector2){xOffset + 4.0f, 160.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
+                    DrawRing((Vector2){xOffset + 4.0f, 225.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
+                    xOffset += sectionSpacing;
+                }
+            }
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_collision_area.c b/raylib/examples/shapes/shapes_collision_area.c
--- a/raylib/examples/shapes/shapes_collision_area.c
+++ b/raylib/examples/shapes/shapes_collision_area.c
@@ -2,18 +2,18 @@
 *
 *   raylib [shapes] example - collision area
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
-
-#include <stdlib.h>     // Required for: abs()
 
 //------------------------------------------------------------------------------------
 // Program main entry point
diff --git a/raylib/examples/shapes/shapes_colors_palette.c b/raylib/examples/shapes/shapes_colors_palette.c
--- a/raylib/examples/shapes/shapes_colors_palette.c
+++ b/raylib/examples/shapes/shapes_colors_palette.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - Colors palette
+*   raylib [shapes] example - colors palette
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/shapes/shapes_dashed_line.c b/raylib/examples/shapes/shapes_dashed_line.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_dashed_line.c
@@ -0,0 +1,96 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - dashed line
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Luís Almeida (@luis605)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Luís Almeida (@luis605)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - dashed line");
+
+    // Line Properties
+    Vector2 lineStartPosition = { 20.0f, 50.0f };
+    Vector2 lineEndPosition = { 780.0f, 400.0f };
+    float dashLength = 25.0f;
+    float blankLength = 15.0f;
+
+    // Color selection
+    Color lineColors[] = { RED, ORANGE, GOLD, GREEN, BLUE, VIOLET, PINK, BLACK };
+    int colorIndex = 0;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        lineEndPosition = GetMousePosition(); // Line endpoint follows the mouse
+
+        // Change Dash Length (UP/DOWN arrows)
+        if (IsKeyDown(KEY_UP)) dashLength += 1.0f;
+        if (IsKeyDown(KEY_DOWN) && dashLength > 1.0f) dashLength -= 1.0f;
+
+        // Change Space Length (LEFT/RIGHT arrows)
+        if (IsKeyDown(KEY_RIGHT)) blankLength += 1.0f;
+        if (IsKeyDown(KEY_LEFT) && blankLength > 1.0f) blankLength -= 1.0f;
+
+        // Cycle through colors ('C' key)
+        if (IsKeyPressed(KEY_C)) colorIndex = (colorIndex + 1)%(sizeof(lineColors)/sizeof(Color));
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw the dashed line with the current properties
+            DrawLineDashed(lineStartPosition, lineEndPosition, (int)dashLength, (int)blankLength, lineColors[colorIndex]);
+
+            // Draw UI and Instructions
+            DrawRectangle(5, 5, 265, 95, Fade(SKYBLUE, 0.5f));
+            DrawRectangleLines(5, 5, 265, 95, BLUE);
+
+            DrawText("CONTROLS:", 15, 15, 10, BLACK);
+            DrawText("UP/DOWN: Change Dash Length", 15, 35, 10, BLACK);
+            DrawText("LEFT/RIGHT: Change Space Length", 15, 55, 10, BLACK);
+            DrawText("C: Cycle Color", 15, 75, 10, BLACK);
+
+            DrawText(TextFormat("Dash: %.0f | Space: %.0f", dashLength, blankLength), 15, 115, 10, DARKGRAY);
+
+            DrawFPS(screenWidth - 80, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_digital_clock.c b/raylib/examples/shapes/shapes_digital_clock.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_digital_clock.c
@@ -0,0 +1,319 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - digital clock
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Hamza RAHAL (@hmz-rhl) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Hamza RAHAL (@hmz-rhl) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#define _CRT_SECURE_NO_WARNINGS   // Disable some Visual Studio warnings
+
+#include "raylib.h"
+
+#include <math.h>       // Required for: cosf(), sinf()
+#include <time.h>       // Required for: time(), localtime()
+
+#define CLOCK_ANALOG    0
+#define CLOCK_DIGITAL   1
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Clock hand type
+typedef struct {
+    int value;          // Time value
+
+    // Visual elements
+    float angle;        // Hand angle
+    int length;         // Hand length
+    int thickness;      // Hand thickness
+    Color color;        // Hand color
+} ClockHand;
+
+// Clock hands
+typedef struct {
+    ClockHand second;   // Clock hand for seconds
+    ClockHand minute;   // Clock hand for minutes
+    ClockHand hour;     // Clock hand for hours
+} Clock;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void UpdateClock(Clock *clock); // Update clock time
+static void DrawClockAnalog(Clock clock, Vector2 position); // Draw analog clock at desired center position
+static void DrawClockDigital(Clock clock, Vector2 position); // Draw digital clock at desired position
+
+static void DrawDisplayValue(Vector2 position, int value, Color colorOn, Color colorOff);
+static void Draw7SDisplay(Vector2 position, char segments, Color colorOn, Color colorOff);
+static void DrawDisplaySegment(Vector2 center, int length, int thick, bool vertical, Color color);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock");
+
+    int clockMode = CLOCK_DIGITAL;
+
+    // Initialize clock
+    // NOTE: Includes visual info for anlaog clock
+    Clock clock = {
+        .second.angle = 45,
+        .second.length = 140,
+        .second.thickness = 3,
+        .second.color = MAROON,
+
+        .minute.angle = 10,
+        .minute.length = 130,
+        .minute.thickness = 7,
+        .minute.color = DARKGRAY,
+
+        .hour.angle = 0,
+        .hour.length = 100,
+        .hour.thickness = 7,
+        .hour.color = BLACK,
+    };
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_SPACE))
+        {
+            // Toggle clock mode
+            if (clockMode == CLOCK_DIGITAL) clockMode = CLOCK_ANALOG;
+            else if (clockMode == CLOCK_ANALOG) clockMode = CLOCK_DIGITAL;
+        }
+
+        UpdateClock(&clock); // Update clock required data: value and angle
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw clock in selected mode
+            if (clockMode == CLOCK_ANALOG) DrawClockAnalog(clock, (Vector2){ 400, 240 });
+            else if (clockMode == CLOCK_DIGITAL)
+            {
+                DrawClockDigital(clock, (Vector2){ 30, 60 });
+
+                // Draw clock using default raylib font
+                // Get pointer to formated clock time string
+                // WARNING: Pointing to an internal static string that is reused between TextFormat() calls
+                const char *clockTime = TextFormat("%02i:%02i:%02i", clock.hour.value, clock.minute.value, clock.second.value);
+                DrawText(clockTime, GetScreenWidth()/2 - MeasureText(clockTime, 150)/2, 300, 150, BLACK);
+            }
+
+            DrawText(TextFormat("Press [SPACE] to switch clock mode: %s",
+                (clockMode == CLOCK_DIGITAL)? "DIGITAL CLOCK" : "ANALOGUE CLOCK"), 10, 10, 20, DARKGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Update clock time
+static void UpdateClock(Clock *clock)
+{
+    time_t rawtime;
+    struct tm *timeinfo;
+
+    time(&rawtime);
+    timeinfo = localtime(&rawtime);
+
+    // Updating time data
+    clock->second.value = timeinfo->tm_sec;
+    clock->minute.value = timeinfo->tm_min;
+    clock->hour.value = timeinfo->tm_hour;
+
+    clock->hour.angle = (timeinfo->tm_hour%12)*180.0f/6.0f;
+    clock->hour.angle += (timeinfo->tm_min%60)*30/60.0f;
+    clock->hour.angle -= 90;
+
+    clock->minute.angle = (timeinfo->tm_min%60)*6.0f;
+    clock->minute.angle += (timeinfo->tm_sec%60)*6/60.0f;
+    clock->minute.angle -= 90;
+
+    clock->second.angle = (timeinfo->tm_sec%60)*6.0f;
+    clock->second.angle -= 90;
+}
+
+// Draw analog clock
+// Parameter: position, refers to center position
+static void DrawClockAnalog(Clock clock, Vector2 position)
+{
+        // Draw clock base
+        DrawCircleV(position, clock.second.length + 40.0f, LIGHTGRAY);
+        DrawCircleV(position, 12.0f, GRAY);
+
+        // Draw clock minutes/seconds lines
+        for (int i = 0; i < 60; i++)
+        {
+            DrawLineEx((Vector2){ position.x + (clock.second.length + ((i%5)? 10 : 6))*cosf((6.0f*i - 90.0f)*DEG2RAD),
+                position.y + (clock.second.length + ((i%5)? 10 : 6))*sinf((6.0f*i - 90.0f)*DEG2RAD) },
+                (Vector2){ position.x + (clock.second.length + 20)*cosf((6.0f*i - 90.0f)*DEG2RAD),
+                position.y + (clock.second.length + 20)*sinf((6.0f*i - 90.0f)*DEG2RAD) }, ((i%5)? 1.0f : 3.0f), DARKGRAY);
+
+            // Draw seconds numbers
+            //DrawText(TextFormat("%02i", i), centerPosition.x + (clock.second.length + 50)*cosf((6.0f*i - 90.0f)*DEG2RAD) - 10/2,
+            //    centerPosition.y + (clock.second.length + 50)*sinf((6.0f*i - 90.0f)*DEG2RAD) - 10/2, 10, GRAY);
+        }
+
+        // Draw hand seconds
+        DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.second.length, (float)clock.second.thickness },
+            (Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color);
+
+        // Draw hand minutes
+        DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.minute.length, (float)clock.minute.thickness },
+            (Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color);
+
+        // Draw hand hours
+        DrawRectanglePro((Rectangle){ position.x, position.y, (float)clock.hour.length, (float)clock.hour.thickness },
+            (Vector2){ 0.0f, clock.hour.thickness/2.0f }, clock.hour.angle, clock.hour.color);
+}
+
+// Draw digital clock
+// PARAM: position, refers to top-left corner
+static void DrawClockDigital(Clock clock, Vector2 position)
+{
+    // Draw clock using custom 7-segments display (made of shapes)
+    DrawDisplayValue((Vector2){ position.x, position.y }, clock.hour.value/10, RED, Fade(LIGHTGRAY, 0.3f));
+    DrawDisplayValue((Vector2){ position.x + 120, position.y }, clock.hour.value%10, RED, Fade(LIGHTGRAY, 0.3f));
+
+    DrawCircle((int)position.x + 240, (int)position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
+    DrawCircle((int)position.x + 240, (int)position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
+
+    DrawDisplayValue((Vector2){ position.x + 260, position.y }, clock.minute.value/10, RED, Fade(LIGHTGRAY, 0.3f));
+    DrawDisplayValue((Vector2){ position.x + 380, position.y }, clock.minute.value%10, RED, Fade(LIGHTGRAY, 0.3f));
+
+    DrawCircle((int)position.x + 500, (int)position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
+    DrawCircle((int)position.x + 500, (int)position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f));
+
+    DrawDisplayValue((Vector2){ position.x + 520, position.y }, clock.second.value/10, RED, Fade(LIGHTGRAY, 0.3f));
+    DrawDisplayValue((Vector2){ position.x + 640, position.y }, clock.second.value%10, RED, Fade(LIGHTGRAY, 0.3f));
+}
+
+// Draw 7-segment display with value
+static void DrawDisplayValue(Vector2 position, int value, Color colorOn, Color colorOff)
+{
+    switch (value)
+    {
+        case 0: Draw7SDisplay(position, 0b00111111, colorOn, colorOff); break;
+        case 1: Draw7SDisplay(position, 0b00000110, colorOn, colorOff); break;
+        case 2: Draw7SDisplay(position, 0b01011011, colorOn, colorOff); break;
+        case 3: Draw7SDisplay(position, 0b01001111, colorOn, colorOff); break;
+        case 4: Draw7SDisplay(position, 0b01100110, colorOn, colorOff); break;
+        case 5: Draw7SDisplay(position, 0b01101101, colorOn, colorOff); break;
+        case 6: Draw7SDisplay(position, 0b01111101, colorOn, colorOff); break;
+        case 7: Draw7SDisplay(position, 0b00000111, colorOn, colorOff); break;
+        case 8: Draw7SDisplay(position, 0b01111111, colorOn, colorOff); break;
+        case 9: Draw7SDisplay(position, 0b01101111, colorOn, colorOff); break;
+        default: break;
+    }
+}
+
+// Draw seven segments display
+// Parameter: position, refers to top-left corner of display
+// Parameter: segments, defines in binary the segments to be activated
+static void Draw7SDisplay(Vector2 position, char segments, Color colorOn, Color colorOff)
+{
+    int segmentLen = 60;
+    int segmentThick = 20;
+    float offsetYAdjust = segmentThick*0.3f; // HACK: Adjust gap space between segment limits
+
+    // Segment A
+    DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + segmentThick },
+        segmentLen, segmentThick, false, (segments & 0b00000001)? colorOn : colorOff);
+    // Segment B
+    DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
+        segmentLen, segmentThick, true, (segments & 0b00000010)? colorOn : colorOff);
+    // Segment C
+    DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
+        segmentLen, segmentThick, true, (segments & 0b00000100)? colorOn : colorOff);
+    // Segment D
+    DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 5*segmentThick + 2*segmentLen - 4*offsetYAdjust },
+        segmentLen, segmentThick, false, (segments & 0b00001000)? colorOn : colorOff);
+    // Segment E
+    DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust },
+        segmentLen, segmentThick, true, (segments & 0b00010000)? colorOn : colorOff);
+    // Segment F
+    DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust },
+        segmentLen, segmentThick, true, (segments & 0b00100000)? colorOn : colorOff);
+    // Segment G
+    DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 3*segmentThick + segmentLen - 2*offsetYAdjust },
+        segmentLen, segmentThick, false, (segments & 0b01000000)? colorOn : colorOff);
+}
+
+// Draw one 7-segment display segment, horizontal or vertical
+static void DrawDisplaySegment(Vector2 center, int length, int thick, bool vertical, Color color)
+{
+    if (!vertical)
+    {
+        // Horizontal segment points
+        /*
+             3___________________________5
+            /                             \
+           /1             x               6\
+           \                               /
+            \2___________________________4/
+        */
+        Vector2 segmentPointsH[6] = {
+            (Vector2){ center.x - length/2.0f - thick/2.0f,  center.y },  // Point 1
+            (Vector2){ center.x - length/2.0f,  center.y + thick/2.0f },  // Point 2
+            (Vector2){ center.x - length/2.0f, center.y - thick/2.0f },   // Point 3
+            (Vector2){ center.x + length/2.0f,  center.y + thick/2.0f },  // Point 4
+            (Vector2){ center.x + length/2.0f,  center.y - thick/2.0f },  // Point 5
+            (Vector2){ center.x + length/2.0f + thick/2.0f,  center.y },  // Point 6
+        };
+
+        DrawTriangleStrip(segmentPointsH, 6, color);
+    }
+    else
+    {
+        // Vertical segment points
+        Vector2 segmentPointsV[6] = {
+            (Vector2){ center.x,  center.y - length/2.0f - thick/2.0f },  // Point 1
+            (Vector2){ center.x - thick/2.0f,  center.y - length/2.0f },  // Point 2
+            (Vector2){ center.x + thick/2.0f, center.y - length/2.0f },   // Point 3
+            (Vector2){ center.x - thick/2.0f,  center.y + length/2.0f },  // Point 4
+            (Vector2){ center.x + thick/2.0f,  center.y + length/2.0f },  // Point 5
+            (Vector2){ center.x,  center.y + length/2 + thick/2.0f },     // Point 6
+        };
+
+        DrawTriangleStrip(segmentPointsV, 6, color);
+    }
+}
diff --git a/raylib/examples/shapes/shapes_double_pendulum.c b/raylib/examples/shapes/shapes_double_pendulum.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_double_pendulum.c
@@ -0,0 +1,171 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - double pendulum
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by JoeCheong (@Joecheong2006) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 JoeCheong (@Joecheong2006)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <math.h>       // Required for: sin(), cos(), PI
+
+// Constant for Simulation
+#define SIMULATION_STEPS 30
+#define G 9.81f
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static Vector2 CalculatePendulumEndPoint(float l, float theta);
+static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_HIGHDPI);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - double pendulum");
+
+    // Simulation Parameters
+    float l1 = 15.0f, m1 = 0.2f, theta1 = DEG2RAD*170, w1 = 0;
+    float l2 = 15.0f, m2 = 0.1f, theta2 = DEG2RAD*0, w2 = 0;
+    float lengthScaler = 0.1f;
+    float totalM = m1 + m2;
+
+    Vector2 previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2);
+    previousPosition.x += (screenWidth/2);
+    previousPosition.y += (screenHeight/2 - 100);
+
+    // Scale length
+    float L1 = l1*lengthScaler;
+    float L2 = l2*lengthScaler;
+
+    // Draw parameters
+    float lineThick = 20, trailThick = 2;
+    float fateAlpha = 0.01f;
+
+    // Create framebuffer
+    RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
+    SetTextureFilter(target.texture, TEXTURE_FILTER_BILINEAR);
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float dt = GetFrameTime();
+        float step = dt/SIMULATION_STEPS, step2 = step*step;
+
+        // Update Physics - larger steps = better approximation
+        for (int i = 0; i < SIMULATION_STEPS; i++)
+        {
+            float delta = theta1 - theta2;
+            float sinD = sinf(delta), cosD = cosf(delta), cos2D = cosf(2*delta);
+            float ww1 = w1*w1, ww2 = w2*w2;
+
+            // Calculate a1
+            float a1 = (-G*(2*m1 + m2)*sinf(theta1)
+                         - m2*G*sinf(theta1 - 2*theta2)
+                         - 2*sinD*m2*(ww2*L2 + ww1*L1*cosD))
+                        /(L1*(2*m1 + m2 - m2*cos2D));
+
+            // Calculate a2
+            float a2 = (2*sinD*(ww1*L1*totalM
+                         + G*totalM*cosf(theta1)
+                         + ww2*L2*m2*cosD))
+                        /(L2*(2*m1 + m2 - m2*cos2D));
+
+            // Update thetas
+            theta1 += w1*step + 0.5f*a1*step2;
+            theta2 += w2*step + 0.5f*a2*step2;
+
+            // Update omegas
+            w1 += a1*step;
+            w2 += a2*step;
+        }
+
+        // Calculate position
+        Vector2 currentPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2);
+        currentPosition.x += screenWidth/2;
+        currentPosition.y += screenHeight/2 - 100;
+
+        // Draw to render texture
+        BeginTextureMode(target);
+            // Draw a transparent rectangle - smaller alpha = longer trails
+            DrawRectangle(0, 0, screenWidth, screenHeight, Fade(BLACK, fateAlpha));
+
+            // Draw trail
+            DrawCircleV(previousPosition, trailThick, RED);
+            DrawLineEx(previousPosition, currentPosition, trailThick*2, RED);
+        EndTextureMode();
+
+        // Update previous position
+        previousPosition = currentPosition;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(BLACK);
+
+            // Draw trails texture
+            DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2){ 0, 0 }, WHITE);
+
+            // Draw double pendulum
+            DrawRectanglePro((Rectangle){ screenWidth/2.0f, screenHeight/2.0f - 100, 10*l1, lineThick },
+                (Vector2){0, lineThick*0.5f}, 90 - RAD2DEG*theta1, RAYWHITE);
+
+            Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1);
+            DrawRectanglePro((Rectangle){ screenWidth/2.0f + endpoint1.x, screenHeight/2.0f - 100 + endpoint1.y, 10*l2, lineThick },
+                (Vector2){0, lineThick*0.5f}, 90 - RAD2DEG*theta2, RAYWHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTexture(target);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Calculate pendulum end point
+static Vector2 CalculatePendulumEndPoint(float l, float theta)
+{
+    return (Vector2){ 10*l*sinf(theta), 10*l*cosf(theta) };
+}
+
+// Calculate double pendulum end point
+static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2)
+{
+    Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1);
+    Vector2 endpoint2 = CalculatePendulumEndPoint(l2, theta2);
+    return (Vector2){ endpoint1.x + endpoint2.x, endpoint1.y + endpoint2.y };
+}
diff --git a/raylib/examples/shapes/shapes_draw_circle_sector.c b/raylib/examples/shapes/shapes_draw_circle_sector.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_draw_circle_sector.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - draw circle sector (with gui options)
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 2.5
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2018-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include <raylib.h>
-
-#define RAYGUI_IMPLEMENTATION
-#include "raygui.h"                 // Required for GUI controls
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw circle sector");
-
-    Vector2 center = {(GetScreenWidth() - 300)/2.0f, GetScreenHeight()/2.0f };
-
-    float outerRadius = 180.0f;
-    float startAngle = 0.0f;
-    float endAngle = 180.0f;
-    float segments = 10.0f;
-    float minSegments = 4;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // NOTE: All variables update happens inside GUI control functions
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
-            DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
-
-            DrawCircleSector(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f));
-            DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.6f));
-
-            // Draw GUI controls
-            //------------------------------------------------------------------------------
-            GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, 0, 720);
-            GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, 0, 720);
-
-            GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
-            GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
-            //------------------------------------------------------------------------------
-
-            minSegments = truncf(ceilf((endAngle - startAngle) / 90));
-            DrawText(TextFormat("MODE: %s", (segments >= minSegments)? "MANUAL" : "AUTO"), 600, 200, 10, (segments >= minSegments)? MAROON : DARKGRAY);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_draw_rectangle_rounded.c b/raylib/examples/shapes/shapes_draw_rectangle_rounded.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_draw_rectangle_rounded.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - draw rectangle rounded (with gui options)
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 2.5
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2018-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include <raylib.h>
-
-#define RAYGUI_IMPLEMENTATION
-#include "raygui.h"                 // Required for GUI controls
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded");
-
-    float roundness = 0.2f;
-    float width = 200.0f;
-    float height = 100.0f;
-    float segments = 0.0f;
-    float lineThick = 1.0f;
-
-    bool drawRect = false;
-    bool drawRoundedRect = true;
-    bool drawRoundedLines = false;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        Rectangle rec = { ((float)GetScreenWidth() - width - 250)/2, (GetScreenHeight() - height)/2.0f, (float)width, (float)height };
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawLine(560, 0, 560, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
-            DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
-
-            if (drawRect) DrawRectangleRec(rec, Fade(GOLD, 0.6f));
-            if (drawRoundedRect) DrawRectangleRounded(rec, roundness, (int)segments, Fade(MAROON, 0.2f));
-            if (drawRoundedLines) DrawRectangleRoundedLinesEx(rec, roundness, (int)segments, lineThick, Fade(MAROON, 0.4f));
-
-            // Draw GUI controls
-            //------------------------------------------------------------------------------
-            GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", TextFormat("%.2f", width), &width, 0, (float)GetScreenWidth() - 300);
-            GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", TextFormat("%.2f", height), &height, 0, (float)GetScreenHeight() - 50);
-            GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", TextFormat("%.2f", roundness), &roundness, 0.0f, 1.0f);
-            GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", TextFormat("%.2f", lineThick), &lineThick, 0, 20);
-            GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 60);
-
-            GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect);
-            GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines);
-            GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", &drawRect);
-            //------------------------------------------------------------------------------
-
-            DrawText(TextFormat("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 640, 280, 10, (segments >= 4)? MAROON : DARKGRAY);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_draw_ring.c b/raylib/examples/shapes/shapes_draw_ring.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_draw_ring.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - draw ring (with gui options)
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 2.5
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2018-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include <raylib.h>
-
-#define RAYGUI_IMPLEMENTATION
-#include "raygui.h"                 // Required for GUI controls
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw ring");
-
-    Vector2 center = {(GetScreenWidth() - 300)/2.0f, GetScreenHeight()/2.0f };
-
-    float innerRadius = 80.0f;
-    float outerRadius = 190.0f;
-
-    float startAngle = 0.0f;
-    float endAngle = 360.0f;
-    float segments = 0.0f;
-
-    bool drawRing = true;
-    bool drawRingLines = false;
-    bool drawCircleLines = false;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // NOTE: All variables update happens inside GUI control functions
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
-            DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
-
-            if (drawRing) DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f));
-            if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f));
-            if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f));
-
-            // Draw GUI controls
-            //------------------------------------------------------------------------------
-            GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, -450, 450);
-            GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, -450, 450);
-
-            GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", TextFormat("%.2f", innerRadius), &innerRadius, 0, 100);
-            GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
-
-            GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
-
-            GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing);
-            GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines);
-            GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", &drawCircleLines);
-            //------------------------------------------------------------------------------
-
-            int minSegments = (int)ceilf((endAngle - startAngle)/90);
-            DrawText(TextFormat("MODE: %s", (segments >= minSegments)? "MANUAL" : "AUTO"), 600, 270, 10, (segments >= minSegments)? MAROON : DARKGRAY);
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_easings_ball.c b/raylib/examples/shapes/shapes_easings_ball.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_easings_ball.c
@@ -0,0 +1,117 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - easings ball
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 2.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "reasings.h"                // Required for easing functions
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball");
+
+    // Ball variable value to be animated with easings
+    int ballPositionX = -100;
+    int ballRadius = 20;
+    float ballAlpha = 0.0f;
+
+    int state = 0;
+    int framesCounter = 0;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (state == 0)             // Move ball position X with easing
+        {
+            framesCounter++;
+            ballPositionX = (int)EaseElasticOut((float)framesCounter, -100, screenWidth/2.0f + 100, 120);
+
+            if (framesCounter >= 120)
+            {
+                framesCounter = 0;
+                state = 1;
+            }
+        }
+        else if (state == 1)        // Increase ball radius with easing
+        {
+            framesCounter++;
+            ballRadius = (int)EaseElasticIn((float)framesCounter, 20, 500, 200);
+
+            if (framesCounter >= 200)
+            {
+                framesCounter = 0;
+                state = 2;
+            }
+        }
+        else if (state == 2)        // Change ball alpha with easing (background color blending)
+        {
+            framesCounter++;
+            ballAlpha = EaseCubicOut((float)framesCounter, 0.0f, 1.0f, 200);
+
+            if (framesCounter >= 200)
+            {
+                framesCounter = 0;
+                state = 3;
+            }
+        }
+        else if (state == 3)        // Reset state to play again
+        {
+            if (IsKeyPressed(KEY_ENTER))
+            {
+                // Reset required variables to play again
+                ballPositionX = -100;
+                ballRadius = 20;
+                ballAlpha = 0.0f;
+                state = 0;
+            }
+        }
+
+        if (IsKeyPressed(KEY_R)) framesCounter = 0;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            if (state >= 2) DrawRectangle(0, 0, screenWidth, screenHeight, GREEN);
+            DrawCircle(ballPositionX, 200, (float)ballRadius, Fade(RED, 1.0f - ballAlpha));
+
+            if (state == 3) DrawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_easings_ball_anim.c b/raylib/examples/shapes/shapes_easings_ball_anim.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_easings_ball_anim.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - easings ball anim
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 2.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "reasings.h"                // Required for easing functions
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings ball anim");
-
-    // Ball variable value to be animated with easings
-    int ballPositionX = -100;
-    int ballRadius = 20;
-    float ballAlpha = 0.0f;
-
-    int state = 0;
-    int framesCounter = 0;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        if (state == 0)             // Move ball position X with easing
-        {
-            framesCounter++;
-            ballPositionX = (int)EaseElasticOut((float)framesCounter, -100, screenWidth/2.0f + 100, 120);
-
-            if (framesCounter >= 120)
-            {
-                framesCounter = 0;
-                state = 1;
-            }
-        }
-        else if (state == 1)        // Increase ball radius with easing
-        {
-            framesCounter++;
-            ballRadius = (int)EaseElasticIn((float)framesCounter, 20, 500, 200);
-
-            if (framesCounter >= 200)
-            {
-                framesCounter = 0;
-                state = 2;
-            }
-        }
-        else if (state == 2)        // Change ball alpha with easing (background color blending)
-        {
-            framesCounter++;
-            ballAlpha = EaseCubicOut((float)framesCounter, 0.0f, 1.0f, 200);
-
-            if (framesCounter >= 200)
-            {
-                framesCounter = 0;
-                state = 3;
-            }
-        }
-        else if (state == 3)        // Reset state to play again
-        {
-            if (IsKeyPressed(KEY_ENTER))
-            {
-                // Reset required variables to play again
-                ballPositionX = -100;
-                ballRadius = 20;
-                ballAlpha = 0.0f;
-                state = 0;
-            }
-        }
-
-        if (IsKeyPressed(KEY_R)) framesCounter = 0;
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            if (state >= 2) DrawRectangle(0, 0, screenWidth, screenHeight, GREEN);
-            DrawCircle(ballPositionX, 200, (float)ballRadius, Fade(RED, 1.0f - ballAlpha));
-
-            if (state == 3) DrawText("PRESS [ENTER] TO PLAY AGAIN!", 240, 200, 20, BLACK);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_easings_box.c b/raylib/examples/shapes/shapes_easings_box.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_easings_box.c
@@ -0,0 +1,143 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - easings box
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 2.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "reasings.h"            // Required for easing functions
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings box");
+
+    // Box variables to be animated with easings
+    Rectangle rec = { GetScreenWidth()/2.0f, -100, 100, 100 };
+    float rotation = 0.0f;
+    float alpha = 1.0f;
+
+    int state = 0;
+    int framesCounter = 0;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        switch (state)
+        {
+            case 0:     // Move box down to center of screen
+            {
+                framesCounter++;
+
+                // NOTE: Remember that 3rd parameter of easing function refers to
+                // desired value variation, do not confuse it with expected final value!
+                rec.y = EaseElasticOut((float)framesCounter, -100, GetScreenHeight()/2.0f + 100, 120);
+
+                if (framesCounter >= 120)
+                {
+                    framesCounter = 0;
+                    state = 1;
+                }
+            } break;
+            case 1:     // Scale box to an horizontal bar
+            {
+                framesCounter++;
+                rec.height = EaseBounceOut((float)framesCounter, 100, -90, 120);
+                rec.width = EaseBounceOut((float)framesCounter, 100, (float)GetScreenWidth(), 120);
+
+                if (framesCounter >= 120)
+                {
+                    framesCounter = 0;
+                    state = 2;
+                }
+            } break;
+            case 2:     // Rotate horizontal bar rectangle
+            {
+                framesCounter++;
+                rotation = EaseQuadOut((float)framesCounter, 0.0f, 270.0f, 240);
+
+                if (framesCounter >= 240)
+                {
+                    framesCounter = 0;
+                    state = 3;
+                }
+            } break;
+            case 3:     // Increase bar size to fill all screen
+            {
+                framesCounter++;
+                rec.height = EaseCircOut((float)framesCounter, 10, (float)GetScreenWidth(), 120);
+
+                if (framesCounter >= 120)
+                {
+                    framesCounter = 0;
+                    state = 4;
+                }
+            } break;
+            case 4:     // Fade out animation
+            {
+                framesCounter++;
+                alpha = EaseSineOut((float)framesCounter, 1.0f, -1.0f, 160);
+
+                if (framesCounter >= 160)
+                {
+                    framesCounter = 0;
+                    state = 5;
+                }
+            } break;
+            default: break;
+        }
+
+        // Reset animation at any moment
+        if (IsKeyPressed(KEY_SPACE))
+        {
+            rec = (Rectangle){ GetScreenWidth()/2.0f, -100, 100, 100 };
+            rotation = 0.0f;
+            alpha = 1.0f;
+            state = 0;
+            framesCounter = 0;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawRectanglePro(rec, (Vector2){ rec.width/2, rec.height/2 }, rotation, Fade(BLACK, alpha));
+
+            DrawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_easings_box_anim.c b/raylib/examples/shapes/shapes_easings_box_anim.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_easings_box_anim.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - easings box anim
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 2.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "reasings.h"            // Required for easing functions
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings box anim");
-
-    // Box variables to be animated with easings
-    Rectangle rec = { GetScreenWidth()/2.0f, -100, 100, 100 };
-    float rotation = 0.0f;
-    float alpha = 1.0f;
-
-    int state = 0;
-    int framesCounter = 0;
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        switch (state)
-        {
-            case 0:     // Move box down to center of screen
-            {
-                framesCounter++;
-
-                // NOTE: Remember that 3rd parameter of easing function refers to
-                // desired value variation, do not confuse it with expected final value!
-                rec.y = EaseElasticOut((float)framesCounter, -100, GetScreenHeight()/2.0f + 100, 120);
-
-                if (framesCounter >= 120)
-                {
-                    framesCounter = 0;
-                    state = 1;
-                }
-            } break;
-            case 1:     // Scale box to an horizontal bar
-            {
-                framesCounter++;
-                rec.height = EaseBounceOut((float)framesCounter, 100, -90, 120);
-                rec.width = EaseBounceOut((float)framesCounter, 100, (float)GetScreenWidth(), 120);
-
-                if (framesCounter >= 120)
-                {
-                    framesCounter = 0;
-                    state = 2;
-                }
-            } break;
-            case 2:     // Rotate horizontal bar rectangle
-            {
-                framesCounter++;
-                rotation = EaseQuadOut((float)framesCounter, 0.0f, 270.0f, 240);
-
-                if (framesCounter >= 240)
-                {
-                    framesCounter = 0;
-                    state = 3;
-                }
-            } break;
-            case 3:     // Increase bar size to fill all screen
-            {
-                framesCounter++;
-                rec.height = EaseCircOut((float)framesCounter, 10, (float)GetScreenWidth(), 120);
-
-                if (framesCounter >= 120)
-                {
-                    framesCounter = 0;
-                    state = 4;
-                }
-            } break;
-            case 4:     // Fade out animation
-            {
-                framesCounter++;
-                alpha = EaseSineOut((float)framesCounter, 1.0f, -1.0f, 160);
-
-                if (framesCounter >= 160)
-                {
-                    framesCounter = 0;
-                    state = 5;
-                }
-            } break;
-            default: break;
-        }
-
-        // Reset animation at any moment
-        if (IsKeyPressed(KEY_SPACE))
-        {
-            rec = (Rectangle){ GetScreenWidth()/2.0f, -100, 100, 100 };
-            rotation = 0.0f;
-            alpha = 1.0f;
-            state = 0;
-            framesCounter = 0;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawRectanglePro(rec, (Vector2){ rec.width/2, rec.height/2 }, rotation, Fade(BLACK, alpha));
-
-            DrawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, GetScreenHeight() - 25, 20, LIGHTGRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_easings_rectangle_array.c b/raylib/examples/shapes/shapes_easings_rectangle_array.c
deleted file mode 100644
--- a/raylib/examples/shapes/shapes_easings_rectangle_array.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - easings rectangle array
-*
-*   NOTE: This example requires 'easings.h' library, provided on raylib/src. Just copy
-*   the library to same directory as example or make sure it's available on include path.
-*
-*   Example originally created with raylib 2.0, last time updated with raylib 2.5
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "reasings.h"            // Required for easing functions
-
-#define RECS_WIDTH              50
-#define RECS_HEIGHT             50
-
-#define MAX_RECS_X              800/RECS_WIDTH
-#define MAX_RECS_Y              450/RECS_HEIGHT
-
-#define PLAY_TIME_IN_FRAMES     240                 // At 60 fps = 4 seconds
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangle array");
-
-    Rectangle recs[MAX_RECS_X*MAX_RECS_Y] = { 0 };
-
-    for (int y = 0; y < MAX_RECS_Y; y++)
-    {
-        for (int x = 0; x < MAX_RECS_X; x++)
-        {
-            recs[y*MAX_RECS_X + x].x = RECS_WIDTH/2.0f + RECS_WIDTH*x;
-            recs[y*MAX_RECS_X + x].y = RECS_HEIGHT/2.0f + RECS_HEIGHT*y;
-            recs[y*MAX_RECS_X + x].width = RECS_WIDTH;
-            recs[y*MAX_RECS_X + x].height = RECS_HEIGHT;
-        }
-    }
-
-    float rotation = 0.0f;
-    int framesCounter = 0;
-    int state = 0;                  // Rectangles animation state: 0-Playing, 1-Finished
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        if (state == 0)
-        {
-            framesCounter++;
-
-            for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
-            {
-                recs[i].height = EaseCircOut((float)framesCounter, RECS_HEIGHT, -RECS_HEIGHT, PLAY_TIME_IN_FRAMES);
-                recs[i].width = EaseCircOut((float)framesCounter, RECS_WIDTH, -RECS_WIDTH, PLAY_TIME_IN_FRAMES);
-
-                if (recs[i].height < 0) recs[i].height = 0;
-                if (recs[i].width < 0) recs[i].width = 0;
-
-                if ((recs[i].height == 0) && (recs[i].width == 0)) state = 1;   // Finish playing
-
-                rotation = EaseLinearIn((float)framesCounter, 0.0f, 360.0f, PLAY_TIME_IN_FRAMES);
-            }
-        }
-        else if ((state == 1) && IsKeyPressed(KEY_SPACE))
-        {
-            // When animation has finished, press space to restart
-            framesCounter = 0;
-
-            for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
-            {
-                recs[i].height = RECS_HEIGHT;
-                recs[i].width = RECS_WIDTH;
-            }
-
-            state = 0;
-        }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            if (state == 0)
-            {
-                for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
-                {
-                    DrawRectanglePro(recs[i], (Vector2){ recs[i].width/2, recs[i].height/2 }, rotation, RED);
-                }
-            }
-            else if (state == 1) DrawText("PRESS [SPACE] TO PLAY AGAIN!", 240, 200, 20, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/shapes/shapes_easings_rectangles.c b/raylib/examples/shapes/shapes_easings_rectangles.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_easings_rectangles.c
@@ -0,0 +1,125 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - easings rectangles
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   NOTE: This example requires 'easings.h' library, provided on raylib/src. Just copy
+*   the library to same directory as example or make sure it's available on include path
+*
+*   Example originally created with raylib 2.0, last time updated with raylib 2.5
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "reasings.h"            // Required for easing functions
+
+#define RECS_WIDTH              50
+#define RECS_HEIGHT             50
+
+#define MAX_RECS_X              800/RECS_WIDTH
+#define MAX_RECS_Y              450/RECS_HEIGHT
+
+#define PLAY_TIME_IN_FRAMES     240                 // At 60 fps = 4 seconds
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings rectangles");
+
+    Rectangle recs[MAX_RECS_X*MAX_RECS_Y] = { 0 };
+
+    for (int y = 0; y < MAX_RECS_Y; y++)
+    {
+        for (int x = 0; x < MAX_RECS_X; x++)
+        {
+            recs[y*MAX_RECS_X + x].x = RECS_WIDTH/2.0f + RECS_WIDTH*x;
+            recs[y*MAX_RECS_X + x].y = RECS_HEIGHT/2.0f + RECS_HEIGHT*y;
+            recs[y*MAX_RECS_X + x].width = RECS_WIDTH;
+            recs[y*MAX_RECS_X + x].height = RECS_HEIGHT;
+        }
+    }
+
+    float rotation = 0.0f;
+    int framesCounter = 0;
+    int state = 0;                  // Rectangles animation state: 0-Playing, 1-Finished
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (state == 0)
+        {
+            framesCounter++;
+
+            for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
+            {
+                recs[i].height = EaseCircOut((float)framesCounter, RECS_HEIGHT, -RECS_HEIGHT, PLAY_TIME_IN_FRAMES);
+                recs[i].width = EaseCircOut((float)framesCounter, RECS_WIDTH, -RECS_WIDTH, PLAY_TIME_IN_FRAMES);
+
+                if (recs[i].height < 0) recs[i].height = 0;
+                if (recs[i].width < 0) recs[i].width = 0;
+
+                if ((recs[i].height == 0) && (recs[i].width == 0)) state = 1;   // Finish playing
+
+                rotation = EaseLinearIn((float)framesCounter, 0.0f, 360.0f, PLAY_TIME_IN_FRAMES);
+            }
+        }
+        else if ((state == 1) && IsKeyPressed(KEY_SPACE))
+        {
+            // When animation has finished, press space to restart
+            framesCounter = 0;
+
+            for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
+            {
+                recs[i].height = RECS_HEIGHT;
+                recs[i].width = RECS_WIDTH;
+            }
+
+            state = 0;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            if (state == 0)
+            {
+                for (int i = 0; i < MAX_RECS_X*MAX_RECS_Y; i++)
+                {
+                    DrawRectanglePro(recs[i], (Vector2){ recs[i].width/2, recs[i].height/2 }, rotation, RED);
+                }
+            }
+            else if (state == 1) DrawText("PRESS [SPACE] TO PLAY AGAIN!", 240, 200, 20, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_following_eyes.c b/raylib/examples/shapes/shapes_following_eyes.c
--- a/raylib/examples/shapes/shapes_following_eyes.c
+++ b/raylib/examples/shapes/shapes_following_eyes.c
@@ -2,12 +2,14 @@
 *
 *   raylib [shapes] example - following eyes
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/shapes/shapes_hilbert_curve.c b/raylib/examples/shapes/shapes_hilbert_curve.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_hilbert_curve.c
@@ -0,0 +1,196 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - hilbert curve
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Hamza RAHAL (@hmz-rhl) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Hamza RAHAL (@hmz-rhl)
+*
+********************************************************************************************/
+
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+#include <stdlib.h>     // Required for: calloc(), free()
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount);
+static void UnloadHilbertPath(Vector2 *hilbertPath);
+static Vector2 ComputeHilbertStep(int order, int index);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve");
+
+    int order = 2;
+    float size = GetScreenHeight();
+    int strokeCount = 0;
+    Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount);
+    
+    int prevOrder = order;
+    int prevSize = (int)size;       // NOTE: Size from slider is float but for comparison we use int
+    int counter = 0;
+    float thick = 2.0f;
+    bool animate = true;
+    
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    //--------------------------------------------------------------------------------------
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Check if order or size have changed to regenerate
+        // NOTE: Size from slider is float but for comparison we use int
+        if ((prevOrder != order) || (prevSize != (int)size))
+        {
+            UnloadHilbertPath(hilbertPath);
+            hilbertPath = LoadHilbertPath(order, size, &strokeCount);
+            
+            if (animate) counter = 0;
+            else counter = strokeCount;
+            
+            prevOrder = order;
+            prevSize = size;
+        }
+        //----------------------------------------------------------------------------------
+        
+        // Draw
+        //--------------------------------------------------------------------------
+        BeginDrawing();
+        
+            ClearBackground(RAYWHITE);
+
+            if (counter < strokeCount)
+            {
+                // Draw Hilbert path animation, one stroke every frame
+                for (int i = 1; i <= counter; i++)
+                {
+                    DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f));
+                }
+                
+                counter += 1;
+            }
+            else
+            {
+                // Draw full Hilbert path
+                for (int i = 1; i < strokeCount; i++)
+                {
+                    DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f));
+                }
+            }
+        
+            // Draw UI using raygui
+            GuiCheckBox((Rectangle){ 450, 50, 20, 20 }, "ANIMATE GENERATION ON CHANGE", &animate);
+            GuiSpinner((Rectangle){ 585, 100, 180, 30 }, "HILBERT CURVE ORDER:  ", &order, 2, 8, false);
+            GuiSlider((Rectangle){ 524, 150, 240, 24 }, "THICKNESS:  ", NULL, &thick, 1.0f, 10.0f);
+            GuiSlider((Rectangle){ 524, 190, 240, 24 }, "TOTAL SIZE: ", NULL, &size, 10.0f, GetScreenHeight()*1.5f);
+        
+        EndDrawing();
+        //--------------------------------------------------------------------------
+    }
+    //--------------------------------------------------------------------------------------
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadHilbertPath(hilbertPath);
+    
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+    return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Load the whole Hilbert Path (including each U and their link)
+static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount)
+{
+    int N = 1 << order;
+    float len = size/N;
+    *strokeCount = N*N;
+
+    Vector2 *hilbertPath = (Vector2 *)RL_CALLOC(*strokeCount, sizeof(Vector2));
+    
+    for (int i = 0; i < *strokeCount; i++)
+    {
+        hilbertPath[i] = ComputeHilbertStep(order, i);
+        hilbertPath[i].x = hilbertPath[i].x*len + len/2.0f;
+        hilbertPath[i].y = hilbertPath[i].y*len + len/2.0f;
+    }
+    
+    return hilbertPath;
+}
+
+// Unload Hilbert path data
+static void UnloadHilbertPath(Vector2 *hilbertPath)
+{
+    RL_FREE(hilbertPath);
+}
+
+// Compute Hilbert path U positions
+static Vector2 ComputeHilbertStep(int order, int index)
+{
+    // Hilbert points base pattern
+    static const Vector2 hilbertPoints[4] = {
+        [0] = { .x = 0, .y = 0 },
+        [1] = { .x = 0, .y = 1 },
+        [2] = { .x = 1, .y = 1 },
+        [3] = { .x = 1, .y = 0 },
+    };
+       
+    int hilbertIndex = index&3;
+    Vector2 vect = hilbertPoints[hilbertIndex];
+    float temp = 0.0f;
+    int len = 0;
+
+    for (int j = 1; j < order; j++)
+    {
+        index = index >> 2;
+        hilbertIndex = index&3;
+        len = 1 << j;
+        
+        switch (hilbertIndex)
+        {
+            case 0:
+            {
+                temp = vect.x;
+                vect.x = vect.y;
+                vect.y = temp;
+            } break;
+            case 2: vect.x += len;
+            case 1: vect.y += len; break;
+            case 3:
+            {
+                temp = len - 1 - vect.x;
+                vect.x = 2*len - 1 - vect.y;
+                vect.y = temp;
+            } break;
+            default: break;
+        }
+    }
+    
+    return vect;
+}
diff --git a/raylib/examples/shapes/shapes_kaleidoscope.c b/raylib/examples/shapes/shapes_kaleidoscope.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_kaleidoscope.c
@@ -0,0 +1,172 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - kaleidoscope
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Hugo ARNAL (@hugoarnal) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Hugo ARNAL (@hugoarnal) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <string.h>
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+#include "raymath.h"
+
+#define MAX_DRAW_LINES  8192
+
+// Line data type
+typedef struct {
+    Vector2 start;
+    Vector2 end;
+} Line;
+
+// Lines array as a global static variable to be stored
+// in heap and avoid potential stack overflow (on Web platform)
+static Line lines[MAX_DRAW_LINES] = { 0 };
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - kaleidoscope");
+
+    // Line drawing properties
+    int symmetry = 6;
+    float angle = 360.0f/(float)symmetry;
+    float thickness = 3.0f;
+    Rectangle resetButtonRec = { screenWidth - 55.0f, 5.0f, 50, 25 };
+    Rectangle backButtonRec = { screenWidth - 55.0f, screenHeight - 30.0f, 25, 25 };
+    Rectangle nextButtonRec = { screenWidth - 30.0f, screenHeight - 30.0f, 25, 25 };
+    Vector2 mousePos = { 0 };
+    Vector2 prevMousePos = { 0 };
+    Vector2 scaleVector = { 1.0f, -1.0f };
+    Vector2 offset = { (float)screenWidth/2.0f, (float)screenHeight/2.0f };
+
+    Camera2D camera = { 0 };
+    camera.target = (Vector2){ 0 };
+    camera.offset = offset;
+    camera.rotation = 0.0f;
+    camera.zoom = 1.0f;
+
+    int currentLineCounter = 0;
+    int totalLineCounter = 0;
+    int resetButtonClicked = false;
+    int backButtonClicked = false;
+    int nextButtonClicked = false;
+
+    SetTargetFPS(20);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        prevMousePos = mousePos;
+        mousePos = GetMousePosition();
+
+        Vector2 lineStart = Vector2Subtract(mousePos, offset);
+        Vector2 lineEnd = Vector2Subtract(prevMousePos, offset);
+
+        if (
+            IsMouseButtonDown(MOUSE_LEFT_BUTTON)
+            && (CheckCollisionPointRec(mousePos, resetButtonRec) == false)
+            && (CheckCollisionPointRec(mousePos, backButtonRec) == false)
+            && (CheckCollisionPointRec(mousePos, nextButtonRec) == false)
+        )
+        {
+            for (int s = 0; (s < symmetry) && (totalLineCounter < (MAX_DRAW_LINES - 1)); s++)
+            {
+                lineStart = Vector2Rotate(lineStart, angle*DEG2RAD);
+                lineEnd = Vector2Rotate(lineEnd, angle*DEG2RAD);
+
+                // Store mouse line
+                lines[totalLineCounter].start = lineStart;
+                lines[totalLineCounter].end = lineEnd;
+
+                // Store reflective line
+                lines[totalLineCounter + 1].start = Vector2Multiply(lineStart, scaleVector);
+                lines[totalLineCounter + 1].end = Vector2Multiply(lineEnd, scaleVector);
+
+                totalLineCounter += 2;
+                currentLineCounter = totalLineCounter;
+            }
+        }
+
+        if (resetButtonClicked)
+        {
+            memset(&lines, 0, sizeof(Line)*MAX_DRAW_LINES);
+            currentLineCounter = 0;
+            totalLineCounter = 0;
+        }
+
+        if (backButtonClicked && (currentLineCounter > 0))
+        {
+            currentLineCounter -= 1;
+        }
+
+        if (nextButtonClicked && (currentLineCounter < MAX_DRAW_LINES) && ((currentLineCounter + 1) <= totalLineCounter))
+        {
+            currentLineCounter += 1;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+            BeginMode2D(camera);
+
+                for (int s = 0; s < symmetry; s++)
+                {
+                    for (int i = 0; i < currentLineCounter; i += 2)
+                    {
+                        DrawLineEx(lines[i].start, lines[i].end, thickness, BLACK);
+                        DrawLineEx(lines[i + 1].start, lines[i + 1].end, thickness, BLACK);
+                    }
+                }
+
+            EndMode2D();
+
+            if ((currentLineCounter - 1) < 0) GuiDisable();
+
+            backButtonClicked = GuiButton(backButtonRec, "<");
+            GuiEnable();
+
+            if ((currentLineCounter + 1) > totalLineCounter) GuiDisable();
+
+            nextButtonClicked = GuiButton(nextButtonRec, ">");
+            GuiEnable();
+            resetButtonClicked = GuiButton(resetButtonRec, "Reset");
+
+            DrawText(TextFormat("LINES: %i/%i", currentLineCounter, MAX_DRAW_LINES), 10, screenHeight - 30, 20, MAROON);
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_lines_bezier.c b/raylib/examples/shapes/shapes_lines_bezier.c
--- a/raylib/examples/shapes/shapes_lines_bezier.c
+++ b/raylib/examples/shapes/shapes_lines_bezier.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - Cubic-bezier lines
+*   raylib [shapes] example - lines bezier
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.7, last time updated with raylib 1.7
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -24,7 +26,7 @@
     const int screenHeight = 450;
 
     SetConfigFlags(FLAG_MSAA_4X_HINT);
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - cubic-bezier lines");
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines bezier");
 
     Vector2 startPoint = { 30, 30 };
     Vector2 endPoint = { (float)screenWidth - 30, (float)screenHeight - 30 };
@@ -67,7 +69,7 @@
 
             // Draw line Cubic Bezier, in-out interpolation (easing), no control points
             DrawLineBezier(startPoint, endPoint, 4.0f, BLUE);
-            
+
             // Draw start-end spline circles with some details
             DrawCircleV(startPoint, CheckCollisionPointCircle(mouse, startPoint, 10.0f)? 14.0f : 8.0f, moveStartPoint? RED : BLUE);
             DrawCircleV(endPoint, CheckCollisionPointCircle(mouse, endPoint, 10.0f)? 14.0f : 8.0f, moveEndPoint? RED : BLUE);
diff --git a/raylib/examples/shapes/shapes_lines_drawing.c b/raylib/examples/shapes/shapes_lines_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_lines_drawing.c
@@ -0,0 +1,137 @@
+﻿/*******************************************************************************************
+*
+*   raylib [shapes] example - lines drawing
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines drawing");
+
+    // Hint text that shows before you click the screen
+    bool startText = true;
+
+    // The mouse's position on the previous frame
+    Vector2 mousePositionPrevious = GetMousePosition();
+
+    // The canvas to draw lines on
+    RenderTexture canvas = LoadRenderTexture(screenWidth, screenHeight);
+
+    // The line's thickness
+    float lineThickness = 8.0f;
+    // The lines hue (in HSV, from 0-360)
+    float lineHue = 0.0f;
+
+    // Clear the canvas to the background color
+    BeginTextureMode(canvas);
+        ClearBackground(RAYWHITE);
+    EndTextureMode();
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Disable the hint text once the user clicks
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && startText) startText = false;
+
+        // Clear the canvas when the user middle-clicks
+        if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+        {
+            BeginTextureMode(canvas);
+                ClearBackground(RAYWHITE);
+            EndTextureMode();
+        }
+
+        // Store whether the left and right buttons are down
+        bool leftButtonDown = IsMouseButtonDown(MOUSE_BUTTON_LEFT);
+        bool rightButtonDown = IsMouseButtonDown(MOUSE_BUTTON_RIGHT);
+
+        if (leftButtonDown || rightButtonDown)
+        {
+            // The color for the line
+            Color drawColor = WHITE;
+
+            if (leftButtonDown)
+            {
+                // Increase the hue value by the distance our cursor has moved since the last frame (divided by 3)
+                lineHue += Vector2Distance(mousePositionPrevious, GetMousePosition())/3.0f;
+
+                // While the hue is >=360, subtract it to bring it down into the range 0-360
+                // This is more visually accurate than resetting to zero
+                while (lineHue >= 360.0f) lineHue -= 360.0f;
+
+                // Create the final color
+                drawColor = ColorFromHSV(lineHue, 1.0f, 1.0f);
+            }
+            else if (rightButtonDown) drawColor = RAYWHITE; // Use the background color as an "eraser"
+
+            // Draw the line onto the canvas
+            BeginTextureMode(canvas);
+                // Circles act as "caps", smoothing corners
+                DrawCircleV(mousePositionPrevious, lineThickness/2.0f, drawColor);
+                DrawCircleV(GetMousePosition(), lineThickness/2.0f, drawColor);
+                DrawLineEx(mousePositionPrevious, GetMousePosition(), lineThickness, drawColor);
+            EndTextureMode();
+        }
+
+        // Update line thickness based on mousewheel
+        lineThickness += GetMouseWheelMove();
+        lineThickness = Clamp(lineThickness, 1.0, 500.0f);
+
+        // Update mouse's previous position
+        mousePositionPrevious = GetMousePosition();
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            // Draw the render texture to the screen, flipped vertically to make it appear top-side up
+            DrawTextureRec(canvas.texture, (Rectangle){ 0.0f, 0.0f, (float)canvas.texture.width,(float)-canvas.texture.height }, Vector2Zero(), WHITE);
+
+            // Draw the preview circle
+            if (!leftButtonDown) DrawCircleLinesV(GetMousePosition(), lineThickness/2.0f, (Color){ 127, 127, 127, 127 });
+
+            // Draw the hint text
+            if (startText) DrawText("try clicking and dragging!", 275, 215, 20, LIGHTGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTexture(canvas); // Unload the canvas render texture
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_logo_raylib.c b/raylib/examples/shapes/shapes_logo_raylib.c
--- a/raylib/examples/shapes/shapes_logo_raylib.c
+++ b/raylib/examples/shapes/shapes_logo_raylib.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - Draw raylib logo using basic shapes
+*   raylib [shapes] example - logo raylib
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo using shapes");
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - logo raylib");
 
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
diff --git a/raylib/examples/shapes/shapes_logo_raylib_anim.c b/raylib/examples/shapes/shapes_logo_raylib_anim.c
--- a/raylib/examples/shapes/shapes_logo_raylib_anim.c
+++ b/raylib/examples/shapes/shapes_logo_raylib_anim.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - raylib logo animation
+*   raylib [shapes] example - logo raylib anim
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - raylib logo animation");
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - logo raylib anim");
 
     int logoPositionX = screenWidth/2 - 128;
     int logoPositionY = screenHeight/2 - 128;
diff --git a/raylib/examples/shapes/shapes_math_angle_rotation.c b/raylib/examples/shapes/shapes_math_angle_rotation.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_math_angle_rotation.c
@@ -0,0 +1,105 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - math angle rotation
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6
+*
+*   Example contributed by Kris (@krispy-snacc) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Kris (@krispy-snacc)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h" // for DEG2RAD
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 720;
+    const int screenHeight = 400;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math angle rotation");
+    SetTargetFPS(60);
+
+    Vector2 center = { screenWidth/2.0f, screenHeight/2.0f };
+    const float lineLength = 150.0f;
+
+    // Predefined angles for fixed lines
+    int angles[] = { 0, 30, 60, 90 };
+    int numAngles = sizeof(angles)/sizeof(angles[0]);
+
+    float totalAngle = 0.0f; // Animated rotation angle
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        totalAngle += 1.0f; // degrees per frame
+        if (totalAngle >= 360.0f) totalAngle -= 360.0f;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(WHITE);
+
+            DrawText("Fixed angles + rotating line", 10, 10, 20, LIGHTGRAY);
+
+            // Draw fixed-angle lines with colorful gradient
+            for (int i = 0; i < numAngles; i++)
+            {
+                float rad = angles[i]*DEG2RAD;
+                Vector2 end = { center.x + cosf(rad)*lineLength,
+                                center.y + sinf(rad)*lineLength };
+
+                // Gradient color from green → cyan → blue → magenta
+                Color col;
+                switch(i)
+                {
+                    case 0: col = GREEN; break;
+                    case 1: col = ORANGE; break;
+                    case 2: col = BLUE; break;
+                    case 3: col = MAGENTA; break;
+                    default: col = WHITE; break;
+                }
+
+                DrawLineEx(center, end, 5.0f, col);
+
+                // Draw angle label slightly offset along the line
+                Vector2 textPos = { center.x + cosf(rad)*(lineLength + 20),
+                                    center.y + sinf(rad)*(lineLength + 20) };
+                DrawText(TextFormat("%d°", angles[i]), (int)textPos.x, (int)textPos.y, 20, col);
+            }
+
+            // Draw animated rotating line with changing color
+            float animRad = totalAngle*DEG2RAD;
+            Vector2 animEnd = { center.x + cosf(animRad)*lineLength,
+                                center.y + sinf(animRad)*lineLength };
+
+            // Cycle through HSV colors for animated line
+            Color animCol = ColorFromHSV(fmodf(totalAngle, 360.0f), 0.8f, 0.9f);
+            DrawLineEx(center, animEnd, 5.0f, animCol);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_math_sine_cosine.c b/raylib/examples/shapes/shapes_math_sine_cosine.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_math_sine_cosine.c
@@ -0,0 +1,177 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - math sine cosine
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Jopestpe (@jopestpe) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jopestpe (@jopestpe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+#include "raymath.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+// Wave points for sine/cosine visualization
+#define WAVE_POINTS 36
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math sine cosine");
+
+    Vector2 sinePoints[WAVE_POINTS];
+    Vector2 cosPoints[WAVE_POINTS];
+    Vector2 center = { (screenWidth/2.0f) - 30.0f, screenHeight/2.0f };
+    Rectangle start = { 20.0f, screenHeight - 120.f , 200.0f, 100.0f};
+    float radius = 130.0f;
+    float angle = 0.0f;
+    bool pause = false;
+
+    for (int i = 0; i < WAVE_POINTS; i++)
+    {
+        float t = i/(float)(WAVE_POINTS - 1);
+        float currentAngle = t*360.0f*DEG2RAD;
+        sinePoints[i] = (Vector2){ start.x + t*start.width, start.y + start.height/2.0f - sinf(currentAngle)*(start.height/2.0f) };
+        cosPoints[i] = (Vector2){ start.x + t*start.width, start.y + start.height/2.0f - cosf(currentAngle)*(start.height/2.0f) };
+    }
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float angleRad = angle*DEG2RAD;
+        float cosRad = cosf(angleRad);
+        float sinRad = sinf(angleRad);
+
+        Vector2 point = { center.x + cosRad*radius, center.y - sinRad*radius };
+        Vector2 limitMin = { center.x - radius, center.y - radius };
+        Vector2 limitMax = { center.x + radius, center.y + radius };
+
+        float complementary = 90.0f - angle;
+        float supplementary = 180.0f - angle;
+        float explementary = 360.0f - angle;
+
+        float tangent = Clamp(tanf(angleRad), -10.0f, 10.0f);
+        float cotangent = (fabsf(tangent) > 0.001f) ? Clamp(1.0f/tangent, -radius, radius) : 0.0f;
+        Vector2 tangentPoint = { center.x + radius, center.y - tangent*radius };
+        Vector2 cotangentPoint = { center.x + cotangent*radius, center.y - radius };
+
+        angle = Wrap(angle + (!pause ? 1.0f : 0.0f), 0.0f, 360.0f);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Cotangent (orange)
+            DrawLineEx((Vector2){ center.x , limitMin.y }, (Vector2){ cotangentPoint.x, limitMin.y }, 2.0f, ORANGE);
+            DrawLineDashed(center, cotangentPoint, 10.0f, 4.0f, ORANGE);
+
+            // Side background
+            DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
+            DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), (Color){ 232, 232, 232, 255 });
+
+            // Base circle and axes
+            DrawCircleLinesV(center, radius, GRAY);
+            DrawLineEx((Vector2){ center.x, limitMin.y }, (Vector2){ center.x, limitMax.y }, 1.0f, GRAY);
+            DrawLineEx((Vector2){ limitMin.x, center.y }, (Vector2){ limitMax.x, center.y }, 1.0f, GRAY);
+
+            // Wave graph axes
+            DrawLineEx((Vector2){ start.x , start.y }, (Vector2){ start.x , start.y + start.height }, 2.0f, GRAY);
+            DrawLineEx((Vector2){ start.x + start.width, start.y }, (Vector2){ start.x + start.width, start.y + start.height }, 2.0f, GRAY);
+            DrawLineEx((Vector2){ start.x, start.y + start.height/2 }, (Vector2){ start.x + start.width, start.y + start.height/2 }, 2.0f, GRAY);
+
+            // Wave graph axis labels
+            DrawText("1", start.x - 8,  start.y, 6, GRAY);
+            DrawText("0", start.x - 8,  start.y + start.height/2 - 6, 6, GRAY);
+            DrawText("-1", start.x - 12, start.y + start.height - 8, 6, GRAY);
+            DrawText("0", start.x - 2,  start.y + start.height + 4, 6, GRAY);
+            DrawText("360", start.x + start.width - 8,  start.y + start.height + 4, 6, GRAY);
+
+            // Sine (red - vertical)
+            DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ center.x, point.y }, 2.0f, RED);
+            DrawLineDashed((Vector2){ point.x, center.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, RED);
+            DrawText(TextFormat("Sine %.2f", sinRad), 640, 190, 6, RED);
+            DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-sinRad + 1)*start.height/2.0f) }, 4.0f, RED);
+            DrawSplineLinear(sinePoints, WAVE_POINTS, 1.0f, RED);
+
+            // Cosine (blue - horizontal)
+            DrawLineEx((Vector2){ center.x, center.y }, (Vector2){ point.x, center.y }, 2.0f, BLUE);
+            DrawLineDashed((Vector2){ center.x , point.y }, (Vector2){ point.x, point.y }, 10.0f, 4.0f, BLUE);
+            DrawText(TextFormat("Cosine %.2f", cosRad), 640, 210, 6, BLUE);
+            DrawCircleV((Vector2){ start.x + (angle/360.0f)*start.width, start.y + ((-cosRad + 1)*start.height/2.0f) }, 4.0f, BLUE);
+            DrawSplineLinear(cosPoints, WAVE_POINTS, 1.0f, BLUE);
+
+            // Tangent (purple)
+            DrawLineEx((Vector2){ limitMax.x , center.y }, (Vector2){ limitMax.x, tangentPoint.y }, 2.0f, PURPLE);
+            DrawLineDashed(center, tangentPoint, 10.0f, 4.0f, PURPLE);
+            DrawText(TextFormat("Tangent %.2f", tangent), 640, 230, 6, PURPLE);
+
+            // Cotangent (orange)
+            DrawText(TextFormat("Cotangent %.2f", cotangent), 640, 250, 6, ORANGE);
+
+            // Complementary angle (beige)
+            DrawCircleSectorLines(center, radius*0.6f , -angle, -90.0f , 36.0f, BEIGE);
+            DrawText(TextFormat("Complementary  %0.f°",complementary), 640, 150, 6, BEIGE);
+
+            // Supplementary angle (darkblue)
+            DrawCircleSectorLines(center, radius*0.5f , -angle, -180.0f , 36.0f, DARKBLUE);
+            DrawText(TextFormat("Supplementary  %0.f°",supplementary), 640, 130, 6, DARKBLUE);
+
+            // Explementary angle (pink)
+            DrawCircleSectorLines(center, radius*0.4f , -angle, -360.0f , 36.0f, PINK);
+            DrawText(TextFormat("Explementary  %0.f°",explementary), 640, 170, 6, PINK);
+
+            // Current angle - arc (lime), radius (black), endpoint (black)
+            DrawCircleSectorLines(center, radius*0.7f , -angle, 0.0f, 36.0f, LIME);
+            DrawLineEx((Vector2){ center.x , center.y }, point, 2.0f, BLACK);
+            DrawCircleV(point, 4.0f, BLACK);
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(GRAY));
+            GuiToggle((Rectangle){ 640, 70, 120, 20}, TextFormat("Pause"), &pause);
+            GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(LIME));
+            GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f°", angle), &angle, 0.0f, 360.0f);
+
+            // Angle values panel
+            GuiGroupBox((Rectangle){ 620, 110, 140, 170}, "Angle Values");
+            //------------------------------------------------------------------------------
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_mouse_trail.c b/raylib/examples/shapes/shapes_mouse_trail.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_mouse_trail.c
@@ -0,0 +1,102 @@
+/*******************************************************************************************
+*
+* raylib [shapes] example - Draw a mouse trail (position history)
+*
+* Example complexity rating: [★☆☆☆] 1/4
+*
+* Example originally created with raylib 5.6
+*
+* Example contributed by Balamurugan R (@Bala050814]) and reviewed by Ramon Santamaria (@raysan5)
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2025 Balamurugan R (@Bala050814)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"
+
+// Define the maximum number of positions to store in the trail
+#define MAX_TRAIL_LENGTH 30
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - mouse trail");
+
+    // Array to store the history of mouse positions (our fixed-size queue)
+    Vector2 trailPositions[MAX_TRAIL_LENGTH] = { 0 };
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        Vector2 mousePosition = GetMousePosition();
+
+        // Shift all existing positions backward by one slot in the array
+        // The last element (the oldest position) is dropped
+        for (int i = MAX_TRAIL_LENGTH - 1; i > 0; i--)
+        {
+            trailPositions[i] = trailPositions[i - 1];
+        }
+
+        // Store the new, current mouse position at the start of the array (Index 0)
+        trailPositions[0] = mousePosition;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(BLACK);
+
+            // Draw the trail by looping through the history array
+            for (int i = 0; i < MAX_TRAIL_LENGTH; i++)
+            {
+                // Ensure we skip drawing if the array hasn't been fully filled on startup
+                if ((trailPositions[i].x != 0.0f) || (trailPositions[i].y != 0.0f))
+                {
+                    // Calculate relative trail strength (ratio is near 1.0 for new, near 0.0 for old)
+                    float ratio = (float)(MAX_TRAIL_LENGTH - i)/MAX_TRAIL_LENGTH;
+
+                    // Fade effect: oldest positions are more transparent
+                    // Fade (color, alpha) - alpha is 0.5 to 1.0 based on ratio
+                    Color trailColor = Fade(SKYBLUE, ratio*0.5f + 0.5f);
+
+                    // Size effect: oldest positions are smaller
+                    float trailRadius = 15.0f*ratio;
+
+                    DrawCircleV(trailPositions[i], trailRadius, trailColor);
+                }
+            }
+
+            // Draw a distinct white circle for the current mouse position (Index 0)
+            DrawCircleV(mousePosition, 15.0f, WHITE);
+
+            DrawText("Move the mouse to see the trail effect!", 10, screenHeight - 30, 20, LIGHTGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();         // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_penrose_tile.c b/raylib/examples/shapes/shapes_penrose_tile.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_penrose_tile.c
@@ -0,0 +1,271 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - penrose tile
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
+*   Based on: https://processing.org/examples/penrosetile.html
+*
+*   Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#define STR_MAX_SIZE            10000
+#define TURTLE_STACK_MAX_SIZE      50
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct TurtleState {
+    Vector2 origin;
+    double angle;
+} TurtleState;
+
+typedef struct PenroseLSystem {
+    int steps;
+    char *production;
+    const char *ruleW;
+    const char *ruleX;
+    const char *ruleY;
+    const char *ruleZ;
+    float drawLength;
+    float theta;
+} PenroseLSystem;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static TurtleState turtleStack[TURTLE_STACK_MAX_SIZE];
+static int turtleTop = -1;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void PushTurtleState(TurtleState state);
+static TurtleState PopTurtleState(void);
+static PenroseLSystem CreatePenroseLSystem(float drawLength);
+static void BuildProductionStep(PenroseLSystem *ls);
+static void DrawPenroseLSystem(PenroseLSystem *ls);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - penrose tile");
+
+    float drawLength = 460.0f;
+    int minGenerations = 0;
+    int maxGenerations = 4;
+    int generations = 0;
+
+    // Initializee new penrose tile
+    PenroseLSystem ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations));
+    for (int i = 0; i < generations; i++) BuildProductionStep(&ls);
+
+    SetTargetFPS(120);              // Set our game to run at 120 frames-per-second
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        bool rebuild = false;
+        if (IsKeyPressed(KEY_UP))
+        {
+            if (generations < maxGenerations)
+            {
+                generations++;
+                rebuild = true;
+            }
+        }
+        else if (IsKeyPressed(KEY_DOWN))
+        {
+            if (generations > minGenerations)
+            {
+                generations--;
+                if (generations > 0) rebuild = true;
+            }
+        }
+        
+        if (rebuild)
+        {
+            RL_FREE(ls.production); // Free previous production for re-creation
+            ls = CreatePenroseLSystem(drawLength*(generations/(float)maxGenerations));
+            for (int i = 0; i < generations; i++) BuildProductionStep(&ls);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+        
+            ClearBackground( RAYWHITE );
+            
+            if (generations > 0) DrawPenroseLSystem(&ls);
+            
+            DrawText("penrose l-system", 10, 10, 20, DARKGRAY);
+            DrawText("press up or down to change generations", 10, 30, 20, DARKGRAY);
+            DrawText(TextFormat("generations: %d", generations), 10, 50, 20, DARKGRAY);
+            
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Push turtle state for next step
+static void PushTurtleState(TurtleState state)
+{
+    if (turtleTop < (TURTLE_STACK_MAX_SIZE - 1)) turtleStack[++turtleTop] = state;
+    else TraceLog(LOG_WARNING, "TURTLE STACK OVERFLOW!");
+}
+
+// Pop turtle state step
+static TurtleState PopTurtleState(void)
+{
+    if (turtleTop >= 0) return turtleStack[turtleTop--];
+    else TraceLog(LOG_WARNING, "TURTLE STACK UNDERFLOW!");
+
+    return (TurtleState){ 0 };
+}
+
+// Create a new penrose tile structure
+static PenroseLSystem CreatePenroseLSystem(float drawLength)
+{
+    // TODO: Review constant values assignment on recreation?
+    PenroseLSystem ls = {
+        .steps = 0,
+        .ruleW = "YF++ZF4-XF[-YF4-WF]++",
+        .ruleX = "+YF--ZF[3-WF--XF]+",
+        .ruleY = "-WF++XF[+++YF++ZF]-",
+        .ruleZ = "--YF++++WF[+ZF++++XF]--XF",
+        .drawLength = drawLength,
+        .theta = 36.0f // Degrees
+    };
+    
+    ls.production = (char *)RL_MALLOC(sizeof(char)*STR_MAX_SIZE);
+    ls.production[0] = '\0';
+    strncpy(ls.production, "[X]++[X]++[X]++[X]++[X]", STR_MAX_SIZE);
+    
+    return ls;
+}
+
+// Build next penrose step
+static void BuildProductionStep(PenroseLSystem *ls)
+{
+    char *newProduction = (char *)RL_MALLOC(sizeof(char)*STR_MAX_SIZE);
+    newProduction[0] = '\0';
+
+    int productionLength = (int)strnlen(ls->production, STR_MAX_SIZE);
+
+    for (int i = 0; i < productionLength; i++)
+    {
+        char step = ls->production[i];
+        int remainingSpace = STR_MAX_SIZE - (int)strnlen(newProduction, STR_MAX_SIZE) - 1;
+        switch (step)
+        {
+            case 'W': strncat(newProduction, ls->ruleW, remainingSpace); break;
+            case 'X': strncat(newProduction, ls->ruleX, remainingSpace); break;
+            case 'Y': strncat(newProduction, ls->ruleY, remainingSpace); break;
+            case 'Z': strncat(newProduction, ls->ruleZ, remainingSpace); break;
+            default:
+            {
+                if (step != 'F')
+                {
+                    int t = (int)strnlen(newProduction, STR_MAX_SIZE);
+                    newProduction[t] = step;
+                    newProduction[t + 1] = '\0';
+                }
+            } break;
+        }
+    }
+
+    ls->drawLength *= 0.5f;
+    strncpy(ls->production, newProduction, STR_MAX_SIZE);
+    
+    RL_FREE(newProduction);
+}
+
+// Draw penrose tile lines
+static void DrawPenroseLSystem(PenroseLSystem *ls)
+{
+    Vector2 screenCenter = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
+
+    TurtleState turtle = {
+        .origin = { 0 },
+        .angle = -90.0f
+    };
+
+    int repeats = 1;
+    int productionLength = (int)strnlen(ls->production, STR_MAX_SIZE);
+    ls->steps += 12;
+    
+    if (ls->steps > productionLength) ls->steps = productionLength;
+    
+    for (int i = 0; i < ls->steps; i++)
+    {
+        char step = ls->production[i];
+        if (step == 'F')
+        {
+            for (int j = 0; j < repeats; j++)
+            {
+                Vector2 startPosWorld = turtle.origin;
+                float radAngle = DEG2RAD*turtle.angle;
+                turtle.origin.x += ls->drawLength*cosf(radAngle);
+                turtle.origin.y += ls->drawLength*sinf(radAngle);
+                Vector2 startPosScreen = { startPosWorld.x + screenCenter.x, startPosWorld.y + screenCenter.y };
+                Vector2 endPosScreen = { turtle.origin.x + screenCenter.x, turtle.origin.y + screenCenter.y };
+                
+                DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2f));
+            }
+            
+            repeats = 1;
+        } 
+        else if (step == '+')
+        {
+            for (int j = 0; j < repeats; j++) turtle.angle += ls->theta;
+
+            repeats = 1;
+        } 
+        else if (step == '-')
+        {
+            for (int j = 0; j < repeats; j++) turtle.angle += -ls->theta;
+
+            repeats = 1;
+        } 
+        else if (step == '[') PushTurtleState(turtle);
+        else if (step == ']') turtle = PopTurtleState();
+        else if ((step >= 48) && (step <= 57)) repeats = (int) step - 48;
+    }
+
+    turtleTop = -1;
+}
diff --git a/raylib/examples/shapes/shapes_pie_chart.c b/raylib/examples/shapes/shapes_pie_chart.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_pie_chart.c
@@ -0,0 +1,228 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - pie chart
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Gideon Serfontein (@GideonSerf) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Gideon Serfontein (@GideonSerf)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+#include <stdio.h>
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+#define MAX_PIE_SLICES  10       // Max pie slices
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - pie chart");
+
+    int sliceCount = 7;
+    float donutInnerRadius = 25.0f;
+    float values[MAX_PIE_SLICES] = { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f }; // Initial slice values
+    char labels[MAX_PIE_SLICES][32] = { 0 };
+    bool editingLabel[MAX_PIE_SLICES] = { 0 };
+
+    for (int i = 0; i < MAX_PIE_SLICES; i++)
+        snprintf(labels[i], 32, "Slice %02i", i + 1);
+
+    bool showValues = true;
+    bool showPercentages = false;
+    bool showDonut = false;
+    int hoveredSlice = -1;
+    Rectangle scrollPanelBounds = {0};
+    Vector2 scrollContentOffset = {0};
+    Rectangle view = { 0 };
+
+    // UI layout parameters
+    const int panelWidth = 270;
+    const int panelMargin = 5;
+
+    // UI Panel top-left anchor
+    const Vector2 panelPos = {
+        (float)screenWidth  - panelMargin - panelWidth,
+        (float)panelMargin
+    };
+
+    // UI Panel rectangle
+    const Rectangle panelRect = {
+        panelPos.x, panelPos.y,
+        (float)panelWidth,
+        (float)screenHeight - 2.0f*panelMargin
+    };
+
+    // Pie chart geometry
+    const Rectangle canvas = { 0, 0, panelPos.x, (float)screenHeight };
+    const Vector2 center = { canvas.width/2.0f, canvas.height/2.0f};
+    const float radius = 205.0f;
+
+    // Total value for percentage calculations
+    float totalValue = 0.0f;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Calculate total value for percentage calculations
+        totalValue = 0.0f;
+        for (int i = 0; i < sliceCount; i++) totalValue += values[i];
+
+        // Check for mouse hover over slices
+        hoveredSlice = -1; // Reset hovered slice
+        Vector2 mousePos = GetMousePosition();
+        if (CheckCollisionPointRec(mousePos, canvas)) // Only check if mouse is inside the canvas
+        {
+            float dx = mousePos.x - center.x;
+            float dy = mousePos.y - center.y;
+            float distance = sqrtf(dx*dx + dy*dy);
+
+            if (distance <= radius) // Inside the pie radius
+            {
+                float angle = atan2f(dy, dx)*RAD2DEG;
+                if (angle < 0) angle += 360;
+
+                float currentAngle = 0.0f;
+                for (int i = 0; i < sliceCount; i++)
+                {
+                    float sweep = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f;
+
+                    if ((angle >= currentAngle) && (angle < (currentAngle + sweep)))
+                    {
+                        hoveredSlice = i;
+                        break;
+                    }
+
+                    currentAngle += sweep;
+                }
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw the pie chart on the canvas
+            float startAngle = 0.0f;
+            for (int i = 0; i < sliceCount; i++)
+            {
+                float sweepAngle = (totalValue > 0)? (values[i]/totalValue)*360.0f : 0.0f;
+                float midAngle = startAngle + sweepAngle/2.0f; // Middle angle for label positioning
+
+                Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f);
+                float currentRadius = radius;
+
+                // Make the hovered slice pop out by adding 5 pixels to its radius
+                if (i == hoveredSlice) currentRadius += 20.0f;
+
+                // Draw the pie slice using raylib's DrawCircleSector function
+                DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color);
+
+                // Draw the label for the current slice
+                if (values[i] > 0)
+                {
+                    char labelText[64] = { 0 };
+                    if (showValues && showPercentages) snprintf(labelText, 64, "%.1f (%.0f%%)", values[i], (values[i]/totalValue)*100.0f);
+                    else if (showValues) snprintf(labelText, 64, "%.1f", values[i]);
+                    else if (showPercentages) snprintf(labelText, 64, "%.0f%%", (values[i]/totalValue)*100.0f);
+                    else labelText[0] = '\0';
+
+                    Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1);
+                    float labelRadius = radius*0.7f;
+                    Vector2 labelPos = { center.x + cosf(midAngle*DEG2RAD)*labelRadius - textSize.x/2.0f,
+                        center.y + sinf(midAngle*DEG2RAD)*labelRadius - textSize.y/2.0f };
+                    DrawText(labelText, (int)labelPos.x, (int)labelPos.y, 20, WHITE);
+                }
+
+                // Draw inner circle to create donut effect
+                // TODO: This is a hacky solution, better use DrawRing()
+                if (showDonut) DrawCircle(center.x, center.y, donutInnerRadius, RAYWHITE);
+
+                startAngle += sweepAngle;
+            }
+
+            // UI control panel
+            DrawRectangleRec(panelRect, Fade(LIGHTGRAY, 0.5f));
+            DrawRectangleLinesEx(panelRect, 1.0f, GRAY);
+
+            GuiSpinner((Rectangle){ panelPos.x + 95, (float)panelPos.y + 12, 125, 25 }, "Slices ", &sliceCount, 1, MAX_PIE_SLICES, false);
+            GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 40, 20, 20 }, "Show Values", &showValues);
+            GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 70, 20, 20 }, "Show Percentages", &showPercentages);
+            GuiCheckBox((Rectangle){ panelPos.x + 20, (float)panelPos.y + 12 + 100, 20, 20 }, "Make Donut", &showDonut);
+
+            if (showDonut) GuiDisable();
+            GuiSliderBar((Rectangle){ panelPos.x + 80, (float)panelPos.y + 12 + 130, panelRect.width - 100, 30 },
+                            "Inner Radius", NULL, &donutInnerRadius, 5.0f, radius - 10.0f);
+            GuiEnable();
+
+            GuiLine((Rectangle){ panelPos.x + 10, (float)panelPos.y + 12 + 170, panelRect.width - 20, 1 }, NULL);
+
+            // Scrollable area for slice editors
+            scrollPanelBounds = (Rectangle){
+                panelPos.x + panelMargin,
+                (float)panelPos.y + 12 + 190,
+                panelRect.width - panelMargin*2,
+                panelRect.y + panelRect.height - panelPos.y + 12 + 190 - panelMargin
+            };
+            int contentHeight = sliceCount*35;
+
+            GuiScrollPanel(scrollPanelBounds, NULL,
+                (Rectangle){ 0, 0, panelRect.width - 25, (float)contentHeight },
+                &scrollContentOffset, &view);
+
+            const float contentX = view.x + scrollContentOffset.x; // Left of content
+            const float contentY = view.y + scrollContentOffset.y; // Top of content
+
+            BeginScissorMode((int)view.x, (int)view.y, (int)view.width, (int)view.height);
+
+                for (int i = 0; i < sliceCount; i++)
+                {
+                    const int rowY = (int)(contentY + 5 + i*35);
+
+                    // Color indicator
+                    Color color = ColorFromHSV((float)i/sliceCount*360.0f, 0.75f, 0.9f);
+                    DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color);
+
+                    // Label textbox
+                    if (GuiTextBox((Rectangle){ contentX + 45, (float)rowY, 75, 30 }, labels[i], 32, editingLabel[i])) editingLabel[i] = !editingLabel[i];
+
+                    GuiSliderBar((Rectangle){ contentX + 130, (float)rowY, 110, 30 }, NULL, NULL, &values[i], 0.0f, 1000.0f);
+                }
+
+            EndScissorMode();
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow(); // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_rectangle_advanced.c b/raylib/examples/shapes/shapes_rectangle_advanced.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_rectangle_advanced.c
@@ -0,0 +1,358 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - rectangle advanced
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Everton Jr. (@evertonse) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2024-2025 Everton Jr. (@evertonse) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+#include <math.h>
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+// Draw rectangle with rounded edges and horizontal gradient, with options to choose side of roundness
+static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, float roundnessRight, int segments, Color left, Color right);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle advanced");
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update rectangle bounds
+        //----------------------------------------------------------------------------------
+        float width = GetScreenWidth()/2.0f, height = GetScreenHeight()/6.0f;
+        Rectangle rec = {
+            GetScreenWidth()/2.0f - width/2,
+            GetScreenHeight()/2.0f - 5*(height/2),
+            width, height
+        };
+        //--------------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw All Rectangles with different roundess  for each side and different gradients
+            DrawRectangleRoundedGradientH(rec, 0.8f, 0.8f, 36, BLUE, RED);
+
+            rec.y += rec.height + 1;
+            DrawRectangleRoundedGradientH(rec, 0.5f, 1.0f, 36, RED, PINK);
+
+            rec.y += rec.height + 1;
+            DrawRectangleRoundedGradientH(rec, 1.0f, 0.5f, 36, RED, BLUE);
+
+            rec.y += rec.height + 1;
+            DrawRectangleRoundedGradientH(rec, 0.0f, 1.0f, 36, BLUE, BLACK);
+
+            rec.y += rec.height + 1;
+            DrawRectangleRoundedGradientH(rec, 1.0f, 0.0f, 36, BLUE, PINK);
+        EndDrawing();
+        //--------------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+// Draw rectangle with rounded edges and horizontal gradient, with options to choose side of roundness
+// NOTE: Adapted from both 'DrawRectangleRounded()' and 'DrawRectangleGradientH()' raylib [rshapes] implementations
+static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, float roundnessRight, int segments, Color left, Color right)
+{
+    // Neither side is rounded
+    if ((roundnessLeft <= 0.0f && roundnessRight <= 0.0f) || (rec.width < 1) || (rec.height < 1 ))
+    {
+        DrawRectangleGradientEx(rec, left, left, right, right);
+        return;
+    }
+
+    if (roundnessLeft  >= 1.0f) roundnessLeft  = 1.0f;
+    if (roundnessRight >= 1.0f) roundnessRight = 1.0f;
+
+    // Calculate corner radius both from right and left
+    float recSize = rec.width > rec.height ? rec.height : rec.width;
+    float radiusLeft  = (recSize*roundnessLeft)/2;
+    float radiusRight = (recSize*roundnessRight)/2;
+
+    if (radiusLeft <= 0.0f) radiusLeft = 0.0f;
+    if (radiusRight <= 0.0f) radiusRight = 0.0f;
+
+    if (radiusRight <= 0.0f && radiusLeft <= 0.0f) return;
+
+    float stepLength = 90.0f/(float)segments;
+
+    /*
+    Diagram Copied here for reference, original at 'DrawRectangleRounded()' source code
+
+          P0____________________P1
+          /|                    |\
+         /1|          2         |3\
+     P7 /__|____________________|__\ P2
+       |   |P8                P9|   |
+       | 8 |          9         | 4 |
+       | __|____________________|__ |
+     P6 \  |P11              P10|  / P3
+         \7|          6         |5/
+          \|____________________|/
+          P5                    P4
+    */
+
+    // Coordinates of the 12 points also apdated from `DrawRectangleRounded`
+    const Vector2 point[12] = {
+        // PO, P1, P2
+        {(float)rec.x + radiusLeft, rec.y}, {(float)(rec.x + rec.width) - radiusRight, rec.y}, { rec.x + rec.width, (float)rec.y + radiusRight },
+        // P3, P4
+        {rec.x + rec.width, (float)(rec.y + rec.height) - radiusRight}, {(float)(rec.x + rec.width) - radiusRight, rec.y + rec.height},
+        // P5, P6, P7
+        {(float)rec.x + radiusLeft, rec.y + rec.height}, { rec.x, (float)(rec.y + rec.height) - radiusLeft}, {rec.x, (float)rec.y + radiusLeft},
+        // P8, P9
+        {(float)rec.x + radiusLeft, (float)rec.y + radiusLeft}, {(float)(rec.x + rec.width) - radiusRight, (float)rec.y + radiusRight},
+        // P10, P11
+        {(float)(rec.x + rec.width) - radiusRight, (float)(rec.y + rec.height) - radiusRight}, {(float)rec.x + radiusLeft, (float)(rec.y + rec.height) - radiusLeft}
+    };
+
+    const Vector2 centers[4] = { point[8], point[9], point[10], point[11] };
+    const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
+
+#if defined(SUPPORT_QUADS_DRAW_MODE)
+    rlSetTexture(GetShapesTexture().id);
+    Rectangle shapeRect = GetShapesTextureRectangle();
+
+    rlBegin(RL_QUADS);
+        // Draw all the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
+        for (int k = 0; k < 4; ++k)
+        {
+            Color color;
+            float radius;
+            if (k == 0) color = left,  radius = radiusLeft;     // [1] Upper Left Corner
+            if (k == 1) color = right, radius = radiusRight;    // [3] Upper Right Corner
+            if (k == 2) color = right, radius = radiusRight;    // [5] Lower Right Corner
+            if (k == 3) color = left,  radius = radiusLeft;     // [7] Lower Left Corner
+            float angle = angles[k];
+            const Vector2 center = centers[k];
+
+            for (int i = 0; i < segments/2; i++)
+            {
+                rlColor4ub(color.r, color.g, color.b, color.a);
+                rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+                rlVertex2f(center.x, center.y);
+
+                rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+                rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius);
+
+                rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+                rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
+
+                rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+                rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
+
+                angle += (stepLength*2);
+            }
+
+            // End one even segments
+            if ( segments%2)
+            {
+                rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+                rlVertex2f(center.x, center.y);
+
+                rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+                rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
+
+                rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+                rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
+
+                rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+                rlVertex2f(center.x, center.y);
+            }
+        }
+
+        // Here we use the 'Diagram' to guide ourselves to which point receives what color
+        // By choosing the color correctly associated with a pointe the gradient effect
+        // will naturally come from OpenGL interpolation
+
+        // [2] Upper Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[0].x, point[0].y);
+        rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[8].x, point[8].y);
+
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[9].x, point[9].y);
+
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[1].x, point[1].y);
+
+        // [4] Left Rectangle
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[2].x, point[2].y);
+        rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[9].x, point[9].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[10].x, point[10].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[3].x, point[3].y);
+
+        // [6] Bottom Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[11].x, point[11].y);
+        rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[5].x, point[5].y);
+
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[4].x, point[4].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[10].x, point[10].y);
+
+        // [8] left Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[7].x, point[7].y);
+        rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[6].x, point[6].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[11].x, point[11].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[8].x, point[8].y);
+
+        // [9] Middle Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[8].x, point[8].y);
+        rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[11].x, point[11].y);
+
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
+        rlVertex2f(point[10].x, point[10].y);
+        rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
+        rlVertex2f(point[9].x, point[9].y);
+
+    rlEnd();
+    rlSetTexture(0);
+#else
+
+    // Here we use the 'Diagram' to guide ourselves to which point receives what color
+    // By choosing the color correctly associated with a pointe the gradient effect
+    // will naturally come from OpenGL interpolation
+    // But this time instead of Quad, we think in triangles
+
+    rlBegin(RL_TRIANGLES);
+        // Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
+        for (int k = 0; k < 4; ++k)
+        {
+            Color color = { 0 };
+            float radius = 0.0f;
+            if (k == 0) color = left,  radius = radiusLeft;     // [1] Upper Left Corner
+            if (k == 1) color = right, radius = radiusRight;    // [3] Upper Right Corner
+            if (k == 2) color = right, radius = radiusRight;    // [5] Lower Right Corner
+            if (k == 3) color = left,  radius = radiusLeft;     // [7] Lower Left Corner
+
+            float angle = angles[k];
+            const Vector2 center = centers[k];
+
+            for (int i = 0; i < segments; i++)
+            {
+                rlColor4ub(color.r, color.g, color.b, color.a);
+                rlVertex2f(center.x, center.y);
+                rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius);
+                rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius);
+                angle += stepLength;
+            }
+        }
+
+        // [2] Upper Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[0].x, point[0].y);
+        rlVertex2f(point[8].x, point[8].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[9].x, point[9].y);
+        rlVertex2f(point[1].x, point[1].y);
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[0].x, point[0].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[9].x, point[9].y);
+
+        // [4] Right Rectangle
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[9].x, point[9].y);
+        rlVertex2f(point[10].x, point[10].y);
+        rlVertex2f(point[3].x, point[3].y);
+        rlVertex2f(point[2].x, point[2].y);
+        rlVertex2f(point[9].x, point[9].y);
+        rlVertex2f(point[3].x, point[3].y);
+
+        // [6] Bottom Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[11].x, point[11].y);
+        rlVertex2f(point[5].x, point[5].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[4].x, point[4].y);
+        rlVertex2f(point[10].x, point[10].y);
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[11].x, point[11].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[4].x, point[4].y);
+
+        // [8] Left Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[7].x, point[7].y);
+        rlVertex2f(point[6].x, point[6].y);
+        rlVertex2f(point[11].x, point[11].y);
+        rlVertex2f(point[8].x, point[8].y);
+        rlVertex2f(point[7].x, point[7].y);
+        rlVertex2f(point[11].x, point[11].y);
+
+        // [9] Middle Rectangle
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[8].x, point[8].y);
+        rlVertex2f(point[11].x, point[11].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[10].x, point[10].y);
+        rlVertex2f(point[9].x, point[9].y);
+        rlColor4ub(left.r, left.g, left.b, left.a);
+        rlVertex2f(point[8].x, point[8].y);
+        rlColor4ub(right.r, right.g, right.b, right.a);
+        rlVertex2f(point[10].x, point[10].y);
+    rlEnd();
+#endif
+}
diff --git a/raylib/examples/shapes/shapes_rectangle_scaling.c b/raylib/examples/shapes/shapes_rectangle_scaling.c
--- a/raylib/examples/shapes/shapes_rectangle_scaling.c
+++ b/raylib/examples/shapes/shapes_rectangle_scaling.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [shapes] example - rectangle scaling by mouse
+*   raylib [shapes] example - rectangle scaling
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
 *   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -27,7 +29,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling mouse");
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle scaling");
 
     Rectangle rec = { 100, 100, 200, 80 };
 
@@ -63,7 +65,7 @@
             // Check minimum rec size
             if (rec.width < MOUSE_SCALE_MARK_SIZE) rec.width = MOUSE_SCALE_MARK_SIZE;
             if (rec.height < MOUSE_SCALE_MARK_SIZE) rec.height = MOUSE_SCALE_MARK_SIZE;
-            
+
             // Check maximum rec size
             if (rec.width > (GetScreenWidth() - rec.x)) rec.width = GetScreenWidth() - rec.x;
             if (rec.height > (GetScreenHeight() - rec.y)) rec.height = GetScreenHeight() - rec.y;
diff --git a/raylib/examples/shapes/shapes_recursive_tree.c b/raylib/examples/shapes/shapes_recursive_tree.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_recursive_tree.c
@@ -0,0 +1,132 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - recursive tree
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Jopestpe (@jopestpe)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jopestpe (@jopestpe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+    Vector2 start;
+    Vector2 end;
+    float angle;
+    float length;
+} Branch;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - recursive tree");
+
+    Vector2 start = { (screenWidth/2.0f) - 125.0f, (float)screenHeight };
+    float angle = 40.0f;
+    float thick = 1.0f;
+    float treeDepth = 10.0f;
+    float branchDecay = 0.66f;
+    float length = 120.0f;
+    bool bezier = false;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float theta = angle*DEG2RAD;
+        int maxBranches = (int)(powf(2, floorf(treeDepth)));
+        Branch branches[1030] = { 0 };
+        int count = 0;
+
+        Vector2 initialEnd = { start.x + length*sinf(0.0f), start.y - length*cosf(0.0f) };
+        branches[count++] = (Branch){start, initialEnd, 0.0f, length};
+
+        for (int i = 0; i < count; i++)
+        {
+            Branch branch = branches[i];
+            if (branch.length < 2) continue;
+
+            float nextLength = branch.length*branchDecay;
+
+            if (count < maxBranches && nextLength >= 2)
+            {
+                Vector2 branchStart = branch.end;
+
+                float angle1 = branch.angle + theta;
+                Vector2 branchEnd1 = { branchStart.x + nextLength*sinf(angle1), branchStart.y - nextLength*cosf(angle1) };
+                branches[count++] = (Branch){branchStart, branchEnd1, angle1, nextLength};
+
+                float angle2 = branch.angle - theta;
+                Vector2 branchEnd2 = { branchStart.x + nextLength*sinf(angle2), branchStart.y - nextLength*cosf(angle2) };
+                branches[count++] = (Branch){branchStart, branchEnd2, angle2, nextLength};
+            }
+        }
+        //----------------------------------------------------------------------------------
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < count; i++)
+            {
+                Branch branch = branches[i];
+                if (branch.length >= 2)
+                {
+                    if (bezier) DrawLineBezier(branch.start, branch.end, thick, RED);
+                    else DrawLineEx(branch.start, branch.end, thick, RED);
+                }
+            }
+
+            DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
+            DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), (Color){ 232, 232, 232, 255 });
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f", angle), &angle, 0, 180);
+            GuiSliderBar((Rectangle){ 640, 70, 120, 20 }, "Length", TextFormat("%.0f", length), &length, 12.0f, 240.0f);
+            GuiSliderBar((Rectangle){ 640, 100, 120, 20}, "Decay", TextFormat("%.2f", branchDecay), &branchDecay, 0.1f, 0.78f);
+            GuiSliderBar((Rectangle){ 640, 130, 120, 20 }, "Depth", TextFormat("%.0f", treeDepth), &treeDepth, 1.0f, 10.0f);
+            GuiSliderBar((Rectangle){ 640, 160, 120, 20}, "Thick", TextFormat("%.0f", thick), &thick, 1, 8);
+            GuiCheckBox((Rectangle){ 640, 190, 20, 20 }, "Bezier", &bezier);
+            //------------------------------------------------------------------------------
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_ring_drawing.c b/raylib/examples/shapes/shapes_ring_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_ring_drawing.c
@@ -0,0 +1,102 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - ring drawing
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 2.5
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ring drawing");
+
+    Vector2 center = {(GetScreenWidth() - 300)/2.0f, GetScreenHeight()/2.0f };
+
+    float innerRadius = 80.0f;
+    float outerRadius = 190.0f;
+
+    float startAngle = 0.0f;
+    float endAngle = 360.0f;
+    float segments = 0.0f;
+
+    bool drawRing = true;
+    bool drawRingLines = false;
+    bool drawCircleLines = false;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // NOTE: All variables update happens inside GUI control functions
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
+            DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
+
+            if (drawRing) DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f));
+            if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f));
+            if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f));
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", TextFormat("%.2f", startAngle), &startAngle, -450, 450);
+            GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", TextFormat("%.2f", endAngle), &endAngle, -450, 450);
+
+            GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", TextFormat("%.2f", innerRadius), &innerRadius, 0, 100);
+            GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", TextFormat("%.2f", outerRadius), &outerRadius, 0, 200);
+
+            GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", TextFormat("%.2f", segments), &segments, 0, 100);
+
+            GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing);
+            GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines);
+            GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", &drawCircleLines);
+            //------------------------------------------------------------------------------
+
+            int minSegments = (int)ceilf((endAngle - startAngle)/90);
+            DrawText(TextFormat("MODE: %s", (segments >= minSegments)? "MANUAL" : "AUTO"), 600, 270, 10, (segments >= minSegments)? MAROON : DARKGRAY);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_rlgl_color_wheel.c b/raylib/examples/shapes/shapes_rlgl_color_wheel.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_rlgl_color_wheel.c
@@ -0,0 +1,274 @@
+﻿/*******************************************************************************************
+*
+*   raylib [shapes] example - rlgl color wheel
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "rlgl.h"
+#include "raymath.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    // The minimum/maximum points the circle can have
+    const unsigned int pointsMin = 3;
+    const unsigned int pointsMax = 256;
+
+    // The current number of points and the radius of the circle
+    unsigned int triangleCount = 64;
+    float pointScale = 150.0f;
+
+    // Slider value, literally maps to value in HSV
+    float value = 1.0f;
+
+    // The center of the screen
+    Vector2 center = { (float)screenWidth/2.0f, (float)screenHeight/2.0f };
+    // The location of the color wheel
+    Vector2 circlePosition = center;
+
+    // The currently selected color
+    Color color = { 255, 255, 255, 255 };
+
+    // Indicates if the slider is being clicked
+    bool sliderClicked = false;
+
+    // Indicates if the current color going to be updated, as well as the handle position
+    bool settingColor = false;
+
+    // How the color wheel will be rendered
+    unsigned int renderType = RL_TRIANGLES;
+
+    // Enable anti-aliasing
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl color wheel");
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        triangleCount += (unsigned int)GetMouseWheelMove();
+        triangleCount = (unsigned int)Clamp((float)triangleCount, (float)pointsMin, (float)pointsMax);
+
+        Rectangle sliderRectangle = { 42.0f, 16.0f + 64.0f + 45.0f, 64.0f, 16.0f };
+        Vector2 mousePosition = GetMousePosition();
+
+        // Checks if the user is hovering over the value slider
+        bool sliderHover = (mousePosition.x >= sliderRectangle.x && mousePosition.y >= sliderRectangle.y && mousePosition.x < sliderRectangle.x + sliderRectangle.width && mousePosition.y < sliderRectangle.y + sliderRectangle.height);
+
+        // Copy color as hex
+        if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C))
+        {
+            if (IsKeyPressed(KEY_C))
+            {
+                SetClipboardText(TextFormat("#%02X%02X%02X", color.r, color.g, color.b));
+            }
+        }
+
+        // Scale up the color wheel, adjusting the handle visually
+        if (IsKeyDown(KEY_UP))
+        {
+            pointScale *= 1.025f;
+
+            if (pointScale > (float)screenHeight/2.0f)
+            {
+                pointScale = (float)screenHeight/2.0f;
+            }
+            else
+            {
+                circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 1.025f, 1.025f }), center);
+            }
+        }
+
+        // Scale down the wheel, adjusting the handle visually
+        if (IsKeyDown(KEY_DOWN))
+        {
+            pointScale *= 0.975f;
+
+            if (pointScale < 32.0f)
+            {
+                pointScale = 32.0f;
+            }
+            else
+            {
+                circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), (Vector2){ 0.975f, 0.975f }), center);
+            }
+
+            float distance = Vector2Distance(center, circlePosition)/pointScale;
+            float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition))/PI + 1.0f)/2.0f);
+
+            if (distance > 1.0f)
+            {
+                circlePosition = Vector2Add((Vector2){ sinf(angle*(PI*2.0f))*pointScale, -cosf(angle*(PI*2.0f))*pointScale }, center);
+            }
+        }
+
+        // Checks if the user clicked on the color wheel
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && Vector2Distance(GetMousePosition(), center) <= pointScale + 10.0f)
+        {
+            settingColor = true;
+        }
+
+        // Update flag when mouse button is released
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) settingColor = false;
+
+        // Check if the user clicked/released the slider for the color's value
+        if (sliderHover && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) sliderClicked = true;
+
+        if (sliderClicked && IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) sliderClicked = false;
+
+        // Update render mode accordingly
+        if (IsKeyPressed(KEY_SPACE)) renderType = RL_LINES;
+
+        if (IsKeyReleased(KEY_SPACE)) renderType = RL_TRIANGLES;
+
+        // If the slider or the wheel was clicked, update the current color
+        if (settingColor || sliderClicked)
+        {
+            if (settingColor) circlePosition = GetMousePosition();
+
+            float distance = Vector2Distance(center, circlePosition)/pointScale;
+
+            float angle = ((Vector2Angle((Vector2){ 0.0f, -pointScale }, Vector2Subtract(center, circlePosition))/PI + 1.0f)/2.0f);
+            if (settingColor && distance > 1.0f) circlePosition = Vector2Add((Vector2){ sinf(angle*(PI*2.0f))*pointScale, -cosf(angle*(PI* 2.0f))*pointScale }, center);
+
+            float angle360 = angle*360.0f;
+            float valueActual = Clamp(distance, 0.0f, 1.0f);
+            color = ColorLerp((Color){ (int)(value*255.0f), (int)(value*255.0f), (int)(value*255.0f), 255 }, ColorFromHSV(angle360, Clamp(distance, 0.0f, 1.0f), 1.0f), valueActual);
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+        ClearBackground(RAYWHITE);
+
+        // Begin rendering color wheel
+        rlBegin(renderType);
+        for (unsigned int i = 0; i < triangleCount; i++)
+        {
+            float angleOffset = ((PI*2.0f)/(float)triangleCount);
+            float angle = angleOffset*(float)i;
+            float angleOffsetCalculated = ((float)i + 1)*angleOffset;
+            Vector2 scale = (Vector2){ pointScale, pointScale };
+
+            Vector2 offset = Vector2Multiply((Vector2){ sinf(angle), -cosf(angle) }, scale);
+            Vector2 offset2 = Vector2Multiply((Vector2){ sinf(angleOffsetCalculated), -cosf(angleOffsetCalculated) }, scale);
+
+            Vector2 position = Vector2Add(center, offset);
+            Vector2 position2 = Vector2Add(center, offset2);
+
+            float angleNonRadian = (angle/(2.0f*PI))*360.0f;
+            float angleNonRadianOffset = (angleOffset/(2.0f*PI))*360.0f;
+
+            Color currentColor = ColorFromHSV(angleNonRadian, 1.0f, 1.0f);
+            Color offsetColor = ColorFromHSV(angleNonRadian + angleNonRadianOffset, 1.0f, 1.0f);
+
+            // Input vertices differently depending on mode
+            if (renderType == RL_TRIANGLES)
+            {
+                // RL_TRIANGLES expects three vertices per triangle
+                rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+                rlVertex2f(position.x, position.y);
+                rlColor4f(value, value, value, 1.0f);
+                rlVertex2f(center.x, center.y);
+                rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a);
+                rlVertex2f(position2.x, position2.y);
+            }
+            else if (renderType == RL_LINES)
+            {
+                // RL_LINES expects two vertices per line
+                rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+                rlVertex2f(position.x, position.y);
+                rlColor4ub(WHITE.r, WHITE.g, WHITE.b, WHITE.a);
+                rlVertex2f(center.x, center.y);
+
+                rlVertex2f(center.x, center.y);
+                rlColor4ub(offsetColor.r, offsetColor.g, offsetColor.b, offsetColor.a);
+                rlVertex2f(position2.x, position2.y);
+
+                rlVertex2f(position2.x, position2.y);
+                rlColor4ub(currentColor.r, currentColor.g, currentColor.b, currentColor.a);
+                rlVertex2f(position.x, position.y);
+            }
+        }
+        rlEnd();
+
+        // Make the handle slightly more visible overtop darker colors
+        Color handleColor = BLACK;
+
+        if (Vector2Distance(center, circlePosition)/pointScale <= 0.5f && value <= 0.5f)
+        {
+            handleColor = DARKGRAY;
+        }
+
+        // Draw the color handle
+        DrawCircleLinesV(circlePosition, 4.0f, handleColor);
+
+        // Draw the color in a preview, with a darkened outline.
+        DrawRectangleV((Vector2){ 8.0f, 8.0f }, (Vector2){ 64.0f, 64.0f }, color);
+        DrawRectangleLinesEx((Rectangle){ 8.0f, 8.0f, 64.0f, 64.0f }, 2.0f, ColorLerp(color, BLACK, 0.5f));
+
+        // Draw current color as hex and decimal
+        DrawText(TextFormat("#%02X%02X%02X\n(%d, %d, %d)", color.r, color.g, color.b, color.r, color.g, color.b), 8, 8 + 64 + 8, 20, DARKGRAY);
+
+        // Update the visuals for the copying text
+        Color copyColor = DARKGRAY;
+        unsigned int offset = 0;
+        if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyDown(KEY_C))
+        {
+            copyColor = DARKGREEN;
+            offset = 4;
+        }
+
+        // Draw the copying text
+        DrawText("press ctrl+c to copy!", 8, 425 - offset, 20, copyColor);
+
+        // Display the number of rendered triangles
+        DrawText(TextFormat("triangle count: %d", triangleCount), 8, 395, 20, DARKGRAY);
+
+        // Slider to change color's value
+        GuiSliderBar(sliderRectangle, "value: ", "", &value, 0.0f, 1.0f);
+
+        // Draw FPS next to outlined color preview
+        DrawFPS(64 + 16, 8);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_rlgl_triangle.c b/raylib/examples/shapes/shapes_rlgl_triangle.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_rlgl_triangle.c
@@ -0,0 +1,169 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - rlgl triangle
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl triangle");
+
+    // Starting postions and rendered triangle positions
+    Vector2 startingPositions[3] = {{ 400.0f, 150.0f }, { 300.0f, 300.0f }, { 500.0f, 300.0f }};
+    Vector2 trianglePositions[3] = { startingPositions[0], startingPositions[1], startingPositions[2] };
+
+    // Currently selected vertex, -1 means none
+    int triangleIndex = -1;
+    bool linesMode = false;
+    float handleRadius = 8.0f;
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_SPACE)) linesMode = !linesMode;
+
+        // Check selected vertex
+        for (unsigned int i = 0; i < 3; i++)
+        {
+            // If the mouse is within the handle circle
+            if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius) &&
+                IsMouseButtonDown(MOUSE_BUTTON_LEFT))
+            {
+                triangleIndex = i;
+                break;
+            }
+        }
+
+        // If the user has selected a vertex, offset it by the mouse's delta this frame
+        if (triangleIndex != -1)
+        {
+            Vector2 *position = &trianglePositions[triangleIndex];
+
+            Vector2 mouseDelta = GetMouseDelta();
+            position->x += mouseDelta.x;
+            position->y += mouseDelta.y;
+        }
+
+        // Reset index on release
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) triangleIndex = -1;
+
+        // Enable/disable backface culling (2-sided triangles, slower to render)
+        if (IsKeyPressed(KEY_LEFT)) rlEnableBackfaceCulling();
+        if (IsKeyPressed(KEY_RIGHT)) rlDisableBackfaceCulling();
+
+        // Reset triangle vertices to starting positions and reset backface culling
+        if (IsKeyPressed(KEY_R))
+        {
+            trianglePositions[0] = startingPositions[0];
+            trianglePositions[1] = startingPositions[1];
+            trianglePositions[2] = startingPositions[2];
+
+            rlEnableBackfaceCulling();
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            if (linesMode)
+            {
+                // Draw triangle with lines
+                rlBegin(RL_LINES);
+                    // Three lines, six points
+                    // Define color for next vertex
+                    rlColor4ub(255, 0, 0, 255);
+                    // Define vertex
+                    rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+                    rlColor4ub(0, 255, 0, 255);
+                    rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+
+                    rlColor4ub(0, 255, 0, 255);
+                    rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+                    rlColor4ub(0, 0, 255, 255);
+                    rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+
+                    rlColor4ub(0, 0, 255, 255);
+                    rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+                    rlColor4ub(255, 0, 0, 255);
+                    rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+                rlEnd();
+            }
+            else
+            {
+                // Draw triangle as a triangle
+                rlBegin(RL_TRIANGLES);
+                    // One triangle, three points
+                    // Define color for next vertex
+                    rlColor4ub(255, 0, 0, 255);
+                    // Define vertex
+                    rlVertex2f(trianglePositions[0].x, trianglePositions[0].y);
+                    rlColor4ub(0, 255, 0, 255);
+                    rlVertex2f(trianglePositions[1].x, trianglePositions[1].y);
+                    rlColor4ub(0, 0, 255, 255);
+                    rlVertex2f(trianglePositions[2].x, trianglePositions[2].y);
+                rlEnd();
+            }
+
+            // Render the vertex handles, reacting to mouse movement/input
+            for (unsigned int i = 0; i < 3; i++)
+            {
+                // Draw handle fill focused by mouse
+                if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius))
+                    DrawCircleV(trianglePositions[i], handleRadius, ColorAlpha(DARKGRAY, 0.5f));
+
+                // Draw handle fill selected
+                if (i == triangleIndex) DrawCircleV(trianglePositions[i], handleRadius, DARKGRAY);
+
+                // Draw handle outline
+                DrawCircleLinesV(trianglePositions[i], handleRadius, BLACK);
+            }
+
+            // Draw controls
+            DrawText("SPACE: Toggle lines mode", 10, 10, 20, DARKGRAY);
+            DrawText("LEFT-RIGHT: Toggle backface culling", 10, 40, 20, DARKGRAY);
+            DrawText("MOUSE: Click and drag vertex points", 10, 70, 20, DARKGRAY);
+            DrawText("R: Reset triangle to start positions", 10, 100, 20, DARKGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_rounded_rectangle_drawing.c b/raylib/examples/shapes/shapes_rounded_rectangle_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_rounded_rectangle_drawing.c
@@ -0,0 +1,96 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - rounded rectangle drawing
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 2.5
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rounded rectangle drawing");
+
+    float roundness = 0.2f;
+    float width = 200.0f;
+    float height = 100.0f;
+    float segments = 0.0f;
+    float lineThick = 1.0f;
+
+    bool drawRect = false;
+    bool drawRoundedRect = true;
+    bool drawRoundedLines = false;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        Rectangle rec = { ((float)GetScreenWidth() - width - 250)/2, (GetScreenHeight() - height)/2.0f, (float)width, (float)height };
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawLine(560, 0, 560, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f));
+            DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f));
+
+            if (drawRect) DrawRectangleRec(rec, Fade(GOLD, 0.6f));
+            if (drawRoundedRect) DrawRectangleRounded(rec, roundness, (int)segments, Fade(MAROON, 0.2f));
+            if (drawRoundedLines) DrawRectangleRoundedLinesEx(rec, roundness, (int)segments, lineThick, Fade(MAROON, 0.4f));
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", TextFormat("%.2f", width), &width, 0, (float)GetScreenWidth() - 300);
+            GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", TextFormat("%.2f", height), &height, 0, (float)GetScreenHeight() - 50);
+            GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", TextFormat("%.2f", roundness), &roundness, 0.0f, 1.0f);
+            GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", TextFormat("%.2f", lineThick), &lineThick, 0, 20);
+            GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", TextFormat("%.2f", segments), &segments, 0, 60);
+
+            GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect);
+            GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines);
+            GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", &drawRect);
+            //------------------------------------------------------------------------------
+
+            DrawText(TextFormat("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 640, 280, 10, (segments >= 4)? MAROON : DARKGRAY);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_simple_particles.c b/raylib/examples/shapes/shapes_simple_particles.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_simple_particles.c
@@ -0,0 +1,284 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - simple particles
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jordi Santonja (@JordSant)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>         // Required for: calloc(), free()
+#include <math.h>           // Required for: cosf(), sinf()
+
+#define MAX_PARTICLES 3000  // Max number of particles
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef enum ParticleType {
+    WATER = 0,
+    SMOKE,
+    FIRE
+} ParticleType;
+
+static const char particleTypeNames[3][10] = { "WATER", "SMOKE", "FIRE" };
+
+typedef struct Particle {
+    ParticleType type;      // Particle type (WATER, SMOKE, FIRE)
+    Vector2 position;       // Particle position on screen
+    Vector2 velocity;       // Particle current speed and direction
+    float radius;           // Particle radius
+    Color color;            // Particle color
+
+    float lifeTime;         // Particle life time
+    bool alive;             // Particle alive: inside screen and life time
+} Particle;
+
+typedef struct CircularBuffer {
+    int head;               // Index for the next write
+    int tail;               // Index for the next read
+    Particle *buffer;       // Particle buffer array
+} CircularBuffer;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void EmitParticle(CircularBuffer *circularBuffer, Vector2 emitterPosition, ParticleType type);
+static Particle *AddToCircularBuffer(CircularBuffer *circularBuffer);
+static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int screenHeight);
+static void UpdateCircularBuffer(CircularBuffer *circularBuffer);
+static void DrawParticles(CircularBuffer *circularBuffer);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - simple particles");
+
+    // Definition of particles
+    Particle *particles = (Particle*)RL_CALLOC(MAX_PARTICLES, sizeof(Particle)); // Particle array
+    CircularBuffer circularBuffer = { 0, 0, particles };
+
+    // Particle emitter parameters
+    int emissionRate = -2;          // Negative: on average every -X frames. Positive: particles per frame
+    ParticleType currentType = WATER;
+    Vector2 emitterPosition = { screenWidth/2.0f, screenHeight/2.0f };
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Emit new particles: when emissionRate is 1, emit every frame
+        if (emissionRate < 0)
+        {
+            if (rand()%(-emissionRate) == 0) EmitParticle(&circularBuffer, emitterPosition, currentType);
+        }
+        else
+        {
+            for (int i = 0; i <= emissionRate; i++) EmitParticle(&circularBuffer, emitterPosition, currentType);
+        }
+
+        // Update the parameters of each particle
+        UpdateParticles(&circularBuffer, screenWidth, screenHeight);
+
+        // Remove dead particles from the circular buffer
+        UpdateCircularBuffer(&circularBuffer);
+
+        // Change Particle Emission Rate (UP/DOWN arrows)
+        if (IsKeyPressed(KEY_UP)) emissionRate++;
+        if (IsKeyPressed(KEY_DOWN)) emissionRate--;
+
+        // Change Particle Type (LEFT/RIGHT arrows)
+        if (IsKeyPressed(KEY_RIGHT)) (currentType == FIRE)? (currentType = WATER) : currentType++;
+        if (IsKeyPressed(KEY_LEFT)) (currentType == WATER)? (currentType = FIRE) : currentType--;
+
+        if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) emitterPosition = GetMousePosition();
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Call the function with a loop to draw all particles
+            DrawParticles(&circularBuffer);
+
+            // Draw UI and Instructions
+            DrawRectangle(5, 5, 315, 75, Fade(SKYBLUE, 0.5f));
+            DrawRectangleLines(5, 5, 315, 75, BLUE);
+
+            DrawText("CONTROLS:", 15, 15, 10, BLACK);
+            DrawText("UP/DOWN: Change Particle Emission Rate", 15, 35, 10, BLACK);
+            DrawText("LEFT/RIGHT: Change Particle Type (Water, Smoke, Fire)", 15, 55, 10, BLACK);
+
+            if (emissionRate < 0) DrawText(TextFormat("Particles every %d frames | Type: %s", -emissionRate, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
+            else DrawText(TextFormat("%d Particles per frame | Type: %s", emissionRate + 1, particleTypeNames[currentType]), 15, 95, 10, DARKGRAY);
+
+            DrawFPS(screenWidth - 80, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    RL_FREE(particles);     // Free particles array data
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+static void EmitParticle(CircularBuffer *circularBuffer, Vector2 emitterPosition, ParticleType type)
+{
+    Particle *newParticle = AddToCircularBuffer(circularBuffer);
+
+    // If buffer is full, newParticle is NULL
+    if (newParticle != NULL)
+    {
+        // Fill particle properties
+        newParticle->position = emitterPosition;
+        newParticle->alive = true;
+        newParticle->lifeTime = 0.0f;
+        newParticle->type = type;
+        float speed = (float)(rand()%10)/5.0f;
+        switch (type)
+        {
+            case WATER:
+            {
+                newParticle->radius = 5.0f;
+                newParticle->color = BLUE;
+            } break;
+            case SMOKE:
+            {
+                newParticle->radius = 7.0f;
+                newParticle->color = GRAY;
+            } break;
+            case FIRE:
+            {
+                newParticle->radius = 10.0f;
+                newParticle->color = YELLOW;
+                speed /= 10.0f;
+            } break;
+            default: break;
+        }
+
+        float direction = (float)(rand()%360);
+        newParticle->velocity = (Vector2){ speed*cosf(direction*DEG2RAD), speed*sinf(direction*DEG2RAD) };
+    }
+}
+
+static Particle *AddToCircularBuffer(CircularBuffer *circularBuffer)
+{
+    Particle *particle = NULL;
+
+    // Check if buffer full
+    if (((circularBuffer->head + 1)%MAX_PARTICLES) != circularBuffer->tail)
+    {
+        // Add new particle to the head position and advance head
+        particle = &circularBuffer->buffer[circularBuffer->head];
+        circularBuffer->head = (circularBuffer->head + 1)%MAX_PARTICLES;
+    }
+
+    return particle;
+}
+
+static void UpdateParticles(CircularBuffer *circularBuffer, int screenWidth, int screenHeight)
+{
+    for (int i = circularBuffer->tail; i != circularBuffer->head; i = (i + 1)%MAX_PARTICLES)
+    {
+        // Update particle life and positions
+        circularBuffer->buffer[i].lifeTime += 1.0f/60.0f; // 60 FPS -> 1/60 seconds per frame
+
+        switch (circularBuffer->buffer[i].type)
+        {
+            case WATER:
+            {
+                circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x;
+                circularBuffer->buffer[i].velocity.y += 0.2f;   // Gravity
+                circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
+            } break;
+            case SMOKE:
+            {
+                circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x;
+                circularBuffer->buffer[i].velocity.y -= 0.05f;  // Upwards
+                circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
+                circularBuffer->buffer[i].radius += 0.5f;       // Increment radius: smoke expands
+                circularBuffer->buffer[i].color.a -= 4;         // Decrement alpha: smoke fades
+
+                // If alpha transparent, particle dies
+                if (circularBuffer->buffer[i].color.a < 4) circularBuffer->buffer[i].alive = false;
+            } break;
+            case FIRE:
+            {
+                // Add a little horizontal oscillation to fire particles
+                circularBuffer->buffer[i].position.x += circularBuffer->buffer[i].velocity.x + cosf(circularBuffer->buffer[i].lifeTime*215.0f);
+                circularBuffer->buffer[i].velocity.y -= 0.05f;  // Upwards
+                circularBuffer->buffer[i].position.y += circularBuffer->buffer[i].velocity.y;
+                circularBuffer->buffer[i].radius -= 0.15f;      // Decrement radius: fire shrinks
+                circularBuffer->buffer[i].color.g -= 3;         // Decrement green: fire turns reddish starting from yellow
+
+                // If radius too small, particle dies
+                if (circularBuffer->buffer[i].radius <= 0.02f) circularBuffer->buffer[i].alive = false;
+            } break;
+            default: break;
+        }
+
+        // Disable particle when out of screen
+        Vector2 center = circularBuffer->buffer[i].position;
+        float radius = circularBuffer->buffer[i].radius;
+
+        if ((center.x < -radius) || (center.x > (screenWidth + radius)) ||
+            (center.y < -radius) || (center.y > (screenHeight + radius)))
+        {
+            circularBuffer->buffer[i].alive = false;
+        }
+    }
+}
+
+static void UpdateCircularBuffer(CircularBuffer *circularBuffer)
+{
+    // Update circular buffer: advance tail over dead particles
+    while ((circularBuffer->tail != circularBuffer->head) && !circularBuffer->buffer[circularBuffer->tail].alive)
+    {
+        circularBuffer->tail = (circularBuffer->tail + 1)%MAX_PARTICLES;
+    }
+}
+
+static void DrawParticles(CircularBuffer *circularBuffer)
+{
+    for (int i = circularBuffer->tail; i != circularBuffer->head; i = (i + 1)%MAX_PARTICLES)
+    {
+        if (circularBuffer->buffer[i].alive)
+        {
+            DrawCircleV(circularBuffer->buffer[i].position,
+                        circularBuffer->buffer[i].radius,
+                        circularBuffer->buffer[i].color);
+        }
+    }
+}
diff --git a/raylib/examples/shapes/shapes_splines_drawing.c b/raylib/examples/shapes/shapes_splines_drawing.c
--- a/raylib/examples/shapes/shapes_splines_drawing.c
+++ b/raylib/examples/shapes/shapes_splines_drawing.c
@@ -2,12 +2,14 @@
 *
 *   raylib [shapes] example - splines drawing
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 5.0, last time updated with raylib 5.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2023 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -20,8 +22,11 @@
 
 #define MAX_SPLINE_POINTS      32
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 // Cubic Bezier spline control points
-// NOTE: Every segment has two control points 
+// NOTE: Every segment has two control points
 typedef struct {
     Vector2 start;
     Vector2 end;
@@ -55,17 +60,17 @@
         { 520.0f, 60.0f },
         { 710.0f, 260.0f },
     };
-    
-    // Array required for spline bezier-cubic, 
+
+    // Array required for spline bezier-cubic,
     // including control points interleaved with start-end segment points
     Vector2 pointsInterleaved[3*(MAX_SPLINE_POINTS - 1) + 1] = { 0 };
-    
+
     int pointCount = 5;
     int selectedPoint = -1;
     int focusedPoint = -1;
     Vector2 *selectedControlPoint = NULL;
     Vector2 *focusedControlPoint = NULL;
-    
+
     // Cubic Bezier control points initialization
     ControlPoint control[MAX_SPLINE_POINTS-1] = { 0 };
     for (int i = 0; i < pointCount - 1; i++)
@@ -77,9 +82,9 @@
     // Spline config variables
     float splineThickness = 8.0f;
     int splineTypeActive = SPLINE_LINEAR; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier
-    bool splineTypeEditMode = false; 
+    bool splineTypeEditMode = false;
     bool splineHelpersActive = true;
-    
+
     SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
@@ -99,45 +104,50 @@
         }
 
         // Spline point focus and selection logic
-        for (int i = 0; i < pointCount; i++)
+        if ((selectedPoint == -1) && ((splineTypeActive != SPLINE_BEZIER) || (selectedControlPoint == NULL)))
         {
-            if (CheckCollisionPointCircle(GetMousePosition(), points[i], 8.0f))
+            focusedPoint = -1;
+            for (int i = 0; i < pointCount; i++)
             {
-                focusedPoint = i;
-                if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedPoint = i; 
-                break;
+                if (CheckCollisionPointCircle(GetMousePosition(), points[i], 8.0f))
+                {
+                    focusedPoint = i;
+                    break;
+                }
             }
-            else focusedPoint = -1;
+            if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) selectedPoint = focusedPoint;
         }
-        
+
         // Spline point movement logic
         if (selectedPoint >= 0)
         {
             points[selectedPoint] = GetMousePosition();
             if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1;
         }
-        
+
         // Cubic Bezier spline control points logic
         if ((splineTypeActive == SPLINE_BEZIER) && (focusedPoint == -1))
         {
             // Spline control point focus and selection logic
-            for (int i = 0; i < pointCount - 1; i++)
+            if (selectedControlPoint == NULL)
             {
-                if (CheckCollisionPointCircle(GetMousePosition(), control[i].start, 6.0f))
-                {
-                    focusedControlPoint = &control[i].start;
-                    if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].start; 
-                    break;
-                }
-                else if (CheckCollisionPointCircle(GetMousePosition(), control[i].end, 6.0f))
+                focusedControlPoint = NULL;
+                for (int i = 0; i < pointCount - 1; i++)
                 {
-                    focusedControlPoint = &control[i].end;
-                    if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) selectedControlPoint = &control[i].end; 
-                    break;
+                    if (CheckCollisionPointCircle(GetMousePosition(), control[i].start, 6.0f))
+                    {
+                        focusedControlPoint = &control[i].start;
+                        break;
+                    }
+                    else if (CheckCollisionPointCircle(GetMousePosition(), control[i].end, 6.0f))
+                    {
+                        focusedControlPoint = &control[i].end;
+                        break;
+                    }
                 }
-                else focusedControlPoint = NULL;
+                if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) selectedControlPoint = focusedControlPoint;
             }
-            
+
             // Spline control point movement logic
             if (selectedControlPoint != NULL)
             {
@@ -145,12 +155,15 @@
                 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedControlPoint = NULL;
             }
         }
-        
+
         // Spline selection logic
         if (IsKeyPressed(KEY_ONE)) splineTypeActive = 0;
         else if (IsKeyPressed(KEY_TWO)) splineTypeActive = 1;
         else if (IsKeyPressed(KEY_THREE)) splineTypeActive = 2;
         else if (IsKeyPressed(KEY_FOUR)) splineTypeActive = 3;
+
+        // Clear selection when changing to a spline without control points
+        if (IsKeyPressed(KEY_ONE) || IsKeyPressed(KEY_TWO) || IsKeyPressed(KEY_THREE)) selectedControlPoint = NULL;
         //----------------------------------------------------------------------------------
 
         // Draw
@@ -158,7 +171,7 @@
         BeginDrawing();
 
             ClearBackground(RAYWHITE);
-        
+
             if (splineTypeActive == SPLINE_LINEAR)
             {
                 // Draw spline: linear
@@ -181,7 +194,7 @@
             {
                 // Draw spline: catmull-rom
                 DrawSplineCatmullRom(points, pointCount, splineThickness, RED); // Provide connected points array
-                
+
                 /*
                 for (int i = 0; i < (pointCount - 3); i++)
                 {
@@ -192,20 +205,20 @@
             }
             else if (splineTypeActive == SPLINE_BEZIER)
             {
-                // NOTE: Cubic-bezier spline requires the 2 control points of each segnment to be 
+                // NOTE: Cubic-bezier spline requires the 2 control points of each segnment to be
                 // provided interleaved with the start and end point of every segment
-                for (int i = 0; i < (pointCount - 1); i++) 
+                for (int i = 0; i < (pointCount - 1); i++)
                 {
                     pointsInterleaved[3*i] = points[i];
                     pointsInterleaved[3*i + 1] = control[i].start;
                     pointsInterleaved[3*i + 2] = control[i].end;
                 }
-                
+
                 pointsInterleaved[3*(pointCount - 1)] = points[pointCount - 1];
 
                 // Draw spline: cubic-bezier (with control points)
                 DrawSplineBezierCubic(pointsInterleaved, 3*(pointCount - 1) + 1, splineThickness, RED);
-                
+
                 /*
                 for (int i = 0; i < 3*(pointCount - 1); i += 3)
                 {
@@ -224,7 +237,7 @@
                     else if (focusedControlPoint == &control[i].end) DrawCircleV(control[i].end, 8, GREEN);
                     DrawLineEx(points[i], control[i].start, 1.0f, LIGHTGRAY);
                     DrawLineEx(points[i + 1], control[i].end, 1.0f, LIGHTGRAY);
-                
+
                     // Draw spline control lines
                     DrawLineV(points[i], control[i].start, GRAY);
                     //DrawLineV(control[i].start, control[i].end, LIGHTGRAY);
@@ -247,18 +260,20 @@
             }
 
             // Check all possible UI states that require controls lock
-            if (splineTypeEditMode) GuiLock();
-            
+            if (splineTypeEditMode || (selectedPoint != -1) || (selectedControlPoint != NULL)) GuiLock();
+
             // Draw spline config
             GuiLabel((Rectangle){ 12, 62, 140, 24 }, TextFormat("Spline thickness: %i", (int)splineThickness));
             GuiSliderBar((Rectangle){ 12, 60 + 24, 140, 16 }, NULL, NULL, &splineThickness, 1.0f, 40.0f);
 
             GuiCheckBox((Rectangle){ 12, 110, 20, 20 }, "Show point helpers", &splineHelpersActive);
 
-            GuiUnlock();
+            if (splineTypeEditMode) GuiUnlock();
 
             GuiLabel((Rectangle){ 12, 10, 140, 24 }, "Spline type:");
             if (GuiDropdownBox((Rectangle){ 12, 8 + 24, 140, 28 }, "LINEAR;BSPLINE;CATMULLROM;BEZIER", &splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode;
+
+            GuiUnlock();
 
         EndDrawing();
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/shapes/shapes_starfield_effect.c b/raylib/examples/shapes/shapes_starfield_effect.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_starfield_effect.c
@@ -0,0 +1,146 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - starfield effect
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6-dev
+*
+*   Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 JP Mortiboys (@themushroompirates)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"    // Required for: Lerp()
+
+#define STAR_COUNT 420
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - starfield effect");
+
+    Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.69f);
+
+    // Speed at which we fly forward
+    float speed = 10.0f/9.0f;
+
+    // We're either drawing lines or circles
+    bool drawLines = true;
+
+    Vector3 stars[STAR_COUNT] = { 0 };
+    Vector2 starsScreenPos[STAR_COUNT] = { 0 };
+
+    // Setup the stars with a random position
+    for (int i = 0; i < STAR_COUNT; i++)
+    {
+        stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
+        stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
+        stars[i].z = 1.0f;
+    }
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Change speed based on mouse
+        float mouseMove = GetMouseWheelMove();
+        if ((int)mouseMove != 0) speed += 2.0f*mouseMove/9.0f;
+        if (speed < 0.0f) speed = 0.1f;
+        else if (speed > 2.0f) speed = 2.0f;
+
+        // Toggle lines / points with space bar
+        if (IsKeyPressed(KEY_SPACE)) drawLines = !drawLines;
+
+        float dt = GetFrameTime();
+        for (int i = 0; i < STAR_COUNT; i++)
+        {
+            // Update star's timer
+            stars[i].z -= dt*speed;
+
+            // Calculate the screen position
+            starsScreenPos[i] = (Vector2){
+                screenWidth*0.5f + stars[i].x/stars[i].z,
+                screenHeight*0.5f + stars[i].y/stars[i].z,
+            };
+
+            // If the star is too old, or offscreen, it dies and we make a new random one
+            if ((stars[i].z < 0.0f) || (starsScreenPos[i].x < 0) || (starsScreenPos[i].y < 0.0f) ||
+                (starsScreenPos[i].x > screenWidth) || (starsScreenPos[i].y > screenHeight))
+            {
+                stars[i].x = GetRandomValue(-screenWidth*0.5f, screenWidth*0.5f);
+                stars[i].y = GetRandomValue(-screenHeight*0.5f, screenHeight*0.5f);
+                stars[i].z = 1.0f;
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(bgColor);
+
+            for (int i = 0; i < STAR_COUNT; i++)
+            {
+                if (drawLines)
+                {
+                    // Get the time a little while ago for this star, but clamp it
+                    float t = Clamp(stars[i].z + 1.0f/32.0f, 0.0f, 1.0f);
+
+                    // If it's different enough from the current time, we proceed
+                    if ((t - stars[i].z) > 1e-3)
+                    {
+                        // Calculate the screen position of the old point
+                        Vector2 startPos = (Vector2){
+                            screenWidth*0.5f + stars[i].x/t,
+                            screenHeight*0.5f + stars[i].y/t,
+                        };
+
+                        // Draw a line connecting the old point to the current point
+                        DrawLineV(startPos, starsScreenPos[i], RAYWHITE);
+                    }
+                }
+                else
+                {
+                    // Make the radius grow as the star ages
+                    float radius = Lerp(stars[i].z, 1.0f, 5.0f);
+
+                    // Draw the circle
+                    DrawCircleV(starsScreenPos[i], radius, RAYWHITE);
+                }
+            }
+
+            DrawText(TextFormat("[MOUSE WHEEL] Current Speed: %.0f", 9.0f*speed/2.0f), 10, 40, 20, RAYWHITE);
+            DrawText(TextFormat("[SPACE] Current draw mode: %s", drawLines ? "Lines" : "Circles"), 10, 70, 20, RAYWHITE);
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_top_down_lights.c b/raylib/examples/shapes/shapes_top_down_lights.c
--- a/raylib/examples/shapes/shapes_top_down_lights.c
+++ b/raylib/examples/shapes/shapes_top_down_lights.c
@@ -2,14 +2,16 @@
 *
 *   raylib [shapes] example - top down lights
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 Jeffery Myers (@JeffM2501)
+*   Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501)
 *
 ********************************************************************************************/
 
@@ -18,14 +20,17 @@
 #include "rlgl.h"
 
 // Custom Blend Modes
-#define RLGL_SRC_ALPHA 0x0302
-#define RLGL_MIN 0x8007
-#define RLGL_MAX 0x8008
+#define RLGL_SRC_ALPHA  0x0302
+#define RLGL_MIN        0x8007
+#define RLGL_MAX        0x8008
 
-#define MAX_BOXES     20
-#define MAX_SHADOWS   MAX_BOXES*3         // MAX_BOXES *3. Each box can cast up to two shadow volumes for the edges it is away from, and one for the box itself
-#define MAX_LIGHTS    16
+#define MAX_BOXES       20
+#define MAX_SHADOWS     MAX_BOXES*3     // MAX_BOXES*3 - Each box can cast up to two shadow volumes for the edges it is away from, and one for the box itself
+#define MAX_LIGHTS      16
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 // Shadow geometry type
 typedef struct ShadowGeometry {
     Vector2 vertices[4];
@@ -46,167 +51,26 @@
     int shadowCount;
 } LightInfo;
 
-
-LightInfo lights[MAX_LIGHTS] = { 0 };
+//------------------------------------------------------------------------------------
+// Global Variables Definition
+//------------------------------------------------------------------------------------
+static LightInfo lights[MAX_LIGHTS] = { 0 };
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 // Move a light and mark it as dirty so that we update it's mask next frame
-void MoveLight(int slot, float x, float y)
-{
-    lights[slot].dirty = true;
-    lights[slot].position.x = x; 
-    lights[slot].position.y = y;
-
-    // update the cached bounds
-    lights[slot].bounds.x = x - lights[slot].outerRadius;
-    lights[slot].bounds.y = y - lights[slot].outerRadius;
-}
-
+static void MoveLight(int slot, float x, float y);
 // Compute a shadow volume for the edge
-// It takes the edge and projects it back by the light radius and turns it into a quad
-void ComputeShadowVolumeForEdge(int slot, Vector2 sp, Vector2 ep)
-{
-    if (lights[slot].shadowCount >= MAX_SHADOWS) return;
-
-    float extension = lights[slot].outerRadius*2;
-
-    Vector2 spVector = Vector2Normalize(Vector2Subtract(sp, lights[slot].position));
-    Vector2 spProjection = Vector2Add(sp, Vector2Scale(spVector, extension));
-
-    Vector2 epVector = Vector2Normalize(Vector2Subtract(ep, lights[slot].position));
-    Vector2 epProjection = Vector2Add(ep, Vector2Scale(epVector, extension));
-
-    lights[slot].shadows[lights[slot].shadowCount].vertices[0] = sp;
-    lights[slot].shadows[lights[slot].shadowCount].vertices[1] = ep;
-    lights[slot].shadows[lights[slot].shadowCount].vertices[2] = epProjection;
-    lights[slot].shadows[lights[slot].shadowCount].vertices[3] = spProjection;
-
-    lights[slot].shadowCount++;
-}
-
+static void ComputeShadowVolumeForEdge(int slot, Vector2 sp, Vector2 ep);
 // Draw the light and shadows to the mask for a light
-void DrawLightMask(int slot)
-{
-    // Use the light mask
-    BeginTextureMode(lights[slot].mask);
-
-        ClearBackground(WHITE);
-
-        // Force the blend mode to only set the alpha of the destination
-        rlSetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MIN);
-        rlSetBlendMode(BLEND_CUSTOM);
-
-        // If we are valid, then draw the light radius to the alpha mask
-        if (lights[slot].valid) DrawCircleGradient((int)lights[slot].position.x, (int)lights[slot].position.y, lights[slot].outerRadius, ColorAlpha(WHITE, 0), WHITE);
-        
-        rlDrawRenderBatchActive();
-
-        // Cut out the shadows from the light radius by forcing the alpha to maximum
-        rlSetBlendMode(BLEND_ALPHA);
-        rlSetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MAX);
-        rlSetBlendMode(BLEND_CUSTOM);
-
-        // Draw the shadows to the alpha mask
-        for (int i = 0; i < lights[slot].shadowCount; i++)
-        {
-            DrawTriangleFan(lights[slot].shadows[i].vertices, 4, WHITE);
-        }
-
-        rlDrawRenderBatchActive();
-        
-        // Go back to normal blend mode
-        rlSetBlendMode(BLEND_ALPHA);
-
-    EndTextureMode();
-}
-
+static void DrawLightMask(int slot);
 // Setup a light
-void SetupLight(int slot, float x, float y, float radius)
-{
-    lights[slot].active = true;
-    lights[slot].valid = false;  // The light must prove it is valid
-    lights[slot].mask = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
-    lights[slot].outerRadius = radius;
-
-    lights[slot].bounds.width = radius * 2;
-    lights[slot].bounds.height = radius * 2;
-
-    MoveLight(slot, x, y);
-
-    // Force the render texture to have something in it
-    DrawLightMask(slot);
-}
-
+static void SetupLight(int slot, float x, float y, float radius);
 // See if a light needs to update it's mask
-bool UpdateLight(int slot, Rectangle* boxes, int count)
-{
-    if (!lights[slot].active || !lights[slot].dirty) return false;
-
-    lights[slot].dirty = false;
-    lights[slot].shadowCount = 0;
-    lights[slot].valid = false;
-
-    for (int i = 0; i < count; i++)
-    {
-        // Are we in a box? if so we are not valid
-        if (CheckCollisionPointRec(lights[slot].position, boxes[i])) return false;
-
-        // If this box is outside our bounds, we can skip it
-        if (!CheckCollisionRecs(lights[slot].bounds, boxes[i])) continue;
-
-        // Check the edges that are on the same side we are, and cast shadow volumes out from them
-        
-        // Top
-        Vector2 sp = (Vector2){ boxes[i].x, boxes[i].y };
-        Vector2 ep = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y };
-
-        if (lights[slot].position.y > ep.y) ComputeShadowVolumeForEdge(slot, sp, ep);
-
-        // Right
-        sp = ep;
-        ep.y += boxes[i].height;
-        if (lights[slot].position.x < ep.x) ComputeShadowVolumeForEdge(slot, sp, ep);
-
-        // Bottom
-        sp = ep;
-        ep.x -= boxes[i].width;
-        if (lights[slot].position.y < ep.y) ComputeShadowVolumeForEdge(slot, sp, ep);
-
-        // Left
-        sp = ep;
-        ep.y -= boxes[i].height;
-        if (lights[slot].position.x > ep.x) ComputeShadowVolumeForEdge(slot, sp, ep);
-
-        // The box itself
-        lights[slot].shadows[lights[slot].shadowCount].vertices[0] = (Vector2){ boxes[i].x, boxes[i].y };
-        lights[slot].shadows[lights[slot].shadowCount].vertices[1] = (Vector2){ boxes[i].x, boxes[i].y + boxes[i].height };
-        lights[slot].shadows[lights[slot].shadowCount].vertices[2] = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y + boxes[i].height };
-        lights[slot].shadows[lights[slot].shadowCount].vertices[3] = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y };
-        lights[slot].shadowCount++;
-    }
-
-    lights[slot].valid = true;
-
-    DrawLightMask(slot);
-
-    return true;
-}
-
+static bool UpdateLight(int slot, Rectangle* boxes, int count);
 // Set up some boxes
-void SetupBoxes(Rectangle *boxes, int *count)
-{
-    boxes[0] = (Rectangle){ 150,80, 40, 40 };
-    boxes[1] = (Rectangle){ 1200, 700, 40, 40 };
-    boxes[2] = (Rectangle){ 200, 600, 40, 40 };
-    boxes[3] = (Rectangle){ 1000, 50, 40, 40 };
-    boxes[4] = (Rectangle){ 500, 350, 40, 40 };
-
-    for (int i = 5; i < MAX_BOXES; i++)
-    {
-        boxes[i] = (Rectangle){(float)GetRandomValue(0,GetScreenWidth()), (float)GetRandomValue(0,GetScreenHeight()), (float)GetRandomValue(10,100), (float)GetRandomValue(10,100) };
-    }
-
-    *count = MAX_BOXES;
-}
+static void SetupBoxes(Rectangle *boxes, int *count);
 
 //------------------------------------------------------------------------------------
 // Program main entry point
@@ -217,7 +81,7 @@
     //--------------------------------------------------------------------------------------
     const int screenWidth = 800;
     const int screenHeight = 450;
-    
+
     InitWindow(screenWidth, screenHeight, "raylib [shapes] example - top down lights");
 
     // Initialize our 'world' of boxes
@@ -272,7 +136,7 @@
         {
             // Build up the light mask
             BeginTextureMode(lightMask);
-            
+
                 ClearBackground(BLACK);
 
                 // Force the blend mode to only set the alpha of the destination
@@ -298,10 +162,10 @@
         BeginDrawing();
 
             ClearBackground(BLACK);
-            
+
             // Draw the tile background
             DrawTextureRec(backgroundTexture, (Rectangle){ 0, 0, (float)GetScreenWidth(), (float)GetScreenHeight() }, Vector2Zero(), WHITE);
-            
+
             // Overlay the shadows from all the lights
             DrawTextureRec(lightMask.texture, (Rectangle){ 0, 0, (float)GetScreenWidth(), -(float)GetScreenHeight() }, Vector2Zero(), ColorAlpha(WHITE, showLines? 0.75f : 1.0f));
 
@@ -353,4 +217,166 @@
     //--------------------------------------------------------------------------------------
 
     return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+// Move a light and mark it as dirty so that we update it's mask next frame
+static void MoveLight(int slot, float x, float y)
+{
+    lights[slot].dirty = true;
+    lights[slot].position.x = x;
+    lights[slot].position.y = y;
+
+    // update the cached bounds
+    lights[slot].bounds.x = x - lights[slot].outerRadius;
+    lights[slot].bounds.y = y - lights[slot].outerRadius;
+}
+
+// Compute a shadow volume for the edge
+// It takes the edge and projects it back by the light radius and turns it into a quad
+static void ComputeShadowVolumeForEdge(int slot, Vector2 sp, Vector2 ep)
+{
+    if (lights[slot].shadowCount >= MAX_SHADOWS) return;
+
+    float extension = lights[slot].outerRadius*2;
+
+    Vector2 spVector = Vector2Normalize(Vector2Subtract(sp, lights[slot].position));
+    Vector2 spProjection = Vector2Add(sp, Vector2Scale(spVector, extension));
+
+    Vector2 epVector = Vector2Normalize(Vector2Subtract(ep, lights[slot].position));
+    Vector2 epProjection = Vector2Add(ep, Vector2Scale(epVector, extension));
+
+    lights[slot].shadows[lights[slot].shadowCount].vertices[0] = sp;
+    lights[slot].shadows[lights[slot].shadowCount].vertices[1] = ep;
+    lights[slot].shadows[lights[slot].shadowCount].vertices[2] = epProjection;
+    lights[slot].shadows[lights[slot].shadowCount].vertices[3] = spProjection;
+
+    lights[slot].shadowCount++;
+}
+
+// Setup a light
+static void SetupLight(int slot, float x, float y, float radius)
+{
+    lights[slot].active = true;
+    lights[slot].valid = false;  // The light must prove it is valid
+    lights[slot].mask = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
+    lights[slot].outerRadius = radius;
+
+    lights[slot].bounds.width = radius*2;
+    lights[slot].bounds.height = radius*2;
+
+    MoveLight(slot, x, y);
+
+    // Force the render texture to have something in it
+    DrawLightMask(slot);
+}
+
+// See if a light needs to update it's mask
+static bool UpdateLight(int slot, Rectangle* boxes, int count)
+{
+    if (!lights[slot].active || !lights[slot].dirty) return false;
+
+    lights[slot].dirty = false;
+    lights[slot].shadowCount = 0;
+    lights[slot].valid = false;
+
+    for (int i = 0; i < count; i++)
+    {
+        // Are we in a box? if so we are not valid
+        if (CheckCollisionPointRec(lights[slot].position, boxes[i])) return false;
+
+        // If this box is outside our bounds, we can skip it
+        if (!CheckCollisionRecs(lights[slot].bounds, boxes[i])) continue;
+
+        // Check the edges that are on the same side we are, and cast shadow volumes out from them
+
+        // Top
+        Vector2 sp = (Vector2){ boxes[i].x, boxes[i].y };
+        Vector2 ep = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y };
+
+        if (lights[slot].position.y > ep.y) ComputeShadowVolumeForEdge(slot, sp, ep);
+
+        // Right
+        sp = ep;
+        ep.y += boxes[i].height;
+        if (lights[slot].position.x < ep.x) ComputeShadowVolumeForEdge(slot, sp, ep);
+
+        // Bottom
+        sp = ep;
+        ep.x -= boxes[i].width;
+        if (lights[slot].position.y < ep.y) ComputeShadowVolumeForEdge(slot, sp, ep);
+
+        // Left
+        sp = ep;
+        ep.y -= boxes[i].height;
+        if (lights[slot].position.x > ep.x) ComputeShadowVolumeForEdge(slot, sp, ep);
+
+        // The box itself
+        lights[slot].shadows[lights[slot].shadowCount].vertices[0] = (Vector2){ boxes[i].x, boxes[i].y };
+        lights[slot].shadows[lights[slot].shadowCount].vertices[1] = (Vector2){ boxes[i].x, boxes[i].y + boxes[i].height };
+        lights[slot].shadows[lights[slot].shadowCount].vertices[2] = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y + boxes[i].height };
+        lights[slot].shadows[lights[slot].shadowCount].vertices[3] = (Vector2){ boxes[i].x + boxes[i].width, boxes[i].y };
+        lights[slot].shadowCount++;
+    }
+
+    lights[slot].valid = true;
+
+    DrawLightMask(slot);
+
+    return true;
+}
+
+// Draw the light and shadows to the mask for a light
+static void DrawLightMask(int slot)
+{
+    // Use the light mask
+    BeginTextureMode(lights[slot].mask);
+
+        ClearBackground(WHITE);
+
+        // Force the blend mode to only set the alpha of the destination
+        rlSetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MIN);
+        rlSetBlendMode(BLEND_CUSTOM);
+
+        // If we are valid, then draw the light radius to the alpha mask
+        if (lights[slot].valid) DrawCircleGradient((int)lights[slot].position.x, (int)lights[slot].position.y, lights[slot].outerRadius, ColorAlpha(WHITE, 0), WHITE);
+
+        rlDrawRenderBatchActive();
+
+        // Cut out the shadows from the light radius by forcing the alpha to maximum
+        rlSetBlendMode(BLEND_ALPHA);
+        rlSetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MAX);
+        rlSetBlendMode(BLEND_CUSTOM);
+
+        // Draw the shadows to the alpha mask
+        for (int i = 0; i < lights[slot].shadowCount; i++)
+        {
+            DrawTriangleFan(lights[slot].shadows[i].vertices, 4, WHITE);
+        }
+
+        rlDrawRenderBatchActive();
+
+        // Go back to normal blend mode
+        rlSetBlendMode(BLEND_ALPHA);
+
+    EndTextureMode();
+}
+
+// Set up some boxes
+static void SetupBoxes(Rectangle *boxes, int *count)
+{
+    boxes[0] = (Rectangle){ 150,80, 40, 40 };
+    boxes[1] = (Rectangle){ 1200, 700, 40, 40 };
+    boxes[2] = (Rectangle){ 200, 600, 40, 40 };
+    boxes[3] = (Rectangle){ 1000, 50, 40, 40 };
+    boxes[4] = (Rectangle){ 500, 350, 40, 40 };
+
+    for (int i = 5; i < MAX_BOXES; i++)
+    {
+        boxes[i] = (Rectangle){(float)GetRandomValue(0,GetScreenWidth()), (float)GetRandomValue(0,GetScreenHeight()), (float)GetRandomValue(10,100), (float)GetRandomValue(10,100) };
+    }
+
+    *count = MAX_BOXES;
 }
diff --git a/raylib/examples/shapes/shapes_triangle_strip.c b/raylib/examples/shapes/shapes_triangle_strip.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_triangle_strip.c
@@ -0,0 +1,111 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - triangle strip
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Jopestpe (@jopestpe)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jopestpe (@jopestpe)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include <math.h>
+
+#define RAYGUI_IMPLEMENTATION
+#include "raygui.h"                 // Required for GUI controls
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - triangle strip");
+
+    Vector2 points[122] = { 0 };
+    Vector2 center = { (screenWidth/2.0f) - 125.0f, screenHeight/2.0f };
+    float segments = 6.0f;
+    float insideRadius = 100.0f;
+    float outsideRadius = 150.0f;
+    bool outline = true;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        int pointCount = (int)(segments);
+        float angleStep = (360.0f/pointCount)*DEG2RAD;
+
+        for (int i = 0, i2 = 0; i < pointCount; i++, i2 += 2)
+        {
+            float angle1 = i*angleStep;
+            points[i2] = (Vector2){ center.x + cosf(angle1)*insideRadius, center.y + sinf(angle1)*insideRadius };
+            float angle2 = angle1 + angleStep/2.0f;
+            points[i2 + 1] = (Vector2){ center.x + cosf(angle2)*outsideRadius, center.y + sinf(angle2)*outsideRadius };
+        }
+
+        points[pointCount*2] = points[0];
+        points[pointCount*2 + 1] = points[1];
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < pointCount; i++)
+            {
+                Vector2 a = points[i*2];
+                Vector2 b = points[i*2 + 1];
+                Vector2 c = points[i*2 + 2];
+                Vector2 d = points[i*2 + 3];
+
+                float angle1 = i*angleStep;
+                DrawTriangle(c, b, a, ColorFromHSV(angle1*RAD2DEG, 1.0f, 1.0f));
+                DrawTriangle(d, b, c, ColorFromHSV((angle1 + angleStep/2)*RAD2DEG, 1.0f, 1.0f));
+
+                if (outline)
+                {
+                    DrawTriangleLines(a, b, c, BLACK);
+                    DrawTriangleLines(c, b, d, BLACK);
+                }
+            }
+
+            DrawLine(580, 0, 580, GetScreenHeight(), (Color){ 218, 218, 218, 255 });
+            DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), (Color){ 232, 232, 232, 255 });
+
+            // Draw GUI controls
+            //------------------------------------------------------------------------------
+            GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Segments", TextFormat("%.0f", segments), &segments, 6.0f, 60.0f);
+            GuiCheckBox((Rectangle){ 640, 70, 20, 20 }, "Outline", &outline);
+            //------------------------------------------------------------------------------
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/shapes/shapes_vector_angle.c b/raylib/examples/shapes/shapes_vector_angle.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/shapes/shapes_vector_angle.c
@@ -0,0 +1,125 @@
+/*******************************************************************************************
+*
+*   raylib [shapes] example - vector angle
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 1.0, last time updated with raylib 5.0
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2023-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"        // Required for: Vector2LineAngle()
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [shapes] example - vector angle");
+
+    Vector2 v0 = { screenWidth/2.0f, screenHeight/2.0f };
+    Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f });
+    Vector2 v2 = { 0 };             // Updated with mouse position
+
+    float angle = 0.0f;             // Angle in degrees
+    int angleMode = 0;              // 0-Vector2Angle(), 1-Vector2LineAngle()
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float startangle = 0.0f;
+
+        if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG;
+        if (angleMode == 1) startangle = 0.0f;
+
+        v2 = GetMousePosition();
+
+        if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode;
+
+        if ((angleMode == 0) && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) v1 = GetMousePosition();
+
+        if (angleMode == 0)
+        {
+            // Calculate angle between two vectors, considering a common origin (v0)
+            Vector2 v1Normal = Vector2Normalize(Vector2Subtract(v1, v0));
+            Vector2 v2Normal = Vector2Normalize(Vector2Subtract(v2, v0));
+
+            angle = Vector2Angle(v1Normal, v2Normal)*RAD2DEG;
+        }
+        else if (angleMode == 1)
+        {
+            // Calculate angle defined by a two vectors line, in reference to horizontal line
+            angle = Vector2LineAngle(v0, v2)*RAD2DEG;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            if (angleMode == 0)
+            {
+                DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, BLACK);
+                DrawText("Right Click to Move V2", 10, 30, 20, DARKGRAY);
+
+                DrawLineEx(v0, v1, 2.0f, BLACK);
+                DrawLineEx(v0, v2, 2.0f, RED);
+
+                DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f));
+            }
+            else if (angleMode == 1)
+            {
+                DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK);
+
+                DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY);
+                DrawLineEx(v0, v2, 2.0f, RED);
+
+                DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f));
+            }
+
+            DrawText("v0", (int)v0.x, (int)v0.y, 10, DARKGRAY);
+
+            // If the line from v0 to v1 would overlap the text, move it's position up 10
+            if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", (int)v1.x, (int)v1.y-10, 10, DARKGRAY);
+            if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", (int)v1.x, (int)v1.y, 10, DARKGRAY);
+
+            // If angle mode 1, use v1 to emphasize the horizontal line
+            if (angleMode == 1) DrawText("v1", (int)v0.x + 40, (int)v0.y, 10, DARKGRAY);
+
+            // position adjusted by -10 so it isn't hidden by cursor
+            DrawText("v2", (int)v2.x-10, (int)v2.y-10, 10, DARKGRAY);
+
+            DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY);
+            DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/text/text_3d_drawing.c b/raylib/examples/text/text_3d_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_3d_drawing.c
@@ -0,0 +1,696 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - 3d drawing
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   NOTE: Draw a 2D text in 3D space, each letter is drawn in a quad (or 2 quads if backface is set)
+*   where the texture coodinates of each quad map to the texture coordinates of the glyphs
+*   inside the font texture
+*
+*   A more efficient approach, i believe, would be to render the text in a render texture and
+*   map that texture to a plane and render that, or maybe a shader but my method allows more
+*   flexibility...for example to change position of each letter individually to make somethink
+*   like a wavy text effect
+*
+*   Special thanks to:
+*        @Nighten for the DrawTextStyle() code https://github.com/NightenDushi/Raylib_DrawTextStyle
+*        Chris Camacho (codifies - http://bedroomcoders.co.uk/) for the alpha discard shader
+*
+*   Example originally created with raylib 3.5, last time updated with raylib 4.0
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2021-2025 Vlad Adrian (@demizdor)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "rlgl.h"
+
+#include <stddef.h>     // Required for: NULL
+#include <math.h>       // Required for: sinf()
+
+#if defined(PLATFORM_DESKTOP)
+    #define GLSL_VERSION            330
+#else   // PLATFORM_ANDROID, PLATFORM_WEB
+    #define GLSL_VERSION            100
+#endif
+
+//--------------------------------------------------------------------------------------
+// Global variables
+//--------------------------------------------------------------------------------------
+#define LETTER_BOUNDRY_SIZE     0.25f
+#define TEXT_MAX_LAYERS         32
+#define LETTER_BOUNDRY_COLOR    VIOLET
+
+bool SHOW_LETTER_BOUNDRY = false;
+bool SHOW_TEXT_BOUNDRY = false;
+
+//--------------------------------------------------------------------------------------
+// Types and Structures Definition
+//--------------------------------------------------------------------------------------
+// Configuration structure for waving the text
+typedef struct WaveTextConfig {
+    Vector3 waveRange;
+    Vector3 waveSpeed;
+    Vector3 waveOffset;
+} WaveTextConfig;
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+// Draw a codepoint in 3D space
+static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint);
+// Draw a 2D text in 3D space
+static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint);
+
+// Draw a 2D text in 3D space and wave the parts that start with '~~' and end with '~~'
+// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
+static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig *config, float time, Color tint);
+// Measure a text in 3D ignoring the `~~` chars
+static Vector3 MeasureTextWave3D(Font font, const char *text, float fontSize, float fontSpacing, float lineSpacing);
+// Generates a nice color with a random hue
+static Color GenerateRandomColor(float s, float v);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT|FLAG_VSYNC_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - 3d drawing");
+
+    bool spin = true;        // Spin the camera?
+    bool multicolor = false; // Multicolor mode
+
+    // Define the camera to look into our 3d world
+    Camera3D camera = { 0 };
+    camera.position = (Vector3){ -10.0f, 15.0f, -10.0f };   // Camera position
+    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
+    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
+    camera.fovy = 45.0f;                                    // Camera field-of-view Y
+    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
+
+    int camera_mode = CAMERA_ORBITAL;
+
+    Vector3 cubePosition = { 0.0f, 1.0f, 0.0f };
+    Vector3 cubeSize = { 2.0f, 2.0f, 2.0f };
+
+    // Use the default font
+    Font font = GetFontDefault();
+    float fontSize = 0.8f;
+    float fontSpacing = 0.05f;
+    float lineSpacing = -0.1f;
+
+    // Set the text (using markdown!)
+    char text[64] = "Hello ~~World~~ in 3D!";
+    Vector3 tbox = {0};
+    int layers = 1;
+    int quads = 0;
+    float layerDistance = 0.01f;
+
+    WaveTextConfig wcfg;
+    wcfg.waveSpeed.x = wcfg.waveSpeed.y = 3.0f; wcfg.waveSpeed.z = 0.5f;
+    wcfg.waveOffset.x = wcfg.waveOffset.y = wcfg.waveOffset.z = 0.35f;
+    wcfg.waveRange.x = wcfg.waveRange.y = wcfg.waveRange.z = 0.45f;
+
+    float time = 0.0f;
+
+    // Setup a light and dark color
+    Color light = MAROON;
+    Color dark = RED;
+
+    // Load the alpha discard shader
+    Shader alphaDiscard = LoadShader(NULL, TextFormat("resources/shaders/glsl%i/alpha_discard.fs", GLSL_VERSION));
+
+    // Array filled with multiple random colors (when multicolor mode is set)
+    Color multi[TEXT_MAX_LAYERS] = {0};
+
+    DisableCursor();                    // Limit cursor to relative movement inside the window
+
+    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())        // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&camera, camera_mode);
+
+        // Handle font files dropped
+        if (IsFileDropped())
+        {
+            FilePathList droppedFiles = LoadDroppedFiles();
+
+            // NOTE: We only support first ttf file dropped
+            if (IsFileExtension(droppedFiles.paths[0], ".ttf"))
+            {
+                UnloadFont(font);
+                font = LoadFontEx(droppedFiles.paths[0], (int)fontSize, 0, 0);
+            }
+            else if (IsFileExtension(droppedFiles.paths[0], ".fnt"))
+            {
+                UnloadFont(font);
+                font = LoadFont(droppedFiles.paths[0]);
+                fontSize = (float)font.baseSize;
+            }
+
+            UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
+        }
+
+        // Handle Events
+        if (IsKeyPressed(KEY_F1)) SHOW_LETTER_BOUNDRY = !SHOW_LETTER_BOUNDRY;
+        if (IsKeyPressed(KEY_F2)) SHOW_TEXT_BOUNDRY = !SHOW_TEXT_BOUNDRY;
+        if (IsKeyPressed(KEY_F3))
+        {
+            // Handle camera change
+            spin = !spin;
+            // we need to reset the camera when changing modes
+            camera = (Camera3D){ 0 };
+            camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
+            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
+            camera.fovy = 45.0f;                                    // Camera field-of-view Y
+            camera.projection = CAMERA_PERSPECTIVE;                 // Camera mode type
+
+            if (spin)
+            {
+                camera.position = (Vector3){ -10.0f, 15.0f, -10.0f };   // Camera position
+                camera_mode = CAMERA_ORBITAL;
+            }
+            else
+            {
+                camera.position = (Vector3){ 10.0f, 10.0f, -10.0f };   // Camera position
+                camera_mode = CAMERA_FREE;
+            }
+        }
+
+        // Handle clicking the cube
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+        {
+            Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
+
+            // Check collision between ray and box
+            RayCollision collision = GetRayCollisionBox(ray,
+                            (BoundingBox){(Vector3){ cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2 },
+                                          (Vector3){ cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2 }});
+            if (collision.hit)
+            {
+                // Generate new random colors
+                light = GenerateRandomColor(0.5f, 0.78f);
+                dark = GenerateRandomColor(0.4f, 0.58f);
+            }
+        }
+
+        // Handle text layers changes
+        if (IsKeyPressed(KEY_HOME)) { if (layers > 1) --layers; }
+        else if (IsKeyPressed(KEY_END)) { if (layers < TEXT_MAX_LAYERS) ++layers; }
+
+        // Handle text changes
+        if (IsKeyPressed(KEY_LEFT)) fontSize -= 0.5f;
+        else if (IsKeyPressed(KEY_RIGHT)) fontSize += 0.5f;
+        else if (IsKeyPressed(KEY_UP)) fontSpacing -= 0.1f;
+        else if (IsKeyPressed(KEY_DOWN)) fontSpacing += 0.1f;
+        else if (IsKeyPressed(KEY_PAGE_UP)) lineSpacing -= 0.1f;
+        else if (IsKeyPressed(KEY_PAGE_DOWN)) lineSpacing += 0.1f;
+        else if (IsKeyDown(KEY_INSERT)) layerDistance -= 0.001f;
+        else if (IsKeyDown(KEY_DELETE)) layerDistance += 0.001f;
+        else if (IsKeyPressed(KEY_TAB))
+        {
+            multicolor = !multicolor;   // Enable /disable multicolor mode
+
+            if (multicolor)
+            {
+                // Fill color array with random colors
+                for (int i = 0; i < TEXT_MAX_LAYERS; i++)
+                {
+                    multi[i] = GenerateRandomColor(0.5f, 0.8f);
+                    multi[i].a = GetRandomValue(0, 255);
+                }
+            }
+        }
+
+        // Handle text input
+        int ch = GetCharPressed();
+        if (IsKeyPressed(KEY_BACKSPACE))
+        {
+            // Remove last char
+            int len = TextLength(text);
+            if (len > 0) text[len - 1] = '\0';
+        }
+        else if (IsKeyPressed(KEY_ENTER))
+        {
+            // handle newline
+            int len = TextLength(text);
+            if (len < sizeof(text) - 1)
+            {
+                text[len] = '\n';
+                text[len+1] ='\0';
+            }
+        }
+        else
+        {
+            // append only printable chars
+            int len = TextLength(text);
+            if (len < sizeof(text) - 1)
+            {
+                text[len] = ch;
+                text[len+1] ='\0';
+            }
+        }
+
+        // Measure 3D text so we can center it
+        tbox = MeasureTextWave3D(font, text, fontSize, fontSpacing, lineSpacing);
+
+        quads = 0;                      // Reset quad counter
+        time += GetFrameTime();         // Update timer needed by `DrawTextWave3D()`
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(camera);
+                DrawCubeV(cubePosition, cubeSize, dark);
+                DrawCubeWires(cubePosition, 2.1f, 2.1f, 2.1f, light);
+
+                DrawGrid(10, 2.0f);
+
+                // Use a shader to handle the depth buffer issue with transparent textures
+                // NOTE: more info at https://bedroomcoders.co.uk/posts/198
+                BeginShaderMode(alphaDiscard);
+
+                    // Draw the 3D text above the red cube
+                    rlPushMatrix();
+                        rlRotatef(90.0f, 1.0f, 0.0f, 0.0f);
+                        rlRotatef(90.0f, 0.0f, 0.0f, -1.0f);
+
+                        for (int i = 0; i < layers; i++)
+                        {
+                            Color clr = light;
+                            if (multicolor) clr = multi[i];
+                            DrawTextWave3D(font, text, (Vector3){ -tbox.x/2.0f, layerDistance*i, -4.5f }, fontSize, fontSpacing, lineSpacing, true, &wcfg, time, clr);
+                        }
+
+                        // Draw the text boundry if set
+                        if (SHOW_TEXT_BOUNDRY) DrawCubeWiresV((Vector3){ 0.0f, 0.0f, -4.5f + tbox.z/2 }, tbox, dark);
+                    rlPopMatrix();
+
+                    // Don't draw the letter boundries for the 3D text below
+                    bool slb = SHOW_LETTER_BOUNDRY;
+                    SHOW_LETTER_BOUNDRY = false;
+
+                    // Draw 3D options (use default font)
+                    //-------------------------------------------------------------------------
+                    rlPushMatrix();
+                        rlRotatef(180.0f, 0.0f, 1.0f, 0.0f);
+                        char *opt = (char *)TextFormat("< SIZE: %2.1f >", fontSize);
+                        quads += TextLength(opt);
+                        Vector2 m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        Vector3 pos = { -m.x/2.0f, 0.01f, 2.0f};
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, BLUE);
+                        pos.z += 0.5f + m.y;
+
+                        opt = (char *)TextFormat("< SPACING: %2.1f >", fontSpacing);
+                        quads += TextLength(opt);
+                        m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        pos.x = -m.x/2.0f;
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, BLUE);
+                        pos.z += 0.5f + m.y;
+
+                        opt = (char *)TextFormat("< LINE: %2.1f >", lineSpacing);
+                        quads += TextLength(opt);
+                        m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        pos.x = -m.x/2.0f;
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, BLUE);
+                        pos.z += 0.5f + m.y;
+
+                        opt = (char *)TextFormat("< LBOX: %3s >", slb? "ON" : "OFF");
+                        quads += TextLength(opt);
+                        m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        pos.x = -m.x/2.0f;
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, RED);
+                        pos.z += 0.5f + m.y;
+
+                        opt = (char *)TextFormat("< TBOX: %3s >", SHOW_TEXT_BOUNDRY? "ON" : "OFF");
+                        quads += TextLength(opt);
+                        m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        pos.x = -m.x/2.0f;
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, RED);
+                        pos.z += 0.5f + m.y;
+
+                        opt = (char *)TextFormat("< LAYER DISTANCE: %.3f >", layerDistance);
+                        quads += TextLength(opt);
+                        m = MeasureTextEx(GetFontDefault(), opt, 0.8f, 0.1f);
+                        pos.x = -m.x/2.0f;
+                        DrawText3D(GetFontDefault(), opt, pos, 0.8f, 0.1f, 0.0f, false, DARKPURPLE);
+                    rlPopMatrix();
+                    //-------------------------------------------------------------------------
+
+                    // Draw 3D info text (use default font)
+                    //-------------------------------------------------------------------------
+                    opt = "All the text displayed here is in 3D";
+                    quads += 36;
+                    m = MeasureTextEx(GetFontDefault(), opt, 1.0f, 0.05f);
+                    pos = (Vector3){-m.x/2.0f, 0.01f, 2.0f};
+                    DrawText3D(GetFontDefault(), opt, pos, 1.0f, 0.05f, 0.0f, false, DARKBLUE);
+                    pos.z += 1.5f + m.y;
+
+                    opt = "press [Left]/[Right] to change the font size";
+                    quads += 44;
+                    m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
+                    pos.x = -m.x/2.0f;
+                    DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, DARKBLUE);
+                    pos.z += 0.5f + m.y;
+
+                    opt = "press [Up]/[Down] to change the font spacing";
+                    quads += 44;
+                    m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
+                    pos.x = -m.x/2.0f;
+                    DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, DARKBLUE);
+                    pos.z += 0.5f + m.y;
+
+                    opt = "press [PgUp]/[PgDown] to change the line spacing";
+                    quads += 48;
+                    m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
+                    pos.x = -m.x/2.0f;
+                    DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, DARKBLUE);
+                    pos.z += 0.5f + m.y;
+
+                    opt = "press [F1] to toggle the letter boundry";
+                    quads += 39;
+                    m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
+                    pos.x = -m.x/2.0f;
+                    DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, DARKBLUE);
+                    pos.z += 0.5f + m.y;
+
+                    opt = "press [F2] to toggle the text boundry";
+                    quads += 37;
+                    m = MeasureTextEx(GetFontDefault(), opt, 0.6f, 0.05f);
+                    pos.x = -m.x/2.0f;
+                    DrawText3D(GetFontDefault(), opt, pos, 0.6f, 0.05f, 0.0f, false, DARKBLUE);
+                    //-------------------------------------------------------------------------
+
+                    SHOW_LETTER_BOUNDRY = slb;
+                EndShaderMode();
+
+            EndMode3D();
+
+            // Draw 2D info text & stats
+            //-------------------------------------------------------------------------
+            DrawText("Drag & drop a font file to change the font!\nType something, see what happens!\n\n"
+            "Press [F3] to toggle the camera", 10, 35, 10, BLACK);
+
+            quads += TextLength(text)*2*layers;
+            char *tmp = (char *)TextFormat("%2i layer(s) | %s camera | %4i quads (%4i verts)", layers, spin? "ORBITAL" : "FREE", quads, quads*4);
+            int width = MeasureText(tmp, 10);
+            DrawText(tmp, screenWidth - 20 - width, 10, 10, DARKGREEN);
+
+            tmp = "[Home]/[End] to add/remove 3D text layers";
+            width = MeasureText(tmp, 10);
+            DrawText(tmp, screenWidth - 20 - width, 25, 10, DARKGRAY);
+
+            tmp = "[Insert]/[Delete] to increase/decrease distance between layers";
+            width = MeasureText(tmp, 10);
+            DrawText(tmp, screenWidth - 20 - width, 40, 10, DARKGRAY);
+
+            tmp = "click the [CUBE] for a random color";
+            width = MeasureText(tmp, 10);
+            DrawText(tmp, screenWidth - 20 - width, 55, 10, DARKGRAY);
+
+            tmp = "[Tab] to toggle multicolor mode";
+            width = MeasureText(tmp, 10);
+            DrawText(tmp, screenWidth - 20 - width, 70, 10, DARKGRAY);
+            //-------------------------------------------------------------------------
+
+            DrawFPS(10, 10);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadFont(font);
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definitions
+//--------------------------------------------------------------------------------------
+// Draw codepoint at specified position in 3D space
+static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
+{
+    // Character index position in sprite font
+    // NOTE: In case a codepoint is not available in the font, index returned points to '?'
+    int index = GetGlyphIndex(font, codepoint);
+    float scale = fontSize/(float)font.baseSize;
+
+    // Character destination rectangle on screen
+    // NOTE: We consider charsPadding on drawing
+    position.x += (float)(font.glyphs[index].offsetX - font.glyphPadding)*scale;
+    position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding)*scale;
+
+    // Character source rectangle from font texture atlas
+    // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
+    Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding,
+                         font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding };
+
+    float width = (float)(font.recs[index].width + 2.0f*font.glyphPadding)*scale;
+    float height = (float)(font.recs[index].height + 2.0f*font.glyphPadding)*scale;
+
+    if (font.texture.id > 0)
+    {
+        const float x = 0.0f;
+        const float y = 0.0f;
+        const float z = 0.0f;
+
+        // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
+        const float tx = srcRec.x/font.texture.width;
+        const float ty = srcRec.y/font.texture.height;
+        const float tw = (srcRec.x+srcRec.width)/font.texture.width;
+        const float th = (srcRec.y+srcRec.height)/font.texture.height;
+
+        if (SHOW_LETTER_BOUNDRY) DrawCubeWiresV((Vector3){ position.x + width/2, position.y, position.z + height/2}, (Vector3){ width, LETTER_BOUNDRY_SIZE, height }, LETTER_BOUNDRY_COLOR);
+
+        rlCheckRenderBatchLimit(4 + 4*backface);
+        rlSetTexture(font.texture.id);
+
+        rlPushMatrix();
+            rlTranslatef(position.x, position.y, position.z);
+
+            rlBegin(RL_QUADS);
+                rlColor4ub(tint.r, tint.g, tint.b, tint.a);
+
+                // Front Face
+                rlNormal3f(0.0f, 1.0f, 0.0f);                                   // Normal Pointing Up
+                rlTexCoord2f(tx, ty); rlVertex3f(x,         y, z);              // Top Left Of The Texture and Quad
+                rlTexCoord2f(tx, th); rlVertex3f(x,         y, z + height);     // Bottom Left Of The Texture and Quad
+                rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height);     // Bottom Right Of The Texture and Quad
+                rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z);              // Top Right Of The Texture and Quad
+
+                if (backface)
+                {
+                    // Back Face
+                    rlNormal3f(0.0f, -1.0f, 0.0f);                              // Normal Pointing Down
+                    rlTexCoord2f(tx, ty); rlVertex3f(x,         y, z);          // Top Right Of The Texture and Quad
+                    rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z);          // Top Left Of The Texture and Quad
+                    rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
+                    rlTexCoord2f(tx, th); rlVertex3f(x,         y, z + height); // Bottom Right Of The Texture and Quad
+                }
+            rlEnd();
+        rlPopMatrix();
+
+        rlSetTexture(0);
+    }
+}
+
+// Draw a 2D text in 3D space
+static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint)
+{
+    int length = TextLength(text);          // Total length in bytes of the text, scanned by codepoints in loop
+
+    float textOffsetY = 0.0f;               // Offset between lines (on line break '\n')
+    float textOffsetX = 0.0f;               // Offset X to next character to draw
+
+    float scale = fontSize/(float)font.baseSize;
+
+    for (int i = 0; i < length;)
+    {
+        // Get next codepoint from byte string and glyph index in font
+        int codepointByteCount = 0;
+        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
+        int index = GetGlyphIndex(font, codepoint);
+
+        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+        if (codepoint == 0x3f) codepointByteCount = 1;
+
+        if (codepoint == '\n')
+        {
+            // NOTE: Fixed line spacing of 1.5 line-height
+            // TODO: Support custom line spacing defined by user
+            textOffsetY += fontSize + lineSpacing;
+            textOffsetX = 0.0f;
+        }
+        else
+        {
+            if ((codepoint != ' ') && (codepoint != '\t'))
+            {
+                DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
+            }
+
+            if (font.glyphs[index].advanceX == 0) textOffsetX += (float)font.recs[index].width*scale + fontSpacing;
+            else textOffsetX += (float)font.glyphs[index].advanceX*scale + fontSpacing;
+        }
+
+        i += codepointByteCount;   // Move text bytes counter to next codepoint
+    }
+}
+
+// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`
+// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
+static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig* config, float time, Color tint)
+{
+    int length = TextLength(text);          // Total length in bytes of the text, scanned by codepoints in loop
+
+    float textOffsetY = 0.0f;               // Offset between lines (on line break '\n')
+    float textOffsetX = 0.0f;               // Offset X to next character to draw
+
+    float scale = fontSize/(float)font.baseSize;
+
+    bool wave = false;
+
+    for (int i = 0, k = 0; i < length; ++k)
+    {
+        // Get next codepoint from byte string and glyph index in font
+        int codepointByteCount = 0;
+        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
+        int index = GetGlyphIndex(font, codepoint);
+
+        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+        if (codepoint == 0x3f) codepointByteCount = 1;
+
+        if (codepoint == '\n')
+        {
+            // NOTE: Fixed line spacing of 1.5 line-height
+            // TODO: Support custom line spacing defined by user
+            textOffsetY += fontSize + lineSpacing;
+            textOffsetX = 0.0f;
+            k = 0;
+        }
+        else if (codepoint == '~')
+        {
+            if (GetCodepoint(&text[i+1], &codepointByteCount) == '~')
+            {
+                codepointByteCount += 1;
+                wave = !wave;
+            }
+        }
+        else
+        {
+            if ((codepoint != ' ') && (codepoint != '\t'))
+            {
+                Vector3 pos = position;
+                if (wave) // Apply the wave effect
+                {
+                    pos.x += sinf(time*config->waveSpeed.x-k*config->waveOffset.x)*config->waveRange.x;
+                    pos.y += sinf(time*config->waveSpeed.y-k*config->waveOffset.y)*config->waveRange.y;
+                    pos.z += sinf(time*config->waveSpeed.z-k*config->waveOffset.z)*config->waveRange.z;
+                }
+
+                DrawTextCodepoint3D(font, codepoint, (Vector3){ pos.x + textOffsetX, pos.y, pos.z + textOffsetY }, fontSize, backface, tint);
+            }
+
+            if (font.glyphs[index].advanceX == 0) textOffsetX += (float)font.recs[index].width*scale + fontSpacing;
+            else textOffsetX += (float)font.glyphs[index].advanceX*scale + fontSpacing;
+        }
+
+        i += codepointByteCount;   // Move text bytes counter to next codepoint
+    }
+}
+
+// Measure a text in 3D ignoring the `~~` chars
+static Vector3 MeasureTextWave3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing)
+{
+    int len = TextLength(text);
+    int tempLen = 0;                // Used to count longer text line num chars
+    int lenCounter = 0;
+
+    float tempTextWidth = 0.0f;     // Used to count longer text line width
+
+    float scale = fontSize/(float)font.baseSize;
+    float textHeight = scale;
+    float textWidth = 0.0f;
+
+    int letter = 0;                 // Current character
+    int index = 0;                  // Index position in sprite font
+
+    for (int i = 0; i < len; i++)
+    {
+        int next = 0;
+        letter = GetCodepoint(&text[i], &next);
+        index = GetGlyphIndex(font, letter);
+
+        // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+        // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
+        if (letter == 0x3f) next = 1;
+        i += next - 1;
+
+        if (letter != '\n')
+        {
+            if (letter == '~' && GetCodepoint(&text[i+1], &next) == '~')
+            {
+                i++;
+            }
+            else
+            {
+                lenCounter++;
+                if (font.glyphs[index].advanceX != 0) textWidth += font.glyphs[index].advanceX*scale;
+                else textWidth += (font.recs[index].width + font.glyphs[index].offsetX)*scale;
+            }
+        }
+        else
+        {
+            if (tempTextWidth < textWidth) tempTextWidth = textWidth;
+            lenCounter = 0;
+            textWidth = 0.0f;
+            textHeight += fontSize + lineSpacing;
+        }
+
+        if (tempLen < lenCounter) tempLen = lenCounter;
+    }
+
+    if (tempTextWidth < textWidth) tempTextWidth = textWidth;
+
+    Vector3 vec = { 0 };
+    vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing); // Adds chars spacing to measure
+    vec.y = 0.25f;
+    vec.z = textHeight;
+
+    return vec;
+}
+
+// Generates a nice color with a random hue
+static Color GenerateRandomColor(float s, float v)
+{
+    const float Phi = 0.618033988749895f; // Golden ratio conjugate
+    float h = (float)GetRandomValue(0, 360);
+    h = fmodf((h + h*Phi), 360.0f);
+    return ColorFromHSV(h, s, v);
+}
diff --git a/raylib/examples/text/text_codepoints_loading.c b/raylib/examples/text/text_codepoints_loading.c
--- a/raylib/examples/text/text_codepoints_loading.c
+++ b/raylib/examples/text/text_codepoints_loading.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Codepoints loading
+*   raylib [text] example - codepoints loading
 *
-*   Example originally created with raylib 4.2, last time updated with raylib 2.5
+*   Example complexity rating: [★★★☆] 3/4
 *
+*   Example originally created with raylib 4.2, last time updated with raylib 4.2
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2022-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -21,6 +23,9 @@
 // this text will be scanned to get all the required codepoints
 static char *text = "いろはにほへと　ちりぬるを\nわかよたれそ　つねならむ\nうゐのおくやま　けふこえて\nあさきゆめみし　ゑひもせす";
 
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
 // Remove codepoint duplicates if requested
 static int *CodepointRemoveDuplicates(int *codepoints, int codepointCount, int *codepointResultCount);
 
@@ -36,7 +41,8 @@
 
     InitWindow(screenWidth, screenHeight, "raylib [text] example - codepoints loading");
 
-    // Get codepoints from text
+    // Convert each utf-8 character into its
+    // corresponding codepoint in the font file
     int codepointCount = 0;
     int *codepoints = LoadCodepoints(text, &codepointCount);
 
@@ -105,7 +111,7 @@
             }
             else
             {
-                // Draw provided text with laoded font, containing all required codepoint glyphs
+                // Draw provided text with loaded font, containing all required codepoint glyphs
                 DrawTextEx(font, text, (Vector2) { 160, 110 }, 48, 5, BLACK);
             }
 
@@ -125,6 +131,9 @@
     return 0;
 }
 
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
 // Remove codepoint duplicates if requested
 // WARNING: This process could be a bit slow if there text to process is very long
 static int *CodepointRemoveDuplicates(int *codepoints, int codepointCount, int *codepointsResultCount)
diff --git a/raylib/examples/text/text_draw_3d.c b/raylib/examples/text/text_draw_3d.c
deleted file mode 100644
--- a/raylib/examples/text/text_draw_3d.c
+++ /dev/null
@@ -1,750 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [text] example - Draw 3d
-*
-*   NOTE: Draw a 2D text in 3D space, each letter is drawn in a quad (or 2 quads if backface is set)
-*   where the texture coodinates of each quad map to the texture coordinates of the glyphs
-*   inside the font texture.
-*
-*   A more efficient approach, i believe, would be to render the text in a render texture and
-*   map that texture to a plane and render that, or maybe a shader but my method allows more
-*   flexibility...for example to change position of each letter individually to make somethink
-*   like a wavy text effect.
-*    
-*   Special thanks to:
-*        @Nighten for the DrawTextStyle() code https://github.com/NightenDushi/Raylib_DrawTextStyle
-*        Chris Camacho (codifies - http://bedroomcoders.co.uk/) for the alpha discard shader
-*
-*   Example originally created with raylib 3.5, last time updated with raylib 4.0
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2021-2024 Vlad Adrian (@demizdor)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-#include "rlgl.h"
-
-#include <stddef.h>     // Required for: NULL
-#include <math.h>       // Required for: sinf()
-
-// To make it work with the older RLGL module just comment the line below
-#define RAYLIB_NEW_RLGL
-
-//--------------------------------------------------------------------------------------
-// Globals
-//--------------------------------------------------------------------------------------
-#define LETTER_BOUNDRY_SIZE     0.25f
-#define TEXT_MAX_LAYERS         32
-#define LETTER_BOUNDRY_COLOR    VIOLET
-
-bool SHOW_LETTER_BOUNDRY = false;
-bool SHOW_TEXT_BOUNDRY = false;
-
-//--------------------------------------------------------------------------------------
-// Data Types definition
-//--------------------------------------------------------------------------------------
-
-// Configuration structure for waving the text
-typedef struct WaveTextConfig {
-    Vector3 waveRange;
-    Vector3 waveSpeed;
-    Vector3 waveOffset;
-} WaveTextConfig;
-
-//--------------------------------------------------------------------------------------
-// Module Functions Declaration
-//--------------------------------------------------------------------------------------
-// Draw a codepoint in 3D space
-static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint);
-// Draw a 2D text in 3D space
-static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint);
-// Measure a text in 3D. For some reason `MeasureTextEx()` just doesn't seem to work so i had to use this instead.
-static Vector3 MeasureText3D(Font font, const char *text, float fontSize, float fontSpacing, float lineSpacing);
-
-// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`.
-// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
-static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig *config, float time, Color tint);
-// Measure a text in 3D ignoring the `~~` chars.
-static Vector3 MeasureTextWave3D(Font font, const char *text, float fontSize, float fontSpacing, float lineSpacing);
-// Generates a nice color with a random hue
-static Color GenerateRandomColor(float s, float v);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT|FLAG_VSYNC_HINT);
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - draw 2D text in 3D");
-
-    bool spin = true;        // Spin the camera?
-    bool multicolor = false; // Multicolor mode
-
-    // Define the camera to look into our 3d world
-    Camera3D camera = { 0 };
-    camera.position = (Vector3){ -10.0f, 15.0f, -10.0f };   // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
-    camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
-    camera.fovy = 45.0f;                                    // Camera field-of-view Y
-    camera.projection = CAMERA_PERSPECTIVE;                 // Camera projection type
-
-    int camera_mode = CAMERA_ORBITAL;
-
-    Vector3 cubePosition = { 0.0f, 1.0f, 0.0f };
-    Vector3 cubeSize = { 2.0f, 2.0f, 2.0f };
-
-    // Use the default font
-    Font font = GetFontDefault();
-    float fontSize = 8.0f;
-    float fontSpacing = 0.5f;
-    float lineSpacing = -1.0f;
-
-    // Set the text (using markdown!)
-    char text[64] = "Hello ~~World~~ in 3D!";
-    Vector3 tbox = {0};
-    int layers = 1;
-    int quads = 0;
-    float layerDistance = 0.01f;
-
-    WaveTextConfig wcfg;
-    wcfg.waveSpeed.x = wcfg.waveSpeed.y = 3.0f; wcfg.waveSpeed.z = 0.5f;
-    wcfg.waveOffset.x = wcfg.waveOffset.y = wcfg.waveOffset.z = 0.35f;
-    wcfg.waveRange.x = wcfg.waveRange.y = wcfg.waveRange.z = 0.45f;
-
-    float time = 0.0f;
-
-    // Setup a light and dark color
-    Color light = MAROON;
-    Color dark = RED;
-
-    // Load the alpha discard shader
-    Shader alphaDiscard = LoadShader(NULL, "resources/shaders/glsl330/alpha_discard.fs");
-
-    // Array filled with multiple random colors (when multicolor mode is set)
-    Color multi[TEXT_MAX_LAYERS] = {0};
-
-    DisableCursor();                    // Limit cursor to relative movement inside the window
-
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        UpdateCamera(&camera, camera_mode);
-        
-        // Handle font files dropped
-        if (IsFileDropped())
-        {
-            FilePathList droppedFiles = LoadDroppedFiles();
-
-            // NOTE: We only support first ttf file dropped
-            if (IsFileExtension(droppedFiles.paths[0], ".ttf"))
-            {
-                UnloadFont(font);
-                font = LoadFontEx(droppedFiles.paths[0], (int)fontSize, 0, 0);
-            }
-            else if (IsFileExtension(droppedFiles.paths[0], ".fnt"))
-            {
-                UnloadFont(font);
-                font = LoadFont(droppedFiles.paths[0]);
-                fontSize = (float)font.baseSize;
-            }
-            
-            UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
-        }
-
-        // Handle Events
-        if (IsKeyPressed(KEY_F1)) SHOW_LETTER_BOUNDRY = !SHOW_LETTER_BOUNDRY;
-        if (IsKeyPressed(KEY_F2)) SHOW_TEXT_BOUNDRY = !SHOW_TEXT_BOUNDRY;
-        if (IsKeyPressed(KEY_F3))
-        {
-            // Handle camera change
-            spin = !spin;
-            // we need to reset the camera when changing modes
-            camera = (Camera3D){ 0 };
-            camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };          // Camera looking at point
-            camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };              // Camera up vector (rotation towards target)
-            camera.fovy = 45.0f;                                    // Camera field-of-view Y
-            camera.projection = CAMERA_PERSPECTIVE;                 // Camera mode type
-
-            if (spin)
-            {
-                camera.position = (Vector3){ -10.0f, 15.0f, -10.0f };   // Camera position
-                camera_mode = CAMERA_ORBITAL;
-            }
-            else
-            {
-                camera.position = (Vector3){ 10.0f, 10.0f, -10.0f };   // Camera position
-                camera_mode = CAMERA_FREE;
-            }
-        }
-
-        // Handle clicking the cube
-        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
-        {
-            Ray ray = GetScreenToWorldRay(GetMousePosition(), camera);
-
-            // Check collision between ray and box
-            RayCollision collision = GetRayCollisionBox(ray,
-                            (BoundingBox){(Vector3){ cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2 },
-                                          (Vector3){ cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2 }});
-            if (collision.hit)
-            {
-                // Generate new random colors
-                light = GenerateRandomColor(0.5f, 0.78f);
-                dark = GenerateRandomColor(0.4f, 0.58f);
-            }
-        }
-
-        // Handle text layers changes
-        if (IsKeyPressed(KEY_HOME)) { if (layers > 1) --layers; }
-        else if (IsKeyPressed(KEY_END)) { if (layers < TEXT_MAX_LAYERS) ++layers; }
-
-        // Handle text changes
-        if (IsKeyPressed(KEY_LEFT)) fontSize -= 0.5f;
-        else if (IsKeyPressed(KEY_RIGHT)) fontSize += 0.5f;
-        else if (IsKeyPressed(KEY_UP)) fontSpacing -= 0.1f;
-        else if (IsKeyPressed(KEY_DOWN)) fontSpacing += 0.1f;
-        else if (IsKeyPressed(KEY_PAGE_UP)) lineSpacing -= 0.1f;
-        else if (IsKeyPressed(KEY_PAGE_DOWN)) lineSpacing += 0.1f;
-        else if (IsKeyDown(KEY_INSERT)) layerDistance -= 0.001f;
-        else if (IsKeyDown(KEY_DELETE)) layerDistance += 0.001f;
-        else if (IsKeyPressed(KEY_TAB))
-        {
-            multicolor = !multicolor;   // Enable /disable multicolor mode
-
-            if (multicolor)
-            {
-                // Fill color array with random colors
-                for (int i = 0; i < TEXT_MAX_LAYERS; ++i)
-                {
-                    multi[i] = GenerateRandomColor(0.5f, 0.8f);
-                    multi[i].a = GetRandomValue(0, 255);
-                }
-            }
-        }
-
-        // Handle text input
-        int ch = GetCharPressed();
-        if (IsKeyPressed(KEY_BACKSPACE))
-        {
-            // Remove last char
-            int len = TextLength(text);
-            if (len > 0) text[len - 1] = '\0';
-        }
-        else if (IsKeyPressed(KEY_ENTER))
-        {
-            // handle newline
-            int len = TextLength(text);
-            if (len < sizeof(text) - 1)
-            {
-                text[len] = '\n';
-                text[len+1] ='\0';
-            }
-        }
-        else
-        {
-            // append only printable chars
-            int len = TextLength(text);
-            if (len < sizeof(text) - 1)
-            {
-                text[len] = ch;
-                text[len+1] ='\0';
-            }
-        }
-
-        // Measure 3D text so we can center it
-        tbox = MeasureTextWave3D(font, text, fontSize, fontSpacing, lineSpacing);
-
-        quads = 0;                      // Reset quad counter
-        time += GetFrameTime();         // Update timer needed by `DrawTextWave3D()`
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            BeginMode3D(camera);
-                DrawCubeV(cubePosition, cubeSize, dark);
-                DrawCubeWires(cubePosition, 2.1f, 2.1f, 2.1f, light);
-
-                DrawGrid(10, 2.0f);
-
-                // Use a shader to handle the depth buffer issue with transparent textures
-                // NOTE: more info at https://bedroomcoders.co.uk/raylib-billboards-advanced-use/
-                BeginShaderMode(alphaDiscard);
-
-                    // Draw the 3D text above the red cube
-                    rlPushMatrix();
-                        rlRotatef(90.0f, 1.0f, 0.0f, 0.0f);
-                        rlRotatef(90.0f, 0.0f, 0.0f, -1.0f);
-
-                        for (int i = 0; i < layers; ++i)
-                        {
-                            Color clr = light;
-                            if (multicolor) clr = multi[i];
-                            DrawTextWave3D(font, text, (Vector3){ -tbox.x/2.0f, layerDistance*i, -4.5f }, fontSize, fontSpacing, lineSpacing, true, &wcfg, time, clr);
-                        }
-
-                        // Draw the text boundry if set
-                        if (SHOW_TEXT_BOUNDRY) DrawCubeWiresV((Vector3){ 0.0f, 0.0f, -4.5f + tbox.z/2 }, tbox, dark);
-                    rlPopMatrix();
-
-                    // Don't draw the letter boundries for the 3D text below
-                    bool slb = SHOW_LETTER_BOUNDRY;
-                    SHOW_LETTER_BOUNDRY = false;
-
-                    // Draw 3D options (use default font)
-                    //-------------------------------------------------------------------------
-                    rlPushMatrix();
-                        rlRotatef(180.0f, 0.0f, 1.0f, 0.0f);
-                        char *opt = (char *)TextFormat("< SIZE: %2.1f >", fontSize);
-                        quads += TextLength(opt);
-                        Vector3 m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        Vector3 pos = { -m.x/2.0f, 0.01f, 2.0f};
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, BLUE);
-                        pos.z += 0.5f + m.z;
-
-                        opt = (char *)TextFormat("< SPACING: %2.1f >", fontSpacing);
-                        quads += TextLength(opt);
-                        m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        pos.x = -m.x/2.0f;
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, BLUE);
-                        pos.z += 0.5f + m.z;
-
-                        opt = (char *)TextFormat("< LINE: %2.1f >", lineSpacing);
-                        quads += TextLength(opt);
-                        m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        pos.x = -m.x/2.0f;
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, BLUE);
-                        pos.z += 1.0f + m.z;
-
-                        opt = (char *)TextFormat("< LBOX: %3s >", slb? "ON" : "OFF");
-                        quads += TextLength(opt);
-                        m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        pos.x = -m.x/2.0f;
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, RED);
-                        pos.z += 0.5f + m.z;
-
-                        opt = (char *)TextFormat("< TBOX: %3s >", SHOW_TEXT_BOUNDRY? "ON" : "OFF");
-                        quads += TextLength(opt);
-                        m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        pos.x = -m.x/2.0f;
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, RED);
-                        pos.z += 0.5f + m.z;
-
-                        opt = (char *)TextFormat("< LAYER DISTANCE: %.3f >", layerDistance);
-                        quads += TextLength(opt);
-                        m = MeasureText3D(GetFontDefault(), opt, 8.0f, 1.0f, 0.0f);
-                        pos.x = -m.x/2.0f;
-                        DrawText3D(GetFontDefault(), opt, pos, 8.0f, 1.0f, 0.0f, false, DARKPURPLE);
-                    rlPopMatrix();
-                    //-------------------------------------------------------------------------
-
-                    // Draw 3D info text (use default font)
-                    //-------------------------------------------------------------------------
-                    opt = "All the text displayed here is in 3D";
-                    quads += 36;
-                    m = MeasureText3D(GetFontDefault(), opt, 10.0f, 0.5f, 0.0f);
-                    pos = (Vector3){-m.x/2.0f, 0.01f, 2.0f};
-                    DrawText3D(GetFontDefault(), opt, pos, 10.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    pos.z += 1.5f + m.z;
-
-                    opt = "press [Left]/[Right] to change the font size";
-                    quads += 44;
-                    m = MeasureText3D(GetFontDefault(), opt, 6.0f, 0.5f, 0.0f);
-                    pos.x = -m.x/2.0f;
-                    DrawText3D(GetFontDefault(), opt, pos, 6.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    pos.z += 0.5f + m.z;
-
-                    opt = "press [Up]/[Down] to change the font spacing";
-                    quads += 44;
-                    m = MeasureText3D(GetFontDefault(), opt, 6.0f, 0.5f, 0.0f);
-                    pos.x = -m.x/2.0f;
-                    DrawText3D(GetFontDefault(), opt, pos, 6.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    pos.z += 0.5f + m.z;
-
-                    opt = "press [PgUp]/[PgDown] to change the line spacing";
-                    quads += 48;
-                    m = MeasureText3D(GetFontDefault(), opt, 6.0f, 0.5f, 0.0f);
-                    pos.x = -m.x/2.0f;
-                    DrawText3D(GetFontDefault(), opt, pos, 6.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    pos.z += 0.5f + m.z;
-
-                    opt = "press [F1] to toggle the letter boundry";
-                    quads += 39;
-                    m = MeasureText3D(GetFontDefault(), opt, 6.0f, 0.5f, 0.0f);
-                    pos.x = -m.x/2.0f;
-                    DrawText3D(GetFontDefault(), opt, pos, 6.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    pos.z += 0.5f + m.z;
-
-                    opt = "press [F2] to toggle the text boundry";
-                    quads += 37;
-                    m = MeasureText3D(GetFontDefault(), opt, 6.0f, 0.5f, 0.0f);
-                    pos.x = -m.x/2.0f;
-                    DrawText3D(GetFontDefault(), opt, pos, 6.0f, 0.5f, 0.0f, false, DARKBLUE);
-                    //-------------------------------------------------------------------------
-
-                    SHOW_LETTER_BOUNDRY = slb;
-                EndShaderMode();
-
-            EndMode3D();
-
-            // Draw 2D info text & stats
-            //-------------------------------------------------------------------------
-            DrawText("Drag & drop a font file to change the font!\nType something, see what happens!\n\n"
-            "Press [F3] to toggle the camera", 10, 35, 10, BLACK);
-
-            quads += TextLength(text)*2*layers;
-            char *tmp = (char *)TextFormat("%2i layer(s) | %s camera | %4i quads (%4i verts)", layers, spin? "ORBITAL" : "FREE", quads, quads*4);
-            int width = MeasureText(tmp, 10);
-            DrawText(tmp, screenWidth - 20 - width, 10, 10, DARKGREEN);
-
-            tmp = "[Home]/[End] to add/remove 3D text layers";
-            width = MeasureText(tmp, 10);
-            DrawText(tmp, screenWidth - 20 - width, 25, 10, DARKGRAY);
-
-            tmp = "[Insert]/[Delete] to increase/decrease distance between layers";
-            width = MeasureText(tmp, 10);
-            DrawText(tmp, screenWidth - 20 - width, 40, 10, DARKGRAY);
-
-            tmp = "click the [CUBE] for a random color";
-            width = MeasureText(tmp, 10);
-            DrawText(tmp, screenWidth - 20 - width, 55, 10, DARKGRAY);
-
-            tmp = "[Tab] to toggle multicolor mode";
-            width = MeasureText(tmp, 10);
-            DrawText(tmp, screenWidth - 20 - width, 70, 10, DARKGRAY);
-            //-------------------------------------------------------------------------
-
-            DrawFPS(10, 10);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadFont(font);
-    CloseWindow();        // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-//--------------------------------------------------------------------------------------
-// Module Functions Definitions
-//--------------------------------------------------------------------------------------
-// Draw codepoint at specified position in 3D space
-static void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint)
-{
-    // Character index position in sprite font
-    // NOTE: In case a codepoint is not available in the font, index returned points to '?'
-    int index = GetGlyphIndex(font, codepoint);
-    float scale = fontSize/(float)font.baseSize;
-
-    // Character destination rectangle on screen
-    // NOTE: We consider charsPadding on drawing
-    position.x += (float)(font.glyphs[index].offsetX - font.glyphPadding)/(float)font.baseSize*scale;
-    position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding)/(float)font.baseSize*scale;
-
-    // Character source rectangle from font texture atlas
-    // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
-    Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding,
-                         font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding };
-
-    float width = (float)(font.recs[index].width + 2.0f*font.glyphPadding)/(float)font.baseSize*scale;
-    float height = (float)(font.recs[index].height + 2.0f*font.glyphPadding)/(float)font.baseSize*scale;
-
-    if (font.texture.id > 0)
-    {
-        const float x = 0.0f;
-        const float y = 0.0f;
-        const float z = 0.0f;
-
-        // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f)
-        const float tx = srcRec.x/font.texture.width;
-        const float ty = srcRec.y/font.texture.height;
-        const float tw = (srcRec.x+srcRec.width)/font.texture.width;
-        const float th = (srcRec.y+srcRec.height)/font.texture.height;
-
-        if (SHOW_LETTER_BOUNDRY) DrawCubeWiresV((Vector3){ position.x + width/2, position.y, position.z + height/2}, (Vector3){ width, LETTER_BOUNDRY_SIZE, height }, LETTER_BOUNDRY_COLOR);
-
-        rlCheckRenderBatchLimit(4 + 4*backface);
-        rlSetTexture(font.texture.id);
-
-        rlPushMatrix();
-            rlTranslatef(position.x, position.y, position.z);
-
-            rlBegin(RL_QUADS);
-                rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-
-                // Front Face
-                rlNormal3f(0.0f, 1.0f, 0.0f);                                   // Normal Pointing Up
-                rlTexCoord2f(tx, ty); rlVertex3f(x,         y, z);              // Top Left Of The Texture and Quad
-                rlTexCoord2f(tx, th); rlVertex3f(x,         y, z + height);     // Bottom Left Of The Texture and Quad
-                rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height);     // Bottom Right Of The Texture and Quad
-                rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z);              // Top Right Of The Texture and Quad
-
-                if (backface)
-                {
-                    // Back Face
-                    rlNormal3f(0.0f, -1.0f, 0.0f);                              // Normal Pointing Down
-                    rlTexCoord2f(tx, ty); rlVertex3f(x,         y, z);          // Top Right Of The Texture and Quad
-                    rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z);          // Top Left Of The Texture and Quad
-                    rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad
-                    rlTexCoord2f(tx, th); rlVertex3f(x,         y, z + height); // Bottom Right Of The Texture and Quad
-                }
-            rlEnd();
-        rlPopMatrix();
-
-        rlSetTexture(0);
-    }
-}
-
-// Draw a 2D text in 3D space
-static void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint)
-{
-    int length = TextLength(text);          // Total length in bytes of the text, scanned by codepoints in loop
-
-    float textOffsetY = 0.0f;               // Offset between lines (on line break '\n')
-    float textOffsetX = 0.0f;               // Offset X to next character to draw
-
-    float scale = fontSize/(float)font.baseSize;
-
-    for (int i = 0; i < length;)
-    {
-        // Get next codepoint from byte string and glyph index in font
-        int codepointByteCount = 0;
-        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
-        int index = GetGlyphIndex(font, codepoint);
-
-        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
-        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
-        if (codepoint == 0x3f) codepointByteCount = 1;
-
-        if (codepoint == '\n')
-        {
-            // NOTE: Fixed line spacing of 1.5 line-height
-            // TODO: Support custom line spacing defined by user
-            textOffsetY += scale + lineSpacing/(float)font.baseSize*scale;
-            textOffsetX = 0.0f;
-        }
-        else
-        {
-            if ((codepoint != ' ') && (codepoint != '\t'))
-            {
-                DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint);
-            }
-
-            if (font.glyphs[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
-            else textOffsetX += (float)(font.glyphs[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
-        }
-
-        i += codepointByteCount;   // Move text bytes counter to next codepoint
-    }
-}
-
-// Measure a text in 3D. For some reason `MeasureTextEx()` just doesn't seem to work so i had to use this instead.
-static Vector3 MeasureText3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing)
-{
-    int len = TextLength(text);
-    int tempLen = 0;                // Used to count longer text line num chars
-    int lenCounter = 0;
-
-    float tempTextWidth = 0.0f;     // Used to count longer text line width
-
-    float scale = fontSize/(float)font.baseSize;
-    float textHeight = scale;
-    float textWidth = 0.0f;
-
-    int letter = 0;                 // Current character
-    int index = 0;                  // Index position in sprite font
-
-    for (int i = 0; i < len; i++)
-    {
-        lenCounter++;
-
-        int next = 0;
-        letter = GetCodepoint(&text[i], &next);
-        index = GetGlyphIndex(font, letter);
-
-        // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
-        // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
-        if (letter == 0x3f) next = 1;
-        i += next - 1;
-
-        if (letter != '\n')
-        {
-            if (font.glyphs[index].advanceX != 0) textWidth += (font.glyphs[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
-            else textWidth += (font.recs[index].width + font.glyphs[index].offsetX)/(float)font.baseSize*scale;
-        }
-        else
-        {
-            if (tempTextWidth < textWidth) tempTextWidth = textWidth;
-            lenCounter = 0;
-            textWidth = 0.0f;
-            textHeight += scale + lineSpacing/(float)font.baseSize*scale;
-        }
-
-        if (tempLen < lenCounter) tempLen = lenCounter;
-    }
-
-    if (tempTextWidth < textWidth) tempTextWidth = textWidth;
-
-    Vector3 vec = { 0 };
-    vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
-    vec.y = 0.25f;
-    vec.z = textHeight;
-
-    return vec;
-}
-
-// Draw a 2D text in 3D space and wave the parts that start with `~~` and end with `~~`.
-// This is a modified version of the original code by @Nighten found here https://github.com/NightenDushi/Raylib_DrawTextStyle
-static void DrawTextWave3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, WaveTextConfig* config, float time, Color tint)
-{
-    int length = TextLength(text);          // Total length in bytes of the text, scanned by codepoints in loop
-
-    float textOffsetY = 0.0f;               // Offset between lines (on line break '\n')
-    float textOffsetX = 0.0f;               // Offset X to next character to draw
-
-    float scale = fontSize/(float)font.baseSize;
-
-    bool wave = false;
-
-    for (int i = 0, k = 0; i < length; ++k)
-    {
-        // Get next codepoint from byte string and glyph index in font
-        int codepointByteCount = 0;
-        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
-        int index = GetGlyphIndex(font, codepoint);
-
-        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
-        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
-        if (codepoint == 0x3f) codepointByteCount = 1;
-
-        if (codepoint == '\n')
-        {
-            // NOTE: Fixed line spacing of 1.5 line-height
-            // TODO: Support custom line spacing defined by user
-            textOffsetY += scale + lineSpacing/(float)font.baseSize*scale;
-            textOffsetX = 0.0f;
-            k = 0;
-        }
-        else if (codepoint == '~')
-        {
-            if (GetCodepoint(&text[i+1], &codepointByteCount) == '~')
-            {
-                codepointByteCount += 1;
-                wave = !wave;
-            }
-        }
-        else
-        {
-            if ((codepoint != ' ') && (codepoint != '\t'))
-            {
-                Vector3 pos = position;
-                if (wave) // Apply the wave effect
-                {
-                    pos.x += sinf(time*config->waveSpeed.x-k*config->waveOffset.x)*config->waveRange.x;
-                    pos.y += sinf(time*config->waveSpeed.y-k*config->waveOffset.y)*config->waveRange.y;
-                    pos.z += sinf(time*config->waveSpeed.z-k*config->waveOffset.z)*config->waveRange.z;
-                }
-
-                DrawTextCodepoint3D(font, codepoint, (Vector3){ pos.x + textOffsetX, pos.y, pos.z + textOffsetY }, fontSize, backface, tint);
-            }
-
-            if (font.glyphs[index].advanceX == 0) textOffsetX += (float)(font.recs[index].width + fontSpacing)/(float)font.baseSize*scale;
-            else textOffsetX += (float)(font.glyphs[index].advanceX + fontSpacing)/(float)font.baseSize*scale;
-        }
-
-        i += codepointByteCount;   // Move text bytes counter to next codepoint
-    }
-}
-
-// Measure a text in 3D ignoring the `~~` chars.
-static Vector3 MeasureTextWave3D(Font font, const char* text, float fontSize, float fontSpacing, float lineSpacing)
-{
-    int len = TextLength(text);
-    int tempLen = 0;                // Used to count longer text line num chars
-    int lenCounter = 0;
-
-    float tempTextWidth = 0.0f;     // Used to count longer text line width
-
-    float scale = fontSize/(float)font.baseSize;
-    float textHeight = scale;
-    float textWidth = 0.0f;
-
-    int letter = 0;                 // Current character
-    int index = 0;                  // Index position in sprite font
-
-    for (int i = 0; i < len; i++)
-    {
-        lenCounter++;
-
-        int next = 0;
-        letter = GetCodepoint(&text[i], &next);
-        index = GetGlyphIndex(font, letter);
-
-        // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
-        // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
-        if (letter == 0x3f) next = 1;
-        i += next - 1;
-
-        if (letter != '\n')
-        {
-            if (letter == '~' && GetCodepoint(&text[i+1], &next) == '~')
-            {
-                i++;
-            }
-            else
-            {
-                if (font.glyphs[index].advanceX != 0) textWidth += (font.glyphs[index].advanceX+fontSpacing)/(float)font.baseSize*scale;
-                else textWidth += (font.recs[index].width + font.glyphs[index].offsetX)/(float)font.baseSize*scale;
-            }
-        }
-        else
-        {
-            if (tempTextWidth < textWidth) tempTextWidth = textWidth;
-            lenCounter = 0;
-            textWidth = 0.0f;
-            textHeight += scale + lineSpacing/(float)font.baseSize*scale;
-        }
-
-        if (tempLen < lenCounter) tempLen = lenCounter;
-    }
-
-    if (tempTextWidth < textWidth) tempTextWidth = textWidth;
-
-    Vector3 vec = { 0 };
-    vec.x = tempTextWidth + (float)((tempLen - 1)*fontSpacing/(float)font.baseSize*scale); // Adds chars spacing to measure
-    vec.y = 0.25f;
-    vec.z = textHeight;
-
-    return vec;
-}
-
-// Generates a nice color with a random hue
-static Color GenerateRandomColor(float s, float v)
-{
-    const float Phi = 0.618033988749895f; // Golden ratio conjugate
-    float h = (float)GetRandomValue(0, 360);
-    h = fmodf((h + h*Phi), 360.0f);
-    return ColorFromHSV(h, s, v);
-}
diff --git a/raylib/examples/text/text_font_filters.c b/raylib/examples/text/text_font_filters.c
--- a/raylib/examples/text/text_font_filters.c
+++ b/raylib/examples/text/text_font_filters.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Font filters
+*   raylib [text] example - font filters
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: After font loading, font texture atlas filter could be configured for a softer
 *   display of the font when scaling it to different sizes, that way, it's not required
 *   to generate multiple fonts at multiple sizes (as long as the scaling is not very different)
@@ -11,7 +13,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -92,7 +94,7 @@
                 UnloadFont(font);
                 font = LoadFontEx(droppedFiles.paths[0], (int)fontSize, 0, 0);
             }
-            
+
             UnloadDroppedFiles(droppedFiles);    // Unload filepaths from memory
         }
         //----------------------------------------------------------------------------------
diff --git a/raylib/examples/text/text_font_loading.c b/raylib/examples/text/text_font_loading.c
--- a/raylib/examples/text/text_font_loading.c
+++ b/raylib/examples/text/text_font_loading.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Font loading
+*   raylib [text] example - font loading
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: raylib can load fonts from multiple input file formats:
 *
 *     - TTF/OTF > Sprite font atlas is generated on loading, user can configure
 *                 some of the generation parameters (size, characters to include)
 *     - BMFonts > Angel code font fileformat, sprite font image must be provided
-*                 together with the .fnt file, font generation cna not be configured
+*                 together with the .fnt file, font generation can not be configured
 *     - XNA Spritefont > Sprite font image, following XNA Spritefont conventions,
 *                 Characters in image must follow some spacing and order rules
 *
@@ -16,7 +18,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/text/text_font_sdf.c b/raylib/examples/text/text_font_sdf.c
--- a/raylib/examples/text/text_font_sdf.c
+++ b/raylib/examples/text/text_font_sdf.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Font SDF loading
+*   raylib [text] example - font sdf
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -31,7 +33,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - SDF fonts");
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - font sdf");
 
     // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required)
 
@@ -48,7 +50,7 @@
 
     // Loading font data from memory data
     // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 95 (autogenerate chars array)
-    fontDefault.glyphs = LoadFontData(fileData, fileSize, 16, 0, 95, FONT_DEFAULT);
+    fontDefault.glyphs = LoadFontData(fileData, fileSize, 16, 0, 95, FONT_DEFAULT, &fontDefault.glyphCount);
     // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 4 px, pack method: 0 (default)
     Image atlas = GenImageFontAtlas(fontDefault.glyphs, &fontDefault.recs, 95, 16, 4, 0);
     fontDefault.texture = LoadTextureFromImage(atlas);
@@ -59,7 +61,8 @@
     fontSDF.baseSize = 16;
     fontSDF.glyphCount = 95;
     // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 0 (defaults to 95)
-    fontSDF.glyphs = LoadFontData(fileData, fileSize, 16, 0, 0, FONT_SDF);
+    fontSDF.glyphs = LoadFontData(fileData, fileSize, 16, 0, 0, FONT_SDF, &
+    fontSDF.glyphCount);
     // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 0 px, pack method: 1 (Skyline algorythm)
     atlas = GenImageFontAtlas(fontSDF.glyphs, &fontSDF.recs, 95, 16, 0, 1);
     fontSDF.texture = LoadTextureFromImage(atlas);
diff --git a/raylib/examples/text/text_font_spritefont.c b/raylib/examples/text/text_font_spritefont.c
--- a/raylib/examples/text/text_font_spritefont.c
+++ b/raylib/examples/text/text_font_spritefont.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Sprite font loading
+*   raylib [text] example - font spritefont
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: Sprite fonts should be generated following this conventions:
 *
 *     - Characters must be ordered starting with character 32 (Space)
@@ -10,14 +12,14 @@
 *     - Rectangles must be defined by a MAGENTA color background
 *
 *   Following those constraints, a font can be provided just by an image,
-*   this is quite handy to avoid additional font descriptor files (like BMFonts use).
+*   this is quite handy to avoid additional font descriptor files (like BMFonts use)
 *
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -33,7 +35,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite font loading");
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - font spritefont");
 
     const char msg1[50] = "THIS IS A custom SPRITE FONT...";
     const char msg2[50] = "...and this is ANOTHER CUSTOM font...";
diff --git a/raylib/examples/text/text_format_text.c b/raylib/examples/text/text_format_text.c
--- a/raylib/examples/text/text_format_text.c
+++ b/raylib/examples/text/text_format_text.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Text formatting
+*   raylib [text] example - format text
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.1, last time updated with raylib 3.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - text formatting");
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - format text");
 
     int score = 100020;
     int hiscore = 200450;
diff --git a/raylib/examples/text/text_inline_styling.c b/raylib/examples/text/text_inline_styling.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_inline_styling.c
@@ -0,0 +1,287 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - inline styling
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by Wagner Barongello (@SultansOfCode) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Wagner Barongello (@SultansOfCode) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>     // Required for: strtoul(), NULL
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color);
+static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - inline styling");
+
+    Vector2 textSize = { 0 };   // Measure text box for provided font and text
+    Color colRandom = RED;      // Random color used on text
+    int frameCounter = 0;       // Used to generate a new random color every certain frames
+
+    SetTargetFPS(60);           // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        frameCounter++;
+
+        if ((frameCounter%20) == 0)
+        {
+            colRandom.r = (unsigned char)GetRandomValue(0, 255);
+            colRandom.g = (unsigned char)GetRandomValue(0, 255);
+            colRandom.b = (unsigned char)GetRandomValue(0, 255);
+            colRandom.a = 255;
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Text inline styling strategy used: [ ] delimiters for format
+            // - Define foreground color:      [cRRGGBBAA]
+            // - Define background color:      [bRRGGBBAA]
+            // - Reset formating:              [r]
+            // Colors defined with [cRRGGBBAA] or [bRRGGBBAA] are multiplied by the base color alpha
+            // This allows global transparency control while keeping per-section styling (ex. text fade effects)
+            // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
+
+            DrawTextStyled(GetFontDefault(), "This changes the [cFF0000FF]foreground color[r] of provided text!!!",
+                (Vector2){ 100, 80 }, 20.0f, 2.0f, BLACK);
+
+            DrawTextStyled(GetFontDefault(), "This changes the [bFF00FFFF]background color[r] of provided text!!!",
+                (Vector2){ 100, 120 }, 20.0f, 2.0f, BLACK);
+
+            DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff][bff0000ff]foreground and background colors[r]!!!",
+                (Vector2){ 100, 160 }, 20.0f, 2.0f, BLACK);
+
+            DrawTextStyled(GetFontDefault(), "This changes the [c00ff00ff]alpha[r] relative [cffffffff][b000000ff]from source[r] [cff000088]color[r]!!!",
+                (Vector2){ 100, 200 }, 20.0f, 2.0f, (Color){ 0, 0, 0, 100 });
+
+            // Get pointer to formated text
+            const char *text = TextFormat("Let's be [c%02x%02x%02xFF]CREATIVE[r] !!!", colRandom.r, colRandom.g, colRandom.b);
+            DrawTextStyled(GetFontDefault(), text, (Vector2){ 100, 240 }, 40.0f, 2.0f, BLACK);
+
+            textSize = MeasureTextStyled(GetFontDefault(), text, 40.0f, 2.0f);
+            DrawRectangleLines(100, 240, (int)textSize.x, (int)textSize.y, GREEN);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Draw text using inline styling
+// PARAM: color is the default text color, background color is BLANK by default
+// NOTE: Using input color as the base alpha multiplied to inline styles
+static void DrawTextStyled(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color color)
+{
+    // Text inline styling strategy used: [ ] delimiters for format
+    // - Define foreground color:      [cRRGGBBAA]
+    // - Define background color:      [bRRGGBBAA]
+    // - Reset formating:              [r]
+    // Example: [bAA00AAFF][cFF0000FF]red text on gray background[r] normal text
+
+    if (font.texture.id == 0) font = GetFontDefault();
+
+    int textLen = TextLength(text);
+
+    Color colFront = color;
+    Color colBack = BLANK;
+    int backRecPadding = 4; // Background rectangle padding
+
+    float textOffsetY = 0.0f;
+    float textOffsetX = 0.0f;
+    float textLineSpacing = 0.0f;
+    float scaleFactor = fontSize/font.baseSize;
+
+    for (int i = 0; i < textLen;)
+    {
+        int codepointByteCount = 0;
+        int codepoint = GetCodepointNext(&text[i], &codepointByteCount);
+
+        if (codepoint == '\n')
+        {
+            textOffsetY += (fontSize + textLineSpacing);
+            textOffsetX = 0.0f;
+        }
+        else
+        {
+            if (codepoint == '[') // Process pipe styling
+            {
+                if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling
+                {
+                    colFront = color;
+                    colBack = BLANK;
+
+                    i += 3;     // Skip "[r]"
+                    continue;   // Do not draw characters
+                }
+                else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b')))
+                {
+                    i += 2;     // Skip "[c" or "[b" to start parsing color
+
+                    // Parse following color
+                    char colHexText[9] = { 0 };
+                    const char *textPtr = &text[i]; // Color should start here, let's see...
+
+                    int colHexCount = 0;
+                    while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']'))
+                    {
+                        if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) ||
+                            ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) ||
+                            ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f')))
+                        {
+                            colHexText[colHexCount] = textPtr[colHexCount];
+                            colHexCount++;
+                        }
+                        else break; // Only affects while loop
+                    }
+
+                    // Convert hex color text into actual Color
+                    unsigned int colHexValue = strtoul(colHexText, NULL, 16);
+                    if (text[i - 1] == 'c')
+                    {
+						colFront = GetColor(colHexValue);
+						colFront.a = (unsigned char)(colFront.a * (float)color.a/255.0f);
+					}
+                    else if (text[i - 1] == 'b')
+					{
+						colBack = GetColor(colHexValue);
+						colBack.a *= (unsigned char)(colFront.a * (float)color.a / 255.0f);
+					}
+
+                    i += (colHexCount + 1); // Skip color value retrieved and ']'
+                    continue;   // Do not draw characters
+                }
+            }
+
+            int index = GetGlyphIndex(font, codepoint);
+            float increaseX = 0.0f;
+
+            if (font.glyphs[index].advanceX == 0) increaseX = ((float)font.recs[index].width*scaleFactor + spacing);
+            else increaseX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing);
+
+            // Draw background rectangle color (if required)
+            if (colBack.a > 0) DrawRectangleRec((Rectangle) { position.x + textOffsetX, position.y + textOffsetY - backRecPadding, increaseX, fontSize + 2*backRecPadding }, colBack);
+
+            if ((codepoint != ' ') && (codepoint != '\t'))
+            {
+                DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, colFront);
+            }
+
+            textOffsetX += increaseX;
+        }
+
+        i += codepointByteCount;
+    }
+}
+
+// Measure inline styled text
+// NOTE: Measuring styled text requires skipping styling data
+// WARNING: Not considering line breaks
+static Vector2 MeasureTextStyled(Font font, const char *text, float fontSize, float spacing)
+{
+    Vector2 textSize = { 0 };
+
+    if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check
+
+    int textLen = TextLength(text); // Get size in bytes of text
+    //float textLineSpacing = fontSize*1.5f; // Not used...
+
+    float textWidth = 0.0f;
+    float textHeight = fontSize;
+    float scaleFactor = fontSize/(float)font.baseSize;
+
+    int codepoint = 0;              // Current character
+    int index = 0;                  // Index position in sprite font
+    int validCodepointCounter = 0;
+
+    for (int i = 0; i < textLen;)
+    {
+        int codepointByteCount = 0;
+        codepoint = GetCodepointNext(&text[i], &codepointByteCount);
+
+        if (codepoint == '[') // Ignore pipe inline styling
+        {
+            if (((i + 2) < textLen) && (text[i + 1] == 'r') && (text[i + 2] == ']')) // Reset styling
+            {
+                i += 3;     // Skip "[r]"
+                continue;   // Do not measure characters
+            }
+            else if (((i + 1) < textLen) && ((text[i + 1] == 'c') || (text[i + 1] == 'b')))
+            {
+                i += 2;     // Skip "[c" or "[b" to start parsing color
+
+                const char *textPtr = &text[i]; // Color should start here, let's see...
+
+                int colHexCount = 0;
+                while ((textPtr != NULL) && (textPtr[colHexCount] != '\0') && (textPtr[colHexCount] != ']'))
+                {
+                    if (((textPtr[colHexCount] >= '0') && (textPtr[colHexCount] <= '9')) ||
+                        ((textPtr[colHexCount] >= 'A') && (textPtr[colHexCount] <= 'F')) ||
+                        ((textPtr[colHexCount] >= 'a') && (textPtr[colHexCount] <= 'f')))
+                    {
+                        colHexCount++;
+                    }
+                    else break; // Only affects while loop
+                }
+
+                i += (colHexCount + 1); // Skip color value retrieved and ']'
+                continue;   // Do not measure characters
+            }
+        }
+        else if (codepoint != '\n')
+        {
+            index = GetGlyphIndex(font, codepoint);
+
+            if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX;
+            else textWidth += (font.recs[index].width + font.glyphs[index].offsetX);
+
+            validCodepointCounter++;
+            i += codepointByteCount;
+        }
+    }
+
+    textSize.x = textWidth*scaleFactor + (validCodepointCounter - 1)*spacing;
+    textSize.y = textHeight;
+
+    return textSize;
+}
diff --git a/raylib/examples/text/text_input_box.c b/raylib/examples/text/text_input_box.c
--- a/raylib/examples/text/text_input_box.c
+++ b/raylib/examples/text/text_input_box.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Input Box
+*   raylib [text] example - input box
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.7, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -61,7 +63,7 @@
                 if ((key >= 32) && (key <= 125) && (letterCount < MAX_INPUT_CHARS))
                 {
                     name[letterCount] = (char)key;
-                    name[letterCount+1] = '\0'; // Add null terminator at the end of the string.
+                    name[letterCount+1] = '\0'; // Add null terminator at the end of the string
                     letterCount++;
                 }
 
diff --git a/raylib/examples/text/text_raylib_fonts.c b/raylib/examples/text/text_raylib_fonts.c
deleted file mode 100644
--- a/raylib/examples/text/text_raylib_fonts.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [text] example - raylib fonts loading
-*
-*   NOTE: raylib is distributed with some free to use fonts (even for commercial pourposes!)
-*         To view details and credits for those fonts, check raylib license file
-*
-*   Example originally created with raylib 1.7, last time updated with raylib 3.7
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#define MAX_FONTS   8
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - raylib fonts");
-
-    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
-    Font fonts[MAX_FONTS] = { 0 };
-
-    fonts[0] = LoadFont("resources/fonts/alagard.png");
-    fonts[1] = LoadFont("resources/fonts/pixelplay.png");
-    fonts[2] = LoadFont("resources/fonts/mecha.png");
-    fonts[3] = LoadFont("resources/fonts/setback.png");
-    fonts[4] = LoadFont("resources/fonts/romulus.png");
-    fonts[5] = LoadFont("resources/fonts/pixantiqua.png");
-    fonts[6] = LoadFont("resources/fonts/alpha_beta.png");
-    fonts[7] = LoadFont("resources/fonts/jupiter_crash.png");
-
-    const char *messages[MAX_FONTS] = { "ALAGARD FONT designed by Hewett Tsoi",
-                                "PIXELPLAY FONT designed by Aleksander Shevchuk",
-                                "MECHA FONT designed by Captain Falcon",
-                                "SETBACK FONT designed by Brian Kent (AEnigma)",
-                                "ROMULUS FONT designed by Hewett Tsoi",
-                                "PIXANTIQUA FONT designed by Gerhard Grossmann",
-                                "ALPHA_BETA FONT designed by Brian Kent (AEnigma)",
-                                "JUPITER_CRASH FONT designed by Brian Kent (AEnigma)" };
-
-    const int spacings[MAX_FONTS] = { 2, 4, 8, 4, 3, 4, 4, 1 };
-
-    Vector2 positions[MAX_FONTS] = { 0 };
-
-    for (int i = 0; i < MAX_FONTS; i++)
-    {
-        positions[i].x = screenWidth/2.0f - MeasureTextEx(fonts[i], messages[i], fonts[i].baseSize*2.0f, (float)spacings[i]).x/2.0f;
-        positions[i].y = 60.0f + fonts[i].baseSize + 45.0f*i;
-    }
-
-    // Small Y position corrections
-    positions[3].y += 8;
-    positions[4].y += 2;
-    positions[7].y -= 8;
-
-    Color colors[MAX_FONTS] = { MAROON, ORANGE, DARKGREEN, DARKBLUE, DARKPURPLE, LIME, GOLD, RED };
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // TODO: Update your variables here
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawText("free fonts included with raylib", 250, 20, 20, DARKGRAY);
-            DrawLine(220, 50, 590, 50, DARKGRAY);
-
-            for (int i = 0; i < MAX_FONTS; i++)
-            {
-                DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].baseSize*2.0f, (float)spacings[i], colors[i]);
-            }
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-
-    // Fonts unloading
-    for (int i = 0; i < MAX_FONTS; i++) UnloadFont(fonts[i]);
-
-    CloseWindow();                 // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/text/text_rectangle_bounds.c b/raylib/examples/text/text_rectangle_bounds.c
--- a/raylib/examples/text/text_rectangle_bounds.c
+++ b/raylib/examples/text/text_rectangle_bounds.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Rectangle bounds
+*   raylib [text] example - rectangle bounds
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 4.0
 *
 *   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,14 +11,19 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 
-static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint);   // Draw text using font inside rectangle limits
-static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint);    // Draw text using font inside rectangle limits with support for text selection
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// Draw text using font inside rectangle limits
+static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint);
+// Draw text using font inside rectangle limits with support for text selection
+static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint);
 
 //------------------------------------------------------------------------------------
 // Program main entry point
@@ -28,7 +35,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - draw text inside a rectangle");
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - rectangle bounds");
 
     const char text[] = "Text cannot escape\tthis container\t...word wrap also works when active so here's \
 a long text for testing.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
@@ -128,7 +135,7 @@
 }
 
 //--------------------------------------------------------------------------------------
-// Module functions definition
+// Module Functions Definition
 //--------------------------------------------------------------------------------------
 
 // Draw text using font inside rectangle limits
@@ -177,9 +184,9 @@
 
         // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
         // We store this info in startLine and endLine, then we change states, draw the text between those two variables
-        // and change states again and again recursively until the end of the text (or until we get outside of the container).
+        // and change states again and again recursively until the end of the text (or until we get outside of the container)
         // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
-        // and begin drawing on the next line before we can get outside the container.
+        // and begin drawing on the next line before we can get outside the container
         if (state == MEASURE_STATE)
         {
             // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
diff --git a/raylib/examples/text/text_sprite_fonts.c b/raylib/examples/text/text_sprite_fonts.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_sprite_fonts.c
@@ -0,0 +1,112 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - sprite fonts
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   NOTE: raylib is distributed with some free to use fonts (even for commercial pourposes!)
+*         To view details and credits for those fonts, check raylib license file
+*
+*   Example originally created with raylib 1.7, last time updated with raylib 3.7
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define MAX_FONTS   8
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - sprite fonts");
+
+    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
+    Font fonts[MAX_FONTS] = { 0 };
+
+    fonts[0] = LoadFont("resources/sprite_fonts/alagard.png");
+    fonts[1] = LoadFont("resources/sprite_fonts/pixelplay.png");
+    fonts[2] = LoadFont("resources/sprite_fonts/mecha.png");
+    fonts[3] = LoadFont("resources/sprite_fonts/setback.png");
+    fonts[4] = LoadFont("resources/sprite_fonts/romulus.png");
+    fonts[5] = LoadFont("resources/sprite_fonts/pixantiqua.png");
+    fonts[6] = LoadFont("resources/sprite_fonts/alpha_beta.png");
+    fonts[7] = LoadFont("resources/sprite_fonts/jupiter_crash.png");
+
+    const char *messages[MAX_FONTS] = { "ALAGARD FONT designed by Hewett Tsoi",
+                                "PIXELPLAY FONT designed by Aleksander Shevchuk",
+                                "MECHA FONT designed by Captain Falcon",
+                                "SETBACK FONT designed by Brian Kent (AEnigma)",
+                                "ROMULUS FONT designed by Hewett Tsoi",
+                                "PIXANTIQUA FONT designed by Gerhard Grossmann",
+                                "ALPHA_BETA FONT designed by Brian Kent (AEnigma)",
+                                "JUPITER_CRASH FONT designed by Brian Kent (AEnigma)" };
+
+    const int spacings[MAX_FONTS] = { 2, 4, 8, 4, 3, 4, 4, 1 };
+
+    Vector2 positions[MAX_FONTS] = { 0 };
+
+    for (int i = 0; i < MAX_FONTS; i++)
+    {
+        positions[i].x = screenWidth/2.0f - MeasureTextEx(fonts[i], messages[i], fonts[i].baseSize*2.0f, (float)spacings[i]).x/2.0f;
+        positions[i].y = 60.0f + fonts[i].baseSize + 45.0f*i;
+    }
+
+    // Small Y position corrections
+    positions[3].y += 8;
+    positions[4].y += 2;
+    positions[7].y -= 8;
+
+    Color colors[MAX_FONTS] = { MAROON, ORANGE, DARKGREEN, DARKBLUE, DARKPURPLE, LIME, GOLD, RED };
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // TODO: Update your variables here
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("free sprite fonts included with raylib", 220, 20, 20, DARKGRAY);
+            DrawLine(220, 50, 600, 50, DARKGRAY);
+
+            for (int i = 0; i < MAX_FONTS; i++)
+            {
+                DrawTextEx(fonts[i], messages[i], positions[i], fonts[i].baseSize*2.0f, (float)spacings[i], colors[i]);
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+
+    // Fonts unloading
+    for (int i = 0; i < MAX_FONTS; i++) UnloadFont(fonts[i]);
+
+    CloseWindow();                 // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/text/text_strings_management.c b/raylib/examples/text/text_strings_management.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_strings_management.c
@@ -0,0 +1,400 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - strings management
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 David Buzatto (@davidbuzatto)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>
+
+#define MAX_TEXT_LENGTH      100
+#define MAX_TEXT_PARTICLES   100
+#define FONT_SIZE             30
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct TextParticle {
+    char text[MAX_TEXT_LENGTH];
+    Rectangle rect;    // Boundary
+    Vector2 vel;       // Velocity
+    Vector2 ppos;      // Previous position
+    float padding;
+    float borderWidth;
+    float friction;   
+    float elasticity;
+    Color color;
+    bool grabbed;
+} TextParticle;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount);
+TextParticle CreateTextParticle(const char *text, float x, float y, Color color);
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount);
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount);
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount);
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount);
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - strings management");
+
+    TextParticle textParticles[MAX_TEXT_PARTICLES] = { 0 };
+    int particleCount = 0;
+    TextParticle *grabbedTextParticle = NULL;
+    Vector2 pressOffset = {0};
+
+    PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        float delta = GetFrameTime();
+        Vector2 mousePos = GetMousePosition();
+
+        // Checks if a text particle was grabbed
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+        {
+            for (int i = particleCount - 1; i >= 0; i--)
+            {
+                TextParticle *tp = &textParticles[i];
+                pressOffset.x = mousePos.x - tp->rect.x;
+                pressOffset.y = mousePos.y - tp->rect.y;
+                if (CheckCollisionPointRec(mousePos, tp->rect))
+                {
+                    tp->grabbed = true;
+                    grabbedTextParticle = tp;
+                    break;
+                }
+            }
+        }
+
+        // Releases any text particle the was grabbed
+        if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT))
+        {
+            if (grabbedTextParticle != NULL)
+            {
+                grabbedTextParticle->grabbed = false;
+                grabbedTextParticle = NULL;
+            }
+        }
+
+        // Slice os shatter a text particle
+        if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))
+        {
+            for (int i = particleCount - 1; i >= 0; i--)
+            {
+                TextParticle *tp = &textParticles[i];
+                if (CheckCollisionPointRec(mousePos, tp->rect))
+                {
+                    if (IsKeyDown(KEY_LEFT_SHIFT))
+                    {
+                        ShatterTextParticle(tp, i, textParticles, &particleCount);
+                    } 
+                    else
+                    {
+                        SliceTextParticle(tp, i, TextLength(tp->text)/2, textParticles, &particleCount);
+                    }
+                    break;
+                }
+            }
+        }
+
+        // Shake text particles
+        if (IsMouseButtonPressed(MOUSE_BUTTON_MIDDLE))
+        {
+            for (int i = 0; i < particleCount; i++)
+            {
+                if (!textParticles[i].grabbed) textParticles[i].vel = (Vector2){ (float)GetRandomValue(-2000, 2000), (float)GetRandomValue(-2000, 2000) };
+            }
+        }
+
+        // Reset using TextTo* functions
+        if (IsKeyPressed(KEY_ONE)) PrepareFirstTextParticle("raylib => fun videogames programming!", textParticles, &particleCount);
+        if (IsKeyPressed(KEY_TWO)) PrepareFirstTextParticle(TextToUpper("raylib => fun videogames programming!"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_THREE)) PrepareFirstTextParticle(TextToLower("raylib => fun videogames programming!"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_FOUR)) PrepareFirstTextParticle(TextToPascal("raylib_fun_videogames_programming"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_FIVE)) PrepareFirstTextParticle(TextToSnake("RaylibFunVideogamesProgramming"), textParticles, &particleCount);
+        if (IsKeyPressed(KEY_SIX)) PrepareFirstTextParticle(TextToCamel("raylib_fun_videogames_programming"), textParticles, &particleCount);
+
+        // Slice by char pressed only when we have one text particle
+        char charPressed = GetCharPressed();
+        if ((charPressed >= 'A') && (charPressed <= 'z') && (particleCount == 1))
+        {
+            SliceTextParticleByChar(&textParticles[0], charPressed, textParticles, &particleCount);
+        }
+
+        // Updates each text particle state
+        for (int i = 0; i < particleCount; i++)
+        {
+            TextParticle *tp = &textParticles[i];
+
+            // The text particle is not grabbed
+            if (!tp->grabbed) 
+            {
+                // text particle repositioning using the velocity
+                tp->rect.x += tp->vel.x * delta;
+                tp->rect.y += tp->vel.y * delta;
+
+                // Does the text particle hit the screen right boundary?
+                if ((tp->rect.x + tp->rect.width) >= screenWidth) 
+                {
+                    tp->rect.x = screenWidth - tp->rect.width; // Text particle repositioning
+                    tp->vel.x = -tp->vel.x*tp->elasticity;  // Elasticity makes the text particle lose 10% of its velocity on hit
+                } 
+                // Does the text particle hit the screen left boundary?
+                else if (tp->rect.x <= 0)
+                { 
+                    tp->rect.x = 0.0f;
+                    tp->vel.x = -tp->vel.x*tp->elasticity;
+                }
+
+                // The same for y axis
+                if ((tp->rect.y + tp->rect.height) >= screenHeight) 
+                {
+                    tp->rect.y = screenHeight - tp->rect.height;
+                    tp->vel.y = -tp->vel.y*tp->elasticity;
+                } 
+                else if (tp->rect.y <= 0) 
+                { 
+                    tp->rect.y = 0.0f;
+                    tp->vel.y = -tp->vel.y*tp->elasticity;
+                }
+
+                // Friction makes the text particle lose 1% of its velocity each frame
+                tp->vel.x = tp->vel.x*tp->friction;
+                tp->vel.y = tp->vel.y*tp->friction;
+            }
+            else
+            {
+                // Text particle repositioning using the mouse position
+                tp->rect.x = mousePos.x - pressOffset.x;
+                tp->rect.y = mousePos.y - pressOffset.y;
+
+                // While the text particle is grabbed, recalculates its velocity
+                tp->vel.x = (tp->rect.x - tp->ppos.x)/delta;
+                tp->vel.y = (tp->rect.y - tp->ppos.y)/delta;
+                tp->ppos.x = tp->rect.x;
+                tp->ppos.y = tp->rect.y;
+
+                // Glue text particles when dragging and pressing left ctrl
+                if (IsKeyDown(KEY_LEFT_CONTROL))
+                {
+                    for (int i = 0; i < particleCount; i++)
+                    {
+                        if (&textParticles[i] != grabbedTextParticle && grabbedTextParticle->grabbed)
+                        {
+                            if (CheckCollisionRecs(grabbedTextParticle->rect, textParticles[i].rect))
+                            {
+                                GlueTextParticles(grabbedTextParticle, &textParticles[i], textParticles, &particleCount);
+                                grabbedTextParticle = &textParticles[particleCount-1];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            for (int i = 0; i < particleCount; i++)
+            {
+                TextParticle *tp = &textParticles[i];
+                DrawRectangleRec((Rectangle) { tp->rect.x - tp->borderWidth, tp->rect.y - tp->borderWidth, tp->rect.width + tp->borderWidth * 2, tp->rect.height + tp->borderWidth * 2 }, BLACK);
+                DrawRectangleRec(tp->rect, tp->color);
+                DrawText(tp->text, (int)(tp->rect.x+tp->padding), (int)(tp->rect.y+tp->padding), FONT_SIZE, BLACK);
+            }
+
+            DrawText("grab a text particle by pressing with the mouse and throw it by releasing", 10, 10, 10, DARKGRAY);
+            DrawText("slice a text particle by pressing it with the mouse right button", 10, 30, 10, DARKGRAY);
+            DrawText("shatter a text particle keeping left shift pressed and pressing it with the mouse right button", 10, 50, 10, DARKGRAY);
+            DrawText("glue text particles by grabbing than and keeping left control pressed", 10, 70, 10, DARKGRAY);
+            DrawText("1 to 6 to reset", 10, 90, 10, DARKGRAY);
+            DrawText("when you have only one text particle, you can slice it by pressing a char", 10, 110, 10, DARKGRAY);
+            DrawText(TextFormat("TEXT PARTICLE COUNT: %d", particleCount), 10, GetScreenHeight() - 30, 20, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+void PrepareFirstTextParticle(const char* text, TextParticle *tps, int *particleCount)
+{
+    tps[0] = CreateTextParticle(
+        text, 
+        GetScreenWidth()/2.0f, 
+        GetScreenHeight()/2.0f, 
+        RAYWHITE
+    );
+    *particleCount = 1;
+}
+
+TextParticle CreateTextParticle(const char *text, float x, float y, Color color)
+{
+    TextParticle tp = {
+        .text = "",
+        .rect = { x, y, 30, 30 },
+        .vel = { (float)GetRandomValue(-200, 200), (float)GetRandomValue(-200, 200) },
+        .ppos = { 0 },
+        .padding = 5.0f,
+        .borderWidth = 5.0f,
+        .friction = 0.99f,
+        .elasticity = 0.9f,
+        .color = color,
+        .grabbed = false
+    };
+
+    TextCopy(tp.text, text);
+    tp.rect.width = MeasureText(tp.text, FONT_SIZE)+tp.padding*2;
+    tp.rect.height = FONT_SIZE+tp.padding*2;
+    return tp;
+}
+
+void SliceTextParticle(TextParticle *tp, int particlePos, int sliceLength, TextParticle *tps, int *particleCount)
+{
+    int length = TextLength(tp->text);
+
+    if((length > 1) && ((*particleCount+length) < MAX_TEXT_PARTICLES))
+    {
+        for (int i = 0; i < length; i += sliceLength)
+        {
+            const char *text = sliceLength == 1 ? TextFormat("%c", tp->text[i]) : TextSubtext(tp->text, i, sliceLength);
+            tps[(*particleCount)++] = CreateTextParticle(
+                text,
+                tp->rect.x + i * tp->rect.width/length,
+                tp->rect.y,
+                (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+            );
+        }
+        RealocateTextParticles(tps, particlePos, particleCount);
+    }
+}
+
+void SliceTextParticleByChar(TextParticle *tp, char charToSlice, TextParticle *tps, int *particleCount)
+{
+    int tokenCount = 0;
+    char **tokens = TextSplit(tp->text, charToSlice, &tokenCount);
+    
+    if (tokenCount > 1)
+    {
+        int textLength = TextLength(tp->text);
+        for (int i = 0; i < textLength; i++)
+        {
+            if (tp->text[i] == charToSlice)
+            {
+                tps[(*particleCount)++] = CreateTextParticle(
+                    TextFormat("%c", charToSlice),
+                    tp->rect.x,
+                    tp->rect.y,
+                    (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+                );
+            }
+        }
+        for (int i = 0; i < tokenCount; i++)
+        {
+            int tokenLength = TextLength(tokens[i]);
+            tps[(*particleCount)++] = CreateTextParticle(
+                TextFormat("%s", tokens[i]),
+                tp->rect.x + i * tp->rect.width/tokenLength,
+                tp->rect.y,
+                (Color) { GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 }
+            );
+        }
+        if (tokenCount)
+        {
+            RealocateTextParticles(tps, 0, particleCount);
+        }
+    }
+}
+
+void ShatterTextParticle(TextParticle *tp, int particlePos, TextParticle *tps, int *particleCount)
+{
+    SliceTextParticle(tp, particlePos, 1, tps, particleCount);
+}
+
+void GlueTextParticles(TextParticle *grabbed, TextParticle *target, TextParticle *tps, int *particleCount)
+{
+    int p1 = -1;
+    int p2 = -1;
+
+    for (int i = 0; i < *particleCount; i++)
+    {
+        if (&tps[i] == grabbed) p1 = i;
+        if (&tps[i] == target) p2 = i;
+    }
+
+    if ((p1 != -1) && (p2 != -1))
+    {
+        TextParticle tp = CreateTextParticle(
+            TextFormat( "%s%s", grabbed->text, target->text),
+            grabbed->rect.x,
+            grabbed->rect.y,
+            RAYWHITE
+        );
+        tp.grabbed = true;
+        tps[(*particleCount)++] = tp;
+        grabbed->grabbed = false;
+        if (p1 < p2)
+        {
+            RealocateTextParticles(tps, p2, particleCount);
+            RealocateTextParticles(tps, p1, particleCount);
+        }
+        else
+        {
+            RealocateTextParticles(tps, p1, particleCount);
+            RealocateTextParticles(tps, p2, particleCount);
+        }
+    }
+}
+
+void RealocateTextParticles(TextParticle *tps, int particlePos, int *particleCount)
+{
+    for (int i = particlePos+1; i < *particleCount; i++)
+    {
+        tps[i-1] = tps[i];
+    }
+    (*particleCount)--;
+}
diff --git a/raylib/examples/text/text_unicode.c b/raylib/examples/text/text_unicode.c
deleted file mode 100644
--- a/raylib/examples/text/text_unicode.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [text] example - Unicode
-*
-*   Example originally created with raylib 2.5, last time updated with raylib 4.0
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2019-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#define SIZEOF(A) (sizeof(A)/sizeof(A[0]))
-#define EMOJI_PER_WIDTH 8
-#define EMOJI_PER_HEIGHT 4
-
-// String containing 180 emoji codepoints separated by a '\0' char
-const char *const emojiCodepoints = "\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00"
-    "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00"
-    "\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00"
-    "\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00"
-    "\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00"
-    "\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00"
-    "\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00"
-    "\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00"
-    "\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00"
-    "\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00"
-    "\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00"
-    "\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00"
-    "\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00"
-    "\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00"
-    "\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00"
-    "\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00"
-    "\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00"
-    "\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00"
-    "\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00"
-    "\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00"
-    "\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00"
-    "\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00"
-    "\xF0\x9F\x92\x9F\x00\xF0\x9F\x92\x8C\x00\xF0\x9F\x92\xA4\x00\xF0\x9F\x92\xA2\x00\xF0\x9F\x92\xA3\x00";
-
-struct {
-    char *text;
-    char *language;
-} const messages[] = { // Array containing all of the emojis messages
-    {"\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C"
-    "\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"},
-    {"\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74"
-    "\x74\x65\x72\x74\x2E", "German"},
-    {"\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F"
-    "\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"},
-    {"\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB"
-    "\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"},
-    {"\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5"
-    "\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF"
-    "\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"},
-    {"\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5"
-    "\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"},
-    {"\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87"
-    "\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"},
-    {"\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F"
-    "\x77\x61\x6E\x65\x2E", "Polish"},
-    {"\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98"
-    "\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"},
-    {"\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20"
-    "\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"},
-    {"\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"},
-    {"\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8"
-    "\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20"
-    "\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1"
-    "\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1"
-    "\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2"
-    "\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC"
-    "\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"},
-    {"\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70"
-    "\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"},
-    {"\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20"
-    "\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78"
-    "\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"},
-    {"\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20"
-    "\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE"
-    "\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"},
-    {"\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD"
-    "\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"},
-    {"\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"},
-    {"\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"},
-
-    {"\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"},
-    {"\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
-    {"\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"},
-    {"\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
-    {"\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"},
-    {"\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"},
-    {"\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"},
-    {"\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"},
-    {"\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"},
-    {"\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"},
-    {"\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"},
-    {"\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"},
-
-    {"\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"},
-    {"\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"},
-    {"\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"},
-    {"\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
-    {"\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
-    {"\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"},
-    {"\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"},
-    {"\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"},
-
-    {"\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20"
-    "\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E"
-    "\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98"
-    "\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"},
-    {"\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"},
-    {"\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"},
-    {"\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4"
-    "\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
-    {"\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"},
-    {"\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"},
-    {"\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
-    {"\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
-    {"\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
-};
-
-//--------------------------------------------------------------------------------------
-// Module functions declaration
-//--------------------------------------------------------------------------------------
-static void RandomizeEmoji(void);    // Fills the emoji array with random emojis
-
-static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint);   // Draw text using font inside rectangle limits
-static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint);    // Draw text using font inside rectangle limits with support for text selection
-
-//--------------------------------------------------------------------------------------
-// Global variables
-//--------------------------------------------------------------------------------------
-// Arrays that holds the random emojis
-struct {
-    int index;      // Index inside `emojiCodepoints`
-    int message;    // Message index
-    Color color;    // Emoji color
-} emoji[EMOJI_PER_WIDTH*EMOJI_PER_HEIGHT] = { 0 };
-
-static int hovered = -1, selected = -1;
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode");
-
-    // Load the font resources
-    // NOTE: fontAsian is for asian languages,
-    // fontEmoji is the emojis and fontDefault is used for everything else
-    Font fontDefault = LoadFont("resources/dejavu.fnt");
-    Font fontAsian = LoadFont("resources/noto_cjk.fnt");
-    Font fontEmoji = LoadFont("resources/symbola.fnt");
-
-    Vector2 hoveredPos = { 0.0f, 0.0f };
-    Vector2 selectedPos = { 0.0f, 0.0f };
-
-    // Set a random set of emojis when starting up
-    RandomizeEmoji();
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // Add a new set of emojis when SPACE is pressed
-        if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji();
-
-        // Set the selected emoji
-        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (hovered != -1) && (hovered != selected))
-        {
-            selected = hovered;
-            selectedPos = hoveredPos;
-        }
-
-        Vector2 mouse = GetMousePosition();
-        Vector2 position = { 28.8f, 10.0f };
-        hovered = -1;
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            // Draw random emojis in the background
-            //------------------------------------------------------------------------------
-            for (int i = 0; i < SIZEOF(emoji); ++i)
-            {
-                const char *txt = &emojiCodepoints[emoji[i].index];
-                Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize };
-
-                if (!CheckCollisionPointRec(mouse, emojiRect))
-                {
-                    DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f));
-                }
-                else
-                {
-                    DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, emoji[i].color );
-                    hovered = i;
-                    hoveredPos = position;
-                }
-
-                if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { position.y += fontEmoji.baseSize + 24.25f; position.x = 28.8f; }
-                else position.x += fontEmoji.baseSize + 28.8f;
-            }
-            //------------------------------------------------------------------------------
-
-            // Draw the message when a emoji is selected
-            //------------------------------------------------------------------------------
-            if (selected != -1)
-            {
-                const int message = emoji[selected].message;
-                const int horizontalPadding = 20, verticalPadding = 30;
-                Font *font = &fontDefault;
-
-                // Set correct font for asian languages
-                if (TextIsEqual(messages[message].language, "Chinese") ||
-                    TextIsEqual(messages[message].language, "Korean") ||
-                    TextIsEqual(messages[message].language, "Japanese")) font = &fontAsian;
-
-                // Calculate size for the message box (approximate the height and width)
-                Vector2 sz = MeasureTextEx(*font, messages[message].text, (float)font->baseSize, 1.0f);
-                if (sz.x > 300) { sz.y *= sz.x/300; sz.x = 300; }
-                else if (sz.x < 160) sz.x = 160;
-
-                Rectangle msgRect = { selectedPos.x - 38.8f, selectedPos.y, 2 * horizontalPadding + sz.x, 2 * verticalPadding + sz.y };
-                msgRect.y -= msgRect.height;
-
-                // Coordinates for the chat bubble triangle
-                Vector2 a = { selectedPos.x, msgRect.y + msgRect.height }, b = {a.x + 8, a.y + 10}, c= { a.x + 10, a.y };
-
-                // Don't go outside the screen
-                if (msgRect.x < 10) msgRect.x += 28;
-                if (msgRect.y < 10)
-                {
-                    msgRect.y = selectedPos.y + 84;
-                    a.y = msgRect.y;
-                    c.y = a.y;
-                    b.y = a.y - 10;
-
-                    // Swap values so we can actually render the triangle :(
-                    Vector2 tmp = a;
-                    a = b;
-                    b = tmp;
-                }
-
-                if (msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10;
-
-                // Draw chat bubble
-                DrawRectangleRec(msgRect, emoji[selected].color);
-                DrawTriangle(a, b, c, emoji[selected].color);
-
-                // Draw the main text message
-                Rectangle textRect = { msgRect.x + horizontalPadding/2, msgRect.y + verticalPadding/2, msgRect.width - horizontalPadding, msgRect.height };
-                DrawTextBoxed(*font, messages[message].text, textRect, (float)font->baseSize, 1.0f, true, WHITE);
-
-                // Draw the info text below the main message
-                int size = (int)strlen(messages[message].text);
-                int length = GetCodepointCount(messages[message].text);
-                const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, length, size);
-                sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f);
-                
-                DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE);
-            }
-            //------------------------------------------------------------------------------
-
-            // Draw the info text
-            DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650)/2, screenHeight - 40, 20, GRAY);
-            DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484)/2, screenHeight - 16, 10, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadFont(fontDefault);    // Unload font resource
-    UnloadFont(fontAsian);      // Unload font resource
-    UnloadFont(fontEmoji);      // Unload font resource
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-// Fills the emoji array with random emoji (only those emojis present in fontEmoji)
-static void RandomizeEmoji(void)
-{
-    hovered = selected = -1;
-    int start = GetRandomValue(45, 360);
-
-    for (int i = 0; i < SIZEOF(emoji); ++i)
-    {
-        // 0-179 emoji codepoints (from emoji char array) each 4bytes + null char
-        emoji[i].index = GetRandomValue(0, 179)*5;
-
-        // Generate a random color for this emoji
-        emoji[i].color = Fade(ColorFromHSV((float)((start*(i + 1))%360), 0.6f, 0.85f), 0.8f);
-
-        // Set a random message for this emoji
-        emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1);
-    }
-}
-
-//--------------------------------------------------------------------------------------
-// Module functions definition
-//--------------------------------------------------------------------------------------
-
-// Draw text using font inside rectangle limits
-static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
-{
-    DrawTextBoxedSelectable(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
-}
-
-// Draw text using font inside rectangle limits with support for text selection
-static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
-{
-    int length = TextLength(text);  // Total length in bytes of the text, scanned by codepoints in loop
-
-    float textOffsetY = 0.0f;       // Offset between lines (on line break '\n')
-    float textOffsetX = 0.0f;       // Offset X to next character to draw
-
-    float scaleFactor = fontSize/(float)font.baseSize;     // Character rectangle scaling factor
-
-    // Word/character wrapping mechanism variables
-    enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
-    int state = wordWrap? MEASURE_STATE : DRAW_STATE;
-
-    int startLine = -1;         // Index where to begin drawing (where a line begins)
-    int endLine = -1;           // Index where to stop drawing (where a line ends)
-    int lastk = -1;             // Holds last value of the character position
-
-    for (int i = 0, k = 0; i < length; i++, k++)
-    {
-        // Get next codepoint from byte string and glyph index in font
-        int codepointByteCount = 0;
-        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
-        int index = GetGlyphIndex(font, codepoint);
-
-        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
-        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
-        if (codepoint == 0x3f) codepointByteCount = 1;
-        i += (codepointByteCount - 1);
-
-        float glyphWidth = 0;
-        if (codepoint != '\n')
-        {
-            glyphWidth = (font.glyphs[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.glyphs[index].advanceX*scaleFactor;
-
-            if (i + 1 < length) glyphWidth = glyphWidth + spacing;
-        }
-
-        // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
-        // We store this info in startLine and endLine, then we change states, draw the text between those two variables
-        // and change states again and again recursively until the end of the text (or until we get outside of the container).
-        // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
-        // and begin drawing on the next line before we can get outside the container.
-        if (state == MEASURE_STATE)
-        {
-            // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
-            // Ref: http://jkorpela.fi/chars/spaces.html
-            if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
-
-            if ((textOffsetX + glyphWidth) > rec.width)
-            {
-                endLine = (endLine < 1)? i : endLine;
-                if (i == endLine) endLine -= codepointByteCount;
-                if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
-
-                state = !state;
-            }
-            else if ((i + 1) == length)
-            {
-                endLine = i;
-                state = !state;
-            }
-            else if (codepoint == '\n') state = !state;
-
-            if (state == DRAW_STATE)
-            {
-                textOffsetX = 0;
-                i = startLine;
-                glyphWidth = 0;
-
-                // Save character position when we switch states
-                int tmp = lastk;
-                lastk = k - 1;
-                k = tmp;
-            }
-        }
-        else
-        {
-            if (codepoint == '\n')
-            {
-                if (!wordWrap)
-                {
-                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
-                    textOffsetX = 0;
-                }
-            }
-            else
-            {
-                if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width))
-                {
-                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
-                    textOffsetX = 0;
-                }
-
-                // When text overflows rectangle height limit, just stop drawing
-                if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break;
-
-                // Draw selection background
-                bool isGlyphSelected = false;
-                if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
-                {
-                    DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
-                    isGlyphSelected = true;
-                }
-
-                // Draw current character glyph
-                if ((codepoint != ' ') && (codepoint != '\t'))
-                {
-                    DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
-                }
-            }
-
-            if (wordWrap && (i == endLine))
-            {
-                textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
-                textOffsetX = 0;
-                startLine = endLine;
-                endLine = -1;
-                glyphWidth = 0;
-                selectStart += lastk - k;
-                k = lastk;
-
-                state = !state;
-            }
-        }
-
-        textOffsetX += glyphWidth;
-    }
-}
diff --git a/raylib/examples/text/text_unicode_emojis.c b/raylib/examples/text/text_unicode_emojis.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_unicode_emojis.c
@@ -0,0 +1,470 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - unicode emojis
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 2.5, last time updated with raylib 4.0
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2019-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define SIZEOF(A) (sizeof(A)/sizeof(A[0]))
+#define EMOJI_PER_WIDTH 8
+#define EMOJI_PER_HEIGHT 4
+
+//--------------------------------------------------------------------------------------
+// Global Variables Definition
+//--------------------------------------------------------------------------------------
+// Arrays that holds the random emojis
+struct {
+    int index;      // Index inside `emojiCodepoints`
+    int message;    // Message index
+    Color color;    // Emoji color
+} emoji[EMOJI_PER_WIDTH*EMOJI_PER_HEIGHT] = { 0 };
+
+static int hovered = -1;
+static int selected = -1;
+
+// String containing 180 emoji codepoints separated by a '\0' char
+const char *const emojiCodepoints = "\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00"
+    "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00"
+    "\xF0\x9F\xA4\x97\x00\xF0\x9F\xA4\xA9\x00\xF0\x9F\xA4\x94\x00\xF0\x9F\xA4\xA8\x00\xF0\x9F\x98\x90\x00\xF0\x9F\x98\x91\x00\xF0\x9F\x98\xB6\x00\xF0\x9F\x99\x84\x00"
+    "\xF0\x9F\x98\x8F\x00\xF0\x9F\x98\xA3\x00\xF0\x9F\x98\xA5\x00\xF0\x9F\x98\xAE\x00\xF0\x9F\xA4\x90\x00\xF0\x9F\x98\xAF\x00\xF0\x9F\x98\xAA\x00\xF0\x9F\x98\xAB\x00"
+    "\xF0\x9F\x98\xB4\x00\xF0\x9F\x98\x8C\x00\xF0\x9F\x98\x9B\x00\xF0\x9F\x98\x9D\x00\xF0\x9F\xA4\xA4\x00\xF0\x9F\x98\x92\x00\xF0\x9F\x98\x95\x00\xF0\x9F\x99\x83\x00"
+    "\xF0\x9F\xA4\x91\x00\xF0\x9F\x98\xB2\x00\xF0\x9F\x99\x81\x00\xF0\x9F\x98\x96\x00\xF0\x9F\x98\x9E\x00\xF0\x9F\x98\x9F\x00\xF0\x9F\x98\xA4\x00\xF0\x9F\x98\xA2\x00"
+    "\xF0\x9F\x98\xAD\x00\xF0\x9F\x98\xA6\x00\xF0\x9F\x98\xA9\x00\xF0\x9F\xA4\xAF\x00\xF0\x9F\x98\xAC\x00\xF0\x9F\x98\xB0\x00\xF0\x9F\x98\xB1\x00\xF0\x9F\x98\xB3\x00"
+    "\xF0\x9F\xA4\xAA\x00\xF0\x9F\x98\xB5\x00\xF0\x9F\x98\xA1\x00\xF0\x9F\x98\xA0\x00\xF0\x9F\xA4\xAC\x00\xF0\x9F\x98\xB7\x00\xF0\x9F\xA4\x92\x00\xF0\x9F\xA4\x95\x00"
+    "\xF0\x9F\xA4\xA2\x00\xF0\x9F\xA4\xAE\x00\xF0\x9F\xA4\xA7\x00\xF0\x9F\x98\x87\x00\xF0\x9F\xA4\xA0\x00\xF0\x9F\xA4\xAB\x00\xF0\x9F\xA4\xAD\x00\xF0\x9F\xA7\x90\x00"
+    "\xF0\x9F\xA4\x93\x00\xF0\x9F\x98\x88\x00\xF0\x9F\x91\xBF\x00\xF0\x9F\x91\xB9\x00\xF0\x9F\x91\xBA\x00\xF0\x9F\x92\x80\x00\xF0\x9F\x91\xBB\x00\xF0\x9F\x91\xBD\x00"
+    "\xF0\x9F\x91\xBE\x00\xF0\x9F\xA4\x96\x00\xF0\x9F\x92\xA9\x00\xF0\x9F\x98\xBA\x00\xF0\x9F\x98\xB8\x00\xF0\x9F\x98\xB9\x00\xF0\x9F\x98\xBB\x00\xF0\x9F\x98\xBD\x00"
+    "\xF0\x9F\x99\x80\x00\xF0\x9F\x98\xBF\x00\xF0\x9F\x8C\xBE\x00\xF0\x9F\x8C\xBF\x00\xF0\x9F\x8D\x80\x00\xF0\x9F\x8D\x83\x00\xF0\x9F\x8D\x87\x00\xF0\x9F\x8D\x93\x00"
+    "\xF0\x9F\xA5\x9D\x00\xF0\x9F\x8D\x85\x00\xF0\x9F\xA5\xA5\x00\xF0\x9F\xA5\x91\x00\xF0\x9F\x8D\x86\x00\xF0\x9F\xA5\x94\x00\xF0\x9F\xA5\x95\x00\xF0\x9F\x8C\xBD\x00"
+    "\xF0\x9F\x8C\xB6\x00\xF0\x9F\xA5\x92\x00\xF0\x9F\xA5\xA6\x00\xF0\x9F\x8D\x84\x00\xF0\x9F\xA5\x9C\x00\xF0\x9F\x8C\xB0\x00\xF0\x9F\x8D\x9E\x00\xF0\x9F\xA5\x90\x00"
+    "\xF0\x9F\xA5\x96\x00\xF0\x9F\xA5\xA8\x00\xF0\x9F\xA5\x9E\x00\xF0\x9F\xA7\x80\x00\xF0\x9F\x8D\x96\x00\xF0\x9F\x8D\x97\x00\xF0\x9F\xA5\xA9\x00\xF0\x9F\xA5\x93\x00"
+    "\xF0\x9F\x8D\x94\x00\xF0\x9F\x8D\x9F\x00\xF0\x9F\x8D\x95\x00\xF0\x9F\x8C\xAD\x00\xF0\x9F\xA5\xAA\x00\xF0\x9F\x8C\xAE\x00\xF0\x9F\x8C\xAF\x00\xF0\x9F\xA5\x99\x00"
+    "\xF0\x9F\xA5\x9A\x00\xF0\x9F\x8D\xB3\x00\xF0\x9F\xA5\x98\x00\xF0\x9F\x8D\xB2\x00\xF0\x9F\xA5\xA3\x00\xF0\x9F\xA5\x97\x00\xF0\x9F\x8D\xBF\x00\xF0\x9F\xA5\xAB\x00"
+    "\xF0\x9F\x8D\xB1\x00\xF0\x9F\x8D\x98\x00\xF0\x9F\x8D\x9D\x00\xF0\x9F\x8D\xA0\x00\xF0\x9F\x8D\xA2\x00\xF0\x9F\x8D\xA5\x00\xF0\x9F\x8D\xA1\x00\xF0\x9F\xA5\x9F\x00"
+    "\xF0\x9F\xA5\xA1\x00\xF0\x9F\x8D\xA6\x00\xF0\x9F\x8D\xAA\x00\xF0\x9F\x8E\x82\x00\xF0\x9F\x8D\xB0\x00\xF0\x9F\xA5\xA7\x00\xF0\x9F\x8D\xAB\x00\xF0\x9F\x8D\xAF\x00"
+    "\xF0\x9F\x8D\xBC\x00\xF0\x9F\xA5\x9B\x00\xF0\x9F\x8D\xB5\x00\xF0\x9F\x8D\xB6\x00\xF0\x9F\x8D\xBE\x00\xF0\x9F\x8D\xB7\x00\xF0\x9F\x8D\xBB\x00\xF0\x9F\xA5\x82\x00"
+    "\xF0\x9F\xA5\x83\x00\xF0\x9F\xA5\xA4\x00\xF0\x9F\xA5\xA2\x00\xF0\x9F\x91\x81\x00\xF0\x9F\x91\x85\x00\xF0\x9F\x91\x84\x00\xF0\x9F\x92\x8B\x00\xF0\x9F\x92\x98\x00"
+    "\xF0\x9F\x92\x93\x00\xF0\x9F\x92\x97\x00\xF0\x9F\x92\x99\x00\xF0\x9F\x92\x9B\x00\xF0\x9F\xA7\xA1\x00\xF0\x9F\x92\x9C\x00\xF0\x9F\x96\xA4\x00\xF0\x9F\x92\x9D\x00"
+    "\xF0\x9F\x92\x9F\x00\xF0\x9F\x92\x8C\x00\xF0\x9F\x92\xA4\x00\xF0\x9F\x92\xA2\x00\xF0\x9F\x92\xA3\x00";
+
+struct {
+    char *text;
+    char *language;
+} const messages[] = { // Array containing all of the emojis messages
+    {"\x46\x61\x6C\x73\x63\x68\x65\x73\x20\xC3\x9C\x62\x65\x6E\x20\x76\x6F\x6E\x20\x58\x79\x6C\x6F\x70\x68\x6F\x6E\x6D\x75\x73\x69\x6B\x20\x71\x75\xC3\xA4\x6C"
+    "\x74\x20\x6A\x65\x64\x65\x6E\x20\x67\x72\xC3\xB6\xC3\x9F\x65\x72\x65\x6E\x20\x5A\x77\x65\x72\x67", "German"},
+    {"\x42\x65\x69\xC3\x9F\x20\x6E\x69\x63\x68\x74\x20\x69\x6E\x20\x64\x69\x65\x20\x48\x61\x6E\x64\x2C\x20\x64\x69\x65\x20\x64\x69\x63\x68\x20\x66\xC3\xBC\x74"
+    "\x74\x65\x72\x74\x2E", "German"},
+    {"\x41\x75\xC3\x9F\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\xC3\x9C\x62\x65\x6C\x20\x65\x72\x66\x6F\x72\x64\x65\x72\x6E\x20\x61\x75\xC3\x9F"
+    "\x65\x72\x6F\x72\x64\x65\x6E\x74\x6C\x69\x63\x68\x65\x20\x4D\x69\x74\x74\x65\x6C\x2E", "German"},
+    {"\xD4\xBF\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB4\x20\xD5\xA1\xD5\xBA\xD5\xA1\xD5\xAF\xD5\xAB\x20\xD5\xB8\xD6\x82\xD5\xBF\xD5\xA5\xD5\xAC\x20\xD6\x87\x20\xD5\xAB"
+    "\xD5\xB6\xD5\xAE\xD5\xAB\x20\xD5\xA1\xD5\xB6\xD5\xB0\xD5\xA1\xD5\xB6\xD5\xA3\xD5\xAB\xD5\xBD\xD5\xBF\x20\xD5\xB9\xD5\xA8\xD5\xB6\xD5\xA5\xD6\x80", "Armenian"},
+    {"\xD4\xB5\xD6\x80\xD5\xA2\x20\xD5\xB8\xD6\x80\x20\xD5\xAF\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\xD5\xA8\x20\xD5\xA5\xD5\xAF\xD5\xA1\xD6\x82\x20\xD5\xA1\xD5\xB6\xD5"
+    "\xBF\xD5\xA1\xD5\xBC\x2C\x20\xD5\xAE\xD5\xA1\xD5\xBC\xD5\xA5\xD6\x80\xD5\xA8\x20\xD5\xA1\xD5\xBD\xD5\xA1\xD6\x81\xD5\xAB\xD5\xB6\x2E\x2E\x2E\x20\xC2\xAB\xD4\xBF"
+    "\xD5\xB8\xD5\xBF\xD5\xA8\x20\xD5\xB4\xD5\xA5\xD6\x80\xD5\xB8\xD5\xB6\xD6\x81\xD5\xAB\xD6\x81\x20\xD5\xA7\x3A\xC2\xBB", "Armenian"},
+    {"\xD4\xB3\xD5\xA1\xD5\xBC\xD5\xA8\xD5\x9D\x20\xD5\xA3\xD5\xA1\xD6\x80\xD5\xB6\xD5\xA1\xD5\xB6\x2C\x20\xD5\xB1\xD5\xAB\xD6\x82\xD5\xB6\xD5\xA8\xD5\x9D\x20\xD5"
+    "\xB1\xD5\xB4\xD5\xBC\xD5\xA1\xD5\xB6", "Armenian"},
+    {"\x4A\x65\xC5\xBC\x75\x20\x6B\x6C\xC4\x85\x74\x77\x2C\x20\x73\x70\xC5\x82\xC3\xB3\x64\xC5\xBA\x20\x46\x69\x6E\x6F\x6D\x20\x63\x7A\xC4\x99\xC5\x9B\xC4\x87"
+    "\x20\x67\x72\x79\x20\x68\x61\xC5\x84\x62\x21", "Polish"},
+    {"\x44\x6F\x62\x72\x79\x6D\x69\x20\x63\x68\xC4\x99\x63\x69\x61\x6D\x69\x20\x6A\x65\x73\x74\x20\x70\x69\x65\x6B\xC5\x82\x6F\x20\x77\x79\x62\x72\x75\x6B\x6F"
+    "\x77\x61\x6E\x65\x2E", "Polish"},
+    {"\xC3\x8E\xC8\x9B\x69\x20\x6D\x75\x6C\xC8\x9B\x75\x6D\x65\x73\x63\x20\x63\xC4\x83\x20\x61\x69\x20\x61\x6C\x65\x73\x20\x72\x61\x79\x6C\x69\x62\x2E\x0A\xC8\x98"
+    "\x69\x20\x73\x70\x65\x72\x20\x73\xC4\x83\x20\x61\x69\x20\x6F\x20\x7A\x69\x20\x62\x75\x6E\xC4\x83\x21", "Romanian"},
+    {"\xD0\xAD\xD1\x85\x2C\x20\xD1\x87\xD1\x83\xD0\xB6\xD0\xB0\xD0\xBA\x2C\x20\xD0\xBE\xD0\xB1\xD1\x89\xD0\xB8\xD0\xB9\x20\xD1\x81\xD1\x8A\xD1\x91\xD0\xBC\x20"
+    "\xD1\x86\xD0\xB5\xD0\xBD\x20\xD1\x88\xD0\xBB\xD1\x8F\xD0\xBF\x20\x28\xD1\x8E\xD1\x84\xD1\x82\xD1\x8C\x29\x20\xD0\xB2\xD0\xB4\xD1\x80\xD1\x8B\xD0\xB7\xD0\xB3\x21", "Russian"},
+    {"\xD0\xAF\x20\xD0\xBB\xD1\x8E\xD0\xB1\xD0\xBB\xD1\x8E\x20\x72\x61\x79\x6C\x69\x62\x21", "Russian"},
+    {"\xD0\x9C\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2C\x20\xD1\x81\xD0\xBA\xD1\x80\xD1\x8B\xD0\xB2\xD0\xB0\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\x20\xD1\x82\xD0\xB0\xD0\xB8"
+    "\x0A\xD0\x98\x20\xD1\x87\xD1\x83\xD0\xB2\xD1\x81\xD1\x82\xD0\xB2\xD0\xB0\x20\xD0\xB8\x20\xD0\xBC\xD0\xB5\xD1\x87\xD1\x82\xD1\x8B\x20\xD1\x81\xD0\xB2\xD0\xBE\xD0\xB8\x20"
+    "\xE2\x80\x93\x0A\xD0\x9F\xD1\x83\xD1\x81\xD0\xBA\xD0\xB0\xD0\xB9\x20\xD0\xB2\x20\xD0\xB4\xD1\x83\xD1\x88\xD0\xB5\xD0\xB2\xD0\xBD\xD0\xBE\xD0\xB9\x20\xD0\xB3\xD0\xBB\xD1"
+    "\x83\xD0\xB1\xD0\xB8\xD0\xBD\xD0\xB5\x0A\xD0\x98\x20\xD0\xB2\xD1\x81\xD1\x85\xD0\xBE\xD0\xB4\xD1\x8F\xD1\x82\x20\xD0\xB8\x20\xD0\xB7\xD0\xB0\xD0\xB9\xD0\xB4\xD1\x83\xD1"
+    "\x82\x20\xD0\xBE\xD0\xBD\xD0\xB5\x0A\xD0\x9A\xD0\xB0\xD0\xBA\x20\xD0\xB7\xD0\xB2\xD0\xB5\xD0\xB7\xD0\xB4\xD1\x8B\x20\xD1\x8F\xD1\x81\xD0\xBD\xD1\x8B\xD0\xB5\x20\xD0\xB2"
+    "\x20\xD0\xBD\xD0\xBE\xD1\x87\xD0\xB8\x2D\x0A\xD0\x9B\xD1\x8E\xD0\xB1\xD1\x83\xD0\xB9\xD1\x81\xD1\x8F\x20\xD0\xB8\xD0\xBC\xD0\xB8\x20\xE2\x80\x93\x20\xD0\xB8\x20\xD0\xBC"
+    "\xD0\xBE\xD0\xBB\xD1\x87\xD0\xB8\x2E", "Russian"},
+    {"\x56\x6F\x69\x78\x20\x61\x6D\x62\x69\x67\x75\xC3\xAB\x20\x64\xE2\x80\x99\x75\x6E\x20\x63\xC5\x93\x75\x72\x20\x71\x75\x69\x20\x61\x75\x20\x7A\xC3\xA9\x70"
+    "\x68\x79\x72\x20\x70\x72\xC3\xA9\x66\xC3\xA8\x72\x65\x20\x6C\x65\x73\x20\x6A\x61\x74\x74\x65\x73\x20\x64\x65\x20\x6B\x69\x77\x69", "French"},
+    {"\x42\x65\x6E\x6A\x61\x6D\xC3\xAD\x6E\x20\x70\x69\x64\x69\xC3\xB3\x20\x75\x6E\x61\x20\x62\x65\x62\x69\x64\x61\x20\x64\x65\x20\x6B\x69\x77\x69\x20\x79\x20"
+    "\x66\x72\x65\x73\x61\x3B\x20\x4E\x6F\xC3\xA9\x2C\x20\x73\x69\x6E\x20\x76\x65\x72\x67\xC3\xBC\x65\x6E\x7A\x61\x2C\x20\x6C\x61\x20\x6D\xC3\xA1\x73\x20\x65\x78"
+    "\x71\x75\x69\x73\x69\x74\x61\x20\x63\x68\x61\x6D\x70\x61\xC3\xB1\x61\x20\x64\x65\x6C\x20\x6D\x65\x6E\xC3\xBA\x2E", "Spanish"},
+    {"\xCE\xA4\xCE\xB1\xCF\x87\xCE\xAF\xCF\x83\xCF\x84\xCE\xB7\x20\xCE\xB1\xCE\xBB\xCF\x8E\xCF\x80\xCE\xB7\xCE\xBE\x20\xCE\xB2\xCE\xB1\xCF\x86\xCE\xAE\xCF\x82\x20"
+    "\xCF\x88\xCE\xB7\xCE\xBC\xCE\xAD\xCE\xBD\xCE\xB7\x20\xCE\xB3\xCE\xB7\x2C\x20\xCE\xB4\xCF\x81\xCE\xB1\xCF\x83\xCE\xBA\xCE\xB5\xCE\xBB\xCE\xAF\xCE\xB6\xCE\xB5\xCE"
+    "\xB9\x20\xCF\x85\xCF\x80\xCE\xAD\xCF\x81\x20\xCE\xBD\xCF\x89\xCE\xB8\xCF\x81\xCE\xBF\xCF\x8D\x20\xCE\xBA\xCF\x85\xCE\xBD\xCF\x8C\xCF\x82", "Greek"},
+    {"\xCE\x97\x20\xCE\xBA\xCE\xB1\xCE\xBB\xCF\x8D\xCF\x84\xCE\xB5\xCF\x81\xCE\xB7\x20\xCE\xAC\xCE\xBC\xCF\x85\xCE\xBD\xCE\xB1\x20\xCE\xB5\xCE\xAF\xCE\xBD"
+    "\xCE\xB1\xCE\xB9\x20\xCE\xB7\x20\xCE\xB5\xCF\x80\xCE\xAF\xCE\xB8\xCE\xB5\xCF\x83\xCE\xB7\x2E", "Greek"},
+    {"\xCE\xA7\xCF\x81\xCF\x8C\xCE\xBD\xCE\xB9\xCE\xB1\x20\xCE\xBA\xCE\xB1\xCE\xB9\x20\xCE\xB6\xCE\xB1\xCE\xBC\xCE\xAC\xCE\xBD\xCE\xB9\xCE\xB1\x21", "Greek"},
+    {"\xCE\xA0\xCF\x8E\xCF\x82\x20\xCF\x84\xCE\xB1\x20\xCF\x80\xCE\xB1\xCF\x82\x20\xCF\x83\xCE\xAE\xCE\xBC\xCE\xB5\xCF\x81\xCE\xB1\x3B", "Greek"},
+
+    {"\xE6\x88\x91\xE8\x83\xBD\xE5\x90\x9E\xE4\xB8\x8B\xE7\x8E\xBB\xE7\x92\x83\xE8\x80\x8C\xE4\xB8\x8D\xE4\xBC\xA4\xE8\xBA\xAB\xE4\xBD\x93\xE3\x80\x82", "Chinese"},
+    {"\xE4\xBD\xA0\xE5\x90\x83\xE4\xBA\x86\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
+    {"\xE4\xB8\x8D\xE4\xBD\x9C\xE4\xB8\x8D\xE6\xAD\xBB\xE3\x80\x82", "Chinese"},
+    {"\xE6\x9C\x80\xE8\xBF\x91\xE5\xA5\xBD\xE5\x90\x97\xEF\xBC\x9F", "Chinese"},
+    {"\xE5\xA1\x9E\xE7\xBF\x81\xE5\xA4\xB1\xE9\xA9\xAC\xEF\xBC\x8C\xE7\x84\x89\xE7\x9F\xA5\xE9\x9D\x9E\xE7\xA6\x8F\xE3\x80\x82", "Chinese"},
+    {"\xE5\x8D\x83\xE5\x86\x9B\xE6\x98\x93\xE5\xBE\x97\x2C\x20\xE4\xB8\x80\xE5\xB0\x86\xE9\x9A\xBE\xE6\xB1\x82", "Chinese"},
+    {"\xE4\xB8\x87\xE4\xBA\x8B\xE5\xBC\x80\xE5\xA4\xB4\xE9\x9A\xBE\xE3\x80\x82", "Chinese"},
+    {"\xE9\xA3\x8E\xE6\x97\xA0\xE5\xB8\xB8\xE9\xA1\xBA\xEF\xBC\x8C\xE5\x85\xB5\xE6\x97\xA0\xE5\xB8\xB8\xE8\x83\x9C\xE3\x80\x82", "Chinese"},
+    {"\xE6\xB4\xBB\xE5\x88\xB0\xE8\x80\x81\xEF\xBC\x8C\xE5\xAD\xA6\xE5\x88\xB0\xE8\x80\x81\xE3\x80\x82", "Chinese"},
+    {"\xE4\xB8\x80\xE8\xA8\x80\xE6\x97\xA2\xE5\x87\xBA\xEF\xBC\x8C\xE9\xA9\xB7\xE9\xA9\xAC\xE9\x9A\xBE\xE8\xBF\xBD\xE3\x80\x82", "Chinese"},
+    {"\xE8\xB7\xAF\xE9\x81\xA5\xE7\x9F\xA5\xE9\xA9\xAC\xE5\x8A\x9B\xEF\xBC\x8C\xE6\x97\xA5\xE4\xB9\x85\xE8\xA7\x81\xE4\xBA\xBA\xE5\xBF\x83", "Chinese"},
+    {"\xE6\x9C\x89\xE7\x90\x86\xE8\xB5\xB0\xE9\x81\x8D\xE5\xA4\xA9\xE4\xB8\x8B\xEF\xBC\x8C\xE6\x97\xA0\xE7\x90\x86\xE5\xAF\xB8\xE6\xAD\xA5\xE9\x9A\xBE\xE8\xA1\x8C\xE3\x80\x82", "Chinese"},
+
+    {"\xE7\x8C\xBF\xE3\x82\x82\xE6\x9C\xA8\xE3\x81\x8B\xE3\x82\x89\xE8\x90\xBD\xE3\x81\xA1\xE3\x82\x8B", "Japanese"},
+    {"\xE4\xBA\x80\xE3\x81\xAE\xE7\x94\xB2\xE3\x82\x88\xE3\x82\x8A\xE5\xB9\xB4\xE3\x81\xAE\xE5\x8A\x9F", "Japanese"},
+    {"\xE3\x81\x86\xE3\x82\x89\xE3\x82\x84\xE3\x81\xBE\xE3\x81\x97\x20\x20\xE6\x80\x9D\xE3\x81\xB2\xE5\x88\x87\xE3\x82\x8B\xE6\x99\x82\x20\x20\xE7\x8C\xAB\xE3\x81\xAE\xE6\x81\x8B", "Japanese"},
+    {"\xE8\x99\x8E\xE7\xA9\xB4\xE3\x81\xAB\xE5\x85\xA5\xE3\x82\x89\xE3\x81\x9A\xE3\x82\x93\xE3\x81\xB0\xE8\x99\x8E\xE5\xAD\x90\xE3\x82\x92\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
+    {"\xE4\xBA\x8C\xE5\x85\x8E\xE3\x82\x92\xE8\xBF\xBD\xE3\x81\x86\xE8\x80\x85\xE3\x81\xAF\xE4\xB8\x80\xE5\x85\x8E\xE3\x82\x92\xE3\x82\x82\xE5\xBE\x97\xE3\x81\x9A\xE3\x80\x82", "Japanese"},
+    {"\xE9\xA6\xAC\xE9\xB9\xBF\xE3\x81\xAF\xE6\xAD\xBB\xE3\x81\xAA\xE3\x81\xAA\xE3\x81\x8D\xE3\x82\x83\xE6\xB2\xBB\xE3\x82\x89\xE3\x81\xAA\xE3\x81\x84\xE3\x80\x82", "Japanese"},
+    {"\xE6\x9E\xAF\xE9\x87\x8E\xE8\xB7\xAF\xE3\x81\xAB\xE3\x80\x80\xE5\xBD\xB1\xE3\x81\x8B\xE3\x81\x95\xE3\x81\xAA\xE3\x82\x8A\xE3\x81\xA6\xE3\x80\x80\xE3\x82\x8F\xE3\x81\x8B\xE3\x82\x8C\xE3\x81\x91\xE3\x82\x8A", "Japanese"},
+    {"\xE7\xB9\xB0\xE3\x82\x8A\xE8\xBF\x94\xE3\x81\x97\xE9\xBA\xA6\xE3\x81\xAE\xE7\x95\x9D\xE7\xB8\xAB\xE3\x81\xB5\xE8\x83\xA1\xE8\x9D\xB6\xE5\x93\x89", "Japanese"},
+
+    {"\xEC\x95\x84\xEB\x93\x9D\xED\x95\x9C\x20\xEB\xB0\x94\xEB\x8B\xA4\x20\xEC\x9C\x84\xEC\x97\x90\x20\xEA\xB0\x88\xEB\xA7\xA4\xEA\xB8\xB0\x20\xEB\x91\x90\xEC\x97\x87\x20"
+    "\xEB\x82\xA0\xEC\x95\x84\x20\xEB\x8F\x88\xEB\x8B\xA4\x2E\x0A\xEB\x84\x88\xED\x9B\x8C\xEB\x84\x88\xED\x9B\x8C\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E"
+    "\x20\xEB\xAA\xA8\xEB\xA5\xB4\xEB\x8A\x94\x20\xEB\x82\x98\xEB\x9D\xBC\x20\xEA\xB8\x80\xEC\x9E\x90\xEB\x8B\xA4\x2E\x0A\xEB\x84\x90\xEB\x94\xB0\xEB\x9E\x80\x20\xED\x95\x98"
+    "\xEB\x8A\x98\x20\xEB\xB3\xB5\xED\x8C\x90\xEC\x97\x90\x20\xEB\x82\x98\xEB\x8F\x84\x20\xEA\xB0\x99\xEC\x9D\xB4\x20\xEC\x8B\x9C\xEB\xA5\xBC\x20\xEC\x93\xB4\xEB\x8B\xA4\x2E", "Korean"},
+    {"\xEC\xA0\x9C\x20\xEB\x88\x88\xEC\x97\x90\x20\xEC\x95\x88\xEA\xB2\xBD\xEC\x9D\xB4\xEB\x8B\xA4", "Korean"},
+    {"\xEA\xBF\xA9\x20\xEB\xA8\xB9\xEA\xB3\xA0\x20\xEC\x95\x8C\x20\xEB\xA8\xB9\xEB\x8A\x94\xEB\x8B\xA4", "Korean"},
+    {"\xEB\xA1\x9C\xEB\xA7\x88\xEB\x8A\x94\x20\xED\x95\x98\xEB\xA3\xA8\xEC\x95\x84\xEC\xB9\xA8\xEC\x97\x90\x20\xEC\x9D\xB4\xEB\xA3\xA8\xEC\x96\xB4\xEC\xA7\x84\x20\xEA\xB2\x83\xEC\x9D\xB4"
+    "\x20\xEC\x95\x84\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
+    {"\xEA\xB3\xA0\xEC\x83\x9D\x20\xEB\x81\x9D\xEC\x97\x90\x20\xEB\x82\x99\xEC\x9D\xB4\x20\xEC\x98\xA8\xEB\x8B\xA4", "Korean"},
+    {"\xEA\xB0\x9C\xEC\xB2\x9C\xEC\x97\x90\xEC\x84\x9C\x20\xEC\x9A\xA9\x20\xEB\x82\x9C\xEB\x8B\xA4", "Korean"},
+    {"\xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
+    {"\xEB\xA7\x8C\xEB\x82\x98\xEC\x84\x9C\x20\xEB\xB0\x98\xEA\xB0\x91\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4", "Korean"},
+    {"\xED\x95\x9C\xEA\xB5\xAD\xEB\xA7\x90\x20\xED\x95\x98\xEC\x8B\xA4\x20\xEC\xA4\x84\x20\xEC\x95\x84\xEC\x84\xB8\xEC\x9A\x94\x3F", "Korean"},
+};
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+static void RandomizeEmoji(void);    // Fills the emoji array with random emojis
+
+static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint);   // Draw text using font inside rectangle limits
+static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint);    // Draw text using font inside rectangle limits with support for text selection
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode emojis");
+
+    // Load the font resources
+    // NOTE: fontAsian is for asian languages,
+    // fontEmoji is the emojis and fontDefault is used for everything else
+    Font fontDefault = LoadFont("resources/dejavu.fnt"); // Requires "resources/dejavu.png"
+    Font fontAsian = LoadFont("resources/noto_cjk.fnt"); // Requires "resources/noto_cjk.png"
+    Font fontEmoji = LoadFont("resources/symbola.fnt"); // Requires "resources/symbola.png"
+
+    Vector2 hoveredPos = { 0.0f, 0.0f };
+    Vector2 selectedPos = { 0.0f, 0.0f };
+
+    // Set a random set of emojis when starting up
+    RandomizeEmoji();
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Add a new set of emojis when SPACE is pressed
+        if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji();
+
+        // Set the selected emoji
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (hovered != -1) && (hovered != selected))
+        {
+            selected = hovered;
+            selectedPos = hoveredPos;
+        }
+
+        Vector2 mouse = GetMousePosition();
+        Vector2 position = { 28.8f, 10.0f };
+        hovered = -1;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Draw random emojis in the background
+            //------------------------------------------------------------------------------
+            for (int i = 0; i < SIZEOF(emoji); i++)
+            {
+                const char *txt = &emojiCodepoints[emoji[i].index];
+                Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize };
+
+                if (!CheckCollisionPointRec(mouse, emojiRect))
+                {
+                    DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f));
+                }
+                else
+                {
+                    DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, emoji[i].color );
+                    hovered = i;
+                    hoveredPos = position;
+                }
+
+                if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { position.y += fontEmoji.baseSize + 24.25f; position.x = 28.8f; }
+                else position.x += fontEmoji.baseSize + 28.8f;
+            }
+            //------------------------------------------------------------------------------
+
+            // Draw the message when a emoji is selected
+            //------------------------------------------------------------------------------
+            if (selected != -1)
+            {
+                const int message = emoji[selected].message;
+                const int horizontalPadding = 20, verticalPadding = 30;
+                Font *font = &fontDefault;
+
+                // Set correct font for asian languages
+                if (TextIsEqual(messages[message].language, "Chinese") ||
+                    TextIsEqual(messages[message].language, "Korean") ||
+                    TextIsEqual(messages[message].language, "Japanese")) font = &fontAsian;
+
+                // Calculate size for the message box (approximate the height and width)
+                Vector2 sz = MeasureTextEx(*font, messages[message].text, (float)font->baseSize, 1.0f);
+                if (sz.x > 300) { sz.y *= sz.x/300; sz.x = 300; }
+                else if (sz.x < 160) sz.x = 160;
+
+                Rectangle msgRect = { selectedPos.x - 38.8f, selectedPos.y, 2*horizontalPadding + sz.x, 2*verticalPadding + sz.y };
+                msgRect.y -= msgRect.height;
+
+                // Coordinates for the chat bubble triangle
+                Vector2 a = { selectedPos.x, msgRect.y + msgRect.height }, b = {a.x + 8, a.y + 10}, c= { a.x + 10, a.y };
+
+                // Don't go outside the screen
+                if (msgRect.x < 10) msgRect.x += 28;
+                if (msgRect.y < 10)
+                {
+                    msgRect.y = selectedPos.y + 84;
+                    a.y = msgRect.y;
+                    c.y = a.y;
+                    b.y = a.y - 10;
+
+                    // Swap values so we can actually render the triangle :(
+                    Vector2 tmp = a;
+                    a = b;
+                    b = tmp;
+                }
+
+                if (msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10;
+
+                // Draw chat bubble
+                DrawRectangleRec(msgRect, emoji[selected].color);
+                DrawTriangle(a, b, c, emoji[selected].color);
+
+                // Draw the main text message
+                Rectangle textRect = { msgRect.x + horizontalPadding/2, msgRect.y + verticalPadding/2, msgRect.width - horizontalPadding, msgRect.height };
+                DrawTextBoxed(*font, messages[message].text, textRect, (float)font->baseSize, 1.0f, true, WHITE);
+
+                // Draw the info text below the main message
+                int size = (int)strlen(messages[message].text);
+                int length = GetCodepointCount(messages[message].text);
+                const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, length, size);
+                sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f);
+
+                DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE);
+            }
+            //------------------------------------------------------------------------------
+
+            // Draw the info text
+            DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650)/2, screenHeight - 40, 20, GRAY);
+            DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484)/2, screenHeight - 16, 10, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadFont(fontDefault);    // Unload font resource
+    UnloadFont(fontAsian);      // Unload font resource
+    UnloadFont(fontEmoji);      // Unload font resource
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Fills the emoji array with random emoji (only those emojis present in fontEmoji)
+static void RandomizeEmoji(void)
+{
+    hovered = selected = -1;
+    int start = GetRandomValue(45, 360);
+
+    for (int i = 0; i < SIZEOF(emoji); i++)
+    {
+        // 0-179 emoji codepoints (from emoji char array) each 4bytes + null char
+        emoji[i].index = GetRandomValue(0, 179)*5;
+
+        // Generate a random color for this emoji
+        emoji[i].color = Fade(ColorFromHSV((float)((start*(i + 1))%360), 0.6f, 0.85f), 0.8f);
+
+        // Set a random message for this emoji
+        emoji[i].message = GetRandomValue(0, SIZEOF(messages) - 1);
+    }
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+
+// Draw text using font inside rectangle limits
+static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
+{
+    DrawTextBoxedSelectable(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
+}
+
+// Draw text using font inside rectangle limits with support for text selection
+static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
+{
+    int length = TextLength(text);  // Total length in bytes of the text, scanned by codepoints in loop
+
+    float textOffsetY = 0.0f;       // Offset between lines (on line break '\n')
+    float textOffsetX = 0.0f;       // Offset X to next character to draw
+
+    float scaleFactor = fontSize/(float)font.baseSize;     // Character rectangle scaling factor
+
+    // Word/character wrapping mechanism variables
+    enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
+    int state = wordWrap? MEASURE_STATE : DRAW_STATE;
+
+    int startLine = -1;         // Index where to begin drawing (where a line begins)
+    int endLine = -1;           // Index where to stop drawing (where a line ends)
+    int lastk = -1;             // Holds last value of the character position
+
+    for (int i = 0, k = 0; i < length; i++, k++)
+    {
+        // Get next codepoint from byte string and glyph index in font
+        int codepointByteCount = 0;
+        int codepoint = GetCodepoint(&text[i], &codepointByteCount);
+        int index = GetGlyphIndex(font, codepoint);
+
+        // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
+        // but we need to draw all of the bad bytes using the '?' symbol moving one byte
+        if (codepoint == 0x3f) codepointByteCount = 1;
+        i += (codepointByteCount - 1);
+
+        float glyphWidth = 0;
+        if (codepoint != '\n')
+        {
+            glyphWidth = (font.glyphs[index].advanceX == 0) ? font.recs[index].width*scaleFactor : font.glyphs[index].advanceX*scaleFactor;
+
+            if (i + 1 < length) glyphWidth = glyphWidth + spacing;
+        }
+
+        // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
+        // We store this info in startLine and endLine, then we change states, draw the text between those two variables
+        // and change states again and again recursively until the end of the text (or until we get outside of the container)
+        // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
+        // and begin drawing on the next line before we can get outside the container
+        if (state == MEASURE_STATE)
+        {
+            // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
+            // Ref: http://jkorpela.fi/chars/spaces.html
+            if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
+
+            if ((textOffsetX + glyphWidth) > rec.width)
+            {
+                endLine = (endLine < 1)? i : endLine;
+                if (i == endLine) endLine -= codepointByteCount;
+                if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
+
+                state = !state;
+            }
+            else if ((i + 1) == length)
+            {
+                endLine = i;
+                state = !state;
+            }
+            else if (codepoint == '\n') state = !state;
+
+            if (state == DRAW_STATE)
+            {
+                textOffsetX = 0;
+                i = startLine;
+                glyphWidth = 0;
+
+                // Save character position when we switch states
+                int tmp = lastk;
+                lastk = k - 1;
+                k = tmp;
+            }
+        }
+        else
+        {
+            if (codepoint == '\n')
+            {
+                if (!wordWrap)
+                {
+                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                    textOffsetX = 0;
+                }
+            }
+            else
+            {
+                if (!wordWrap && ((textOffsetX + glyphWidth) > rec.width))
+                {
+                    textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                    textOffsetX = 0;
+                }
+
+                // When text overflows rectangle height limit, just stop drawing
+                if ((textOffsetY + font.baseSize*scaleFactor) > rec.height) break;
+
+                // Draw selection background
+                bool isGlyphSelected = false;
+                if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
+                {
+                    DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
+                    isGlyphSelected = true;
+                }
+
+                // Draw current character glyph
+                if ((codepoint != ' ') && (codepoint != '\t'))
+                {
+                    DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
+                }
+            }
+
+            if (wordWrap && (i == endLine))
+            {
+                textOffsetY += (font.baseSize + font.baseSize/2)*scaleFactor;
+                textOffsetX = 0;
+                startLine = endLine;
+                endLine = -1;
+                glyphWidth = 0;
+                selectStart += lastk - k;
+                k = lastk;
+
+                state = !state;
+            }
+        }
+
+        textOffsetX += glyphWidth;
+    }
+}
diff --git a/raylib/examples/text/text_unicode_ranges.c b/raylib/examples/text/text_unicode_ranges.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_unicode_ranges.c
@@ -0,0 +1,205 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - unicode ranges
+*
+*   Example complexity rating: [★★★★] 4/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.6
+*
+*   Example contributed by Vadim Gunko (@GuvaCode) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Vadim Gunko (@GuvaCode) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>     // Required for: calloc(), free()
+
+//--------------------------------------------------------------------------------------
+// Module Functions Declaration
+//--------------------------------------------------------------------------------------
+// Add codepoint range to existing font
+static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode ranges");
+
+    // Load font with default Unicode range: Basic ASCII [32-127]
+    Font font = LoadFont("resources/NotoSansTC-Regular.ttf");
+    SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
+
+    int unicodeRange = 0;           // Track the ranges of codepoints added to font
+    int prevUnicodeRange = 0;       // Previous Unicode range to avoid reloading every frame
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (unicodeRange != prevUnicodeRange)
+        {
+            UnloadFont(font);
+
+            // Load font with default Unicode range: Basic ASCII [32-127]
+            font = LoadFont("resources/NotoSansTC-Regular.ttf");
+
+            // Add required ranges to loaded font
+            switch (unicodeRange)
+            {
+                /*
+                case 5:
+                {
+                    // Unicode range: Devanari, Arabic, Hebrew
+                    // WARNING: Glyphs not available on provided font!
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x900, 0x97f);  // Devanagari
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x600, 0x6ff);  // Arabic
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x5d0, 0x5ea);  // Hebrew
+                }
+                */
+                case 4:
+                {
+                    // Unicode range: CJK (Japanese and Chinese)
+                    // WARNING: Loading thousands of codepoints requires lot of time!
+                    // A better strategy is prefilter the required codepoints for the text
+                    // in the game and just load the required ones
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x4e00, 0x9fff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3400, 0x4dbf);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3000, 0x303f);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3040, 0x309f);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x30A0, 0x30ff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x31f0, 0x31ff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xff00, 0xffef);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xac00, 0xd7af);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1100, 0x11ff);
+                }
+                case 3:
+                {
+                    // Unicode range: Cyrillic
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x400, 0x4ff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x500, 0x52f);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2de0, 0x2Dff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xa640, 0xA69f);
+                }
+                case 2:
+                {
+                    // Unicode range: Greek
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x370, 0x3ff);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1f00, 0x1fff);
+                }
+                case 1:
+                {
+                    // Unicode range: European Languages
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xc0, 0x17f);
+                    AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x180, 0x24f);
+                    //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1e00, 0x1eff);
+                    //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2c60, 0x2c7f);
+                }
+                default: break;
+            }
+
+            prevUnicodeRange = unicodeRange;
+            SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); // Set font atlas scale filter
+        }
+
+        if (IsKeyPressed(KEY_ZERO)) unicodeRange = 0;
+        else if (IsKeyPressed(KEY_ONE)) unicodeRange = 1;
+        else if (IsKeyPressed(KEY_TWO)) unicodeRange = 2;
+        else if (IsKeyPressed(KEY_THREE)) unicodeRange = 3;
+        else if (IsKeyPressed(KEY_FOUR)) unicodeRange = 4;
+        //else if (IsKeyPressed(KEY_FIVE)) unicodeRange = 5;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("ADD CODEPOINTS: [1][2][3][4]", 20, 20, 20, MAROON);
+
+            // Render test strings in different languages
+            DrawTextEx(font, "> English: Hello World!", (Vector2){ 50, 70 }, 32, 1, DARKGRAY); // English
+            DrawTextEx(font, "> Español: Hola mundo!", (Vector2){ 50, 120 }, 32, 1, DARKGRAY); // Spanish
+            DrawTextEx(font, "> Ελληνικά: Γειά σου κόσμε!", (Vector2){ 50, 170 }, 32, 1, DARKGRAY); // Greek
+            DrawTextEx(font, "> Русский: Привет мир!", (Vector2){ 50, 220 }, 32, 0, DARKGRAY); // Russian
+            DrawTextEx(font, "> 中文: 你好世界!", (Vector2){ 50, 270 }, 32, 1, DARKGRAY);        // Chinese
+            DrawTextEx(font, "> 日本語: こんにちは世界!", (Vector2){ 50, 320 }, 32, 1, DARKGRAY); // Japanese
+            //DrawTextEx(font, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY);     // Devanagari (glyphs not available in font)
+
+            // Draw font texture scaled to screen
+            float atlasScale = 380.0f/font.texture.width;
+            DrawRectangleRec((Rectangle) { 400.0f, 16.0f, font.texture.width* atlasScale, font.texture.height* atlasScale }, BLACK);
+            DrawTexturePro(font.texture, (Rectangle){ 0, 0, (float)font.texture.width, (float)font.texture.height },
+                (Rectangle){ 400.0f, 16.0f, font.texture.width*atlasScale, font.texture.height*atlasScale }, (Vector2){ 0, 0 }, 0.0f, WHITE);
+            DrawRectangleLines(400, 16, 380, 380, RED);
+
+            DrawText(TextFormat("ATLAS SIZE: %ix%i px (x%02.2f)", font.texture.width, font.texture.height, atlasScale), 20, 380, 20, BLUE);
+            DrawText(TextFormat("CODEPOINTS GLYPHS LOADED: %i", font.glyphCount), 20, 410, 20, LIME);
+
+            // Display font attribution
+            DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", screenWidth - 300, screenHeight - 20, 10, GRAY);
+
+            if (prevUnicodeRange != unicodeRange)
+            {
+                DrawRectangle(0, 0, screenWidth, screenHeight, Fade(WHITE, 0.8f));
+                DrawRectangle(0, 125, screenWidth, 200, GRAY);
+                DrawText("GENERATING FONT ATLAS...", 120, 210, 40, BLACK);
+            }
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadFont(font);        // Unload font resource
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//--------------------------------------------------------------------------------------
+// Module Functions Definition
+//--------------------------------------------------------------------------------------
+// Add codepoint range to existing font
+static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop)
+{
+    int rangeSize = stop - start + 1;
+    int currentRangeSize = font->glyphCount;
+
+    // TODO: Load glyphs from provided vector font (if available),
+    // add them to existing font, regenerating font image and texture
+
+    int updatedCodepointCount = currentRangeSize + rangeSize;
+    int *updatedCodepoints = (int *)RL_CALLOC(updatedCodepointCount, sizeof(int));
+
+    // Get current codepoint list
+    for (int i = 0; i < currentRangeSize; i++) updatedCodepoints[i] = font->glyphs[i].value;
+
+    // Add new codepoints to list (provided range)
+    for (int i = currentRangeSize; i < updatedCodepointCount; i++)
+        updatedCodepoints[i] = start + (i - currentRangeSize);
+
+    UnloadFont(*font);
+    *font = LoadFontEx(fontPath, 32, updatedCodepoints, updatedCodepointCount);
+    RL_FREE(updatedCodepoints);
+}
+
diff --git a/raylib/examples/text/text_words_alignment.c b/raylib/examples/text/text_words_alignment.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/text/text_words_alignment.c
@@ -0,0 +1,134 @@
+/*******************************************************************************************
+*
+*   raylib [text] example - words alignment
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 5.6-dev
+*
+*   Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 JP Mortiboys (@themushroompirates)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"    // Required for: Lerp()
+
+typedef enum TextAlignment {
+    TEXT_ALIGN_LEFT   = 0,
+    TEXT_ALIGN_TOP    = 0,
+    TEXT_ALIGN_CENTRE = 1,
+    TEXT_ALIGN_MIDDLE = 1,
+    TEXT_ALIGN_RIGHT  = 2,
+    TEXT_ALIGN_BOTTOM = 2
+} TextAlignment;
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - words alignment");
+
+    // Define the rectangle we will draw the text in
+    Rectangle textContainerRect = (Rectangle){ screenWidth/2-screenWidth/4, screenHeight/2-screenHeight/3, screenWidth/2, screenHeight*2/3 };
+
+    // Some text to display the current alignment
+    const char *textAlignNameH[] = { "Left", "Centre", "Right" };
+    const char *textAlignNameV[] = { "Top", "Middle", "Bottom" };
+
+    // Define the text we're going to draw in the rectangle
+    int wordIndex = 0;
+    int wordCount = 0;
+    char **words = TextSplit("raylib is a simple and easy-to-use library to enjoy videogames programming", ' ', &wordCount);
+
+    // Initialize the font size we're going to use
+    int fontSize = 40;
+
+    // And of course the font...
+    Font font = GetFontDefault();
+
+    // Initialize the alignment variables
+    TextAlignment hAlign = TEXT_ALIGN_CENTRE;
+    TextAlignment vAlign = TEXT_ALIGN_MIDDLE;
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        if (IsKeyPressed(KEY_LEFT))
+        {
+            if (hAlign > 0) hAlign = hAlign - 1;
+        }
+
+        if (IsKeyPressed(KEY_RIGHT))
+        {
+            hAlign = hAlign + 1;
+            if (hAlign > 2) hAlign = 2;
+        }
+
+        if (IsKeyPressed(KEY_UP))
+        {
+            if (vAlign > 0) vAlign = vAlign - 1;
+        }
+
+        if (IsKeyPressed(KEY_DOWN))
+        {
+            vAlign = vAlign + 1;
+            if (vAlign > 2) vAlign = 2;
+        }
+
+        // One word per second
+        if (wordCount > 0)  wordIndex = (int)GetTime()%wordCount;
+        else  wordIndex = 0;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(DARKBLUE);
+
+            DrawText("Use Arrow Keys to change the text alignment", 20, 20, 20, LIGHTGRAY);
+            DrawText(TextFormat("Alignment: Horizontal = %s, Vertical = %s", textAlignNameH[hAlign], textAlignNameV[vAlign]), 20, 40, 20, LIGHTGRAY);
+
+            DrawRectangleRec(textContainerRect, BLUE);
+
+            // Get the size of the text to draw
+            Vector2 textSize = MeasureTextEx(font, words[wordIndex], fontSize, fontSize*.1f);
+
+            // Calculate the top-left text position based on the rectangle and alignment
+            Vector2 textPos = (Vector2){
+                textContainerRect.x + Lerp(0.0f, textContainerRect.width  - textSize.x, ((float)hAlign)*0.5f),
+                textContainerRect.y + Lerp(0.0f, textContainerRect.height - textSize.y, ((float)vAlign)*0.5f)
+            };
+
+            // Draw the text
+            DrawTextEx(font, words[wordIndex], textPos, fontSize, fontSize*.1f, RAYWHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/text/text_writing_anim.c b/raylib/examples/text/text_writing_anim.c
--- a/raylib/examples/text/text_writing_anim.c
+++ b/raylib/examples/text/text_writing_anim.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [text] example - Text Writing Animation
+*   raylib [text] example - writing anim
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.4, last time updated with raylib 1.4
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [text] example - text writing anim");
+    InitWindow(screenWidth, screenHeight, "raylib [text] example - writing anim");
 
     const char message[128] = "This sample illustrates a text writing\nanimation effect! Check it out! ;)";
 
diff --git a/raylib/examples/textures/textures_background_scrolling.c b/raylib/examples/textures/textures_background_scrolling.c
--- a/raylib/examples/textures/textures_background_scrolling.c
+++ b/raylib/examples/textures/textures_background_scrolling.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Background scrolling
+*   raylib [textures] example - background scrolling
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 2.0, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_blend_modes.c b/raylib/examples/textures/textures_blend_modes.c
--- a/raylib/examples/textures/textures_blend_modes.c
+++ b/raylib/examples/textures/textures_blend_modes.c
@@ -2,6 +2,8 @@
 *
 *   raylib [textures] example - blend modes
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 3.5, last time updated with raylib 3.5
@@ -11,7 +13,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2020-2024 Karlo Licudine (@accidentalrebel)
+*   Copyright (c) 2020-2025 Karlo Licudine (@accidentalrebel)
 *
 ********************************************************************************************/
 
@@ -76,10 +78,10 @@
 
             switch (blendMode)
             {
-                case BLEND_ALPHA: DrawText("Current: BLEND_ALPHA", (screenWidth / 2) - 60, 370, 10, GRAY); break;
-                case BLEND_ADDITIVE: DrawText("Current: BLEND_ADDITIVE", (screenWidth / 2) - 60, 370, 10, GRAY); break;
-                case BLEND_MULTIPLIED: DrawText("Current: BLEND_MULTIPLIED", (screenWidth / 2) - 60, 370, 10, GRAY); break;
-                case BLEND_ADD_COLORS: DrawText("Current: BLEND_ADD_COLORS", (screenWidth / 2) - 60, 370, 10, GRAY); break;
+                case BLEND_ALPHA: DrawText("Current: BLEND_ALPHA", (screenWidth/2) - 60, 370, 10, GRAY); break;
+                case BLEND_ADDITIVE: DrawText("Current: BLEND_ADDITIVE", (screenWidth/2) - 60, 370, 10, GRAY); break;
+                case BLEND_MULTIPLIED: DrawText("Current: BLEND_MULTIPLIED", (screenWidth/2) - 60, 370, 10, GRAY); break;
+                case BLEND_ADD_COLORS: DrawText("Current: BLEND_ADD_COLORS", (screenWidth/2) - 60, 370, 10, GRAY); break;
                 default: break;
             }
 
diff --git a/raylib/examples/textures/textures_bunnymark.c b/raylib/examples/textures/textures_bunnymark.c
--- a/raylib/examples/textures/textures_bunnymark.c
+++ b/raylib/examples/textures/textures_bunnymark.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Bunnymark
+*   raylib [textures] example - bunnymark
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 1.6, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -21,6 +23,9 @@
 // NOTE: This value is defined in [rlgl] module and can be changed there
 #define MAX_BATCH_ELEMENTS  8192
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 typedef struct Bunny {
     Vector2 position;
     Vector2 speed;
@@ -40,7 +45,7 @@
     InitWindow(screenWidth, screenHeight, "raylib [textures] example - bunnymark");
 
     // Load bunny texture
-    Texture2D texBunny = LoadTexture("resources/wabbit_alpha.png");
+    Texture2D texBunny = LoadTexture("resources/raybunny.png");
 
     Bunny *bunnies = (Bunny *)malloc(MAX_BUNNIES*sizeof(Bunny));    // Bunnies array
 
diff --git a/raylib/examples/textures/textures_cellular_automata.c b/raylib/examples/textures/textures_cellular_automata.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_cellular_automata.c
@@ -0,0 +1,212 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - cellular automata
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jordi Santonja (@JordSant) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Jordi Santonja (@JordSant)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+// Initialization constants
+//--------------------------------------------------------------------------------------
+const int screenWidth = 800;
+const int screenHeight = 450;
+const int imageWidth = 800;
+const int imageHeight = 800/2;
+
+// Rule button sizes and positions
+const int drawRuleStartX       = 585;
+const int drawRuleStartY       = 10;
+const int drawRuleSpacing      = 15;
+const int drawRuleGroupSpacing = 50;
+const int drawRuleSize         = 14;
+const int drawRuleInnerSize    = 10;
+
+// Preset button sizes
+const int presetsSizeX = 42;
+const int presetsSizeY = 22;
+
+const int linesUpdatedPerFrame = 4;
+
+//----------------------------------------------------------------------------------
+// Functions
+//----------------------------------------------------------------------------------
+void ComputeLine(Image *image, int line, int rule)
+{
+    // Compute next line pixels. Boundaries are not computed, always 0
+    for (int i = 1; i < imageWidth - 1; i++)
+    {
+        // Get, from the previous line, the 3 pixels states as a binary value
+        const int prevValue = ((GetImageColor(*image, i - 1, line - 1).r < 5)? 4 : 0) +     // Left pixel
+                              ((GetImageColor(*image, i,     line - 1).r < 5)? 2 : 0) +     // Center pixel
+                              ((GetImageColor(*image, i + 1, line - 1).r < 5)? 1 : 0);      // Right pixel
+        // Get next value from rule bitmask
+        const bool currValue = (rule & (1 << prevValue));
+        // Update pixel color
+        ImageDrawPixel(image, i, line, (currValue)? BLACK : RAYWHITE);
+    }
+}
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - cellular automata");
+
+    // Image that contains the cellular automaton
+    Image image = GenImageColor(imageWidth, imageHeight, RAYWHITE);
+    // The top central pixel set as black
+    ImageDrawPixel(&image, imageWidth/2, 0, BLACK);
+
+    Texture2D texture = LoadTextureFromImage(image);
+
+    // Some interesting rules
+    const int presetValues[] = { 18, 30, 60, 86, 102, 124, 126, 150, 182, 225 };
+    const int presetsCount = sizeof(presetValues)/sizeof(presetValues[0]);
+
+    // Variables
+    int rule = 30;  // Starting rule
+    int line = 1;   // Line to compute, starting from line 1. One point in line 0 is already set
+
+    SetTargetFPS(60);
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Handle mouse
+        const Vector2 mouse = GetMousePosition();
+        int mouseInCell = -1;   // -1: outside any button; 0-7: rule cells; 8+: preset cells
+
+        // Check mouse on rule cells
+        for (int i = 0; i < 8; i++)
+        {
+            const int cellX = drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing;
+            const int cellY = drawRuleStartY + drawRuleSpacing;
+            if ((mouse.x >= cellX) && (mouse.x <= cellX + drawRuleSize) &&
+                (mouse.y >= cellY) && (mouse.y <= cellY + drawRuleSize))
+            {
+                mouseInCell = i;    // 0-7: rule cells
+                break;
+            }
+        }
+
+        // Check mouse on preset cells
+        if (mouseInCell < 0)
+        {
+            for (int i = 0; i < presetsCount; i++)
+            {
+                const int cellX = 4 + (presetsSizeX + 2)*(i/2);
+                const int cellY = 2 + (presetsSizeY + 2)*(i%2);
+                if ((mouse.x >= cellX) && (mouse.x <= cellX + presetsSizeX) &&
+                    (mouse.y >= cellY) && (mouse.y <= cellY + presetsSizeY))
+                {
+                    mouseInCell = i + 8;    // 8+: preset cells
+                    break;
+                }
+            }
+        }
+
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (mouseInCell >= 0))
+        {
+            // Rule changed both by selecting a preset or toggling a bit
+            if (mouseInCell < 8)
+                rule ^= (1 << mouseInCell);
+            else
+                rule = presetValues[mouseInCell - 8];
+
+            // Reset image
+            ImageClearBackground(&image, RAYWHITE);
+            ImageDrawPixel(&image, imageWidth/2, 0, BLACK);
+            line = 1;
+        }
+
+        // Compute next lines
+        //----------------------------------------------------------------------------------
+        if (line < imageHeight)
+        {
+            for (int i = 0; (i < linesUpdatedPerFrame) && (line + i < imageHeight); i++)
+                ComputeLine(&image, line + i, rule);
+            line += linesUpdatedPerFrame;
+
+            UpdateTexture(texture, image.data);
+        }
+
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw cellular automaton texture
+            DrawTexture(texture, 0, screenHeight - imageHeight, WHITE);
+
+            // Draw preset values
+            for (int i = 0; i < presetsCount; i++)
+            {
+                DrawText(TextFormat("%i", presetValues[i]), 8 + (presetsSizeX + 2)*(i/2), 4 + (presetsSizeY + 2)*(i%2), 20, GRAY);
+                DrawRectangleLines(4 + (presetsSizeX + 2)*(i/2), 2 + (presetsSizeY + 2)*(i%2), presetsSizeX, presetsSizeY, BLUE);
+
+                // If the mouse is on this preset, highlight it
+                if (mouseInCell == i + 8)
+                    DrawRectangleLinesEx((Rectangle) { 2 + (presetsSizeX + 2.0f)*(i/2),
+                                                       (presetsSizeY + 2.0f)*(i%2),
+                                                       presetsSizeX + 4.0f, presetsSizeY + 4.0f }, 3, RED);
+            }
+
+            // Draw rule bits
+            for (int i = 0; i < 8; i++)
+            {
+                // The three input bits
+                for (int j = 0; j < 3; j++)
+                {
+                    DrawRectangleLines(drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing*j, drawRuleStartY, drawRuleSize, drawRuleSize, GRAY);
+                    if (i & (4 >> j))
+                        DrawRectangle(drawRuleStartX + 2 - drawRuleGroupSpacing*i + drawRuleSpacing*j, drawRuleStartY + 2, drawRuleInnerSize, drawRuleInnerSize, BLACK);
+                }
+
+                // The output bit
+                DrawRectangleLines(drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing, drawRuleStartY + drawRuleSpacing, drawRuleSize, drawRuleSize, BLUE);
+                if (rule & (1 << i))
+                    DrawRectangle(drawRuleStartX + 2 - drawRuleGroupSpacing*i + drawRuleSpacing, drawRuleStartY + 2 + drawRuleSpacing, drawRuleInnerSize, drawRuleInnerSize, BLACK);
+
+                // If the mouse is on this rule bit, highlight it
+                if (mouseInCell == i)
+                    DrawRectangleLinesEx((Rectangle){ drawRuleStartX - drawRuleGroupSpacing*i + drawRuleSpacing - 2.0f,
+                                                      drawRuleStartY + drawRuleSpacing - 2.0f,
+                                                      drawRuleSize + 4.0f, drawRuleSize + 4.0f }, 3, RED);
+            }
+
+            DrawText(TextFormat("RULE: %i", rule), drawRuleStartX + drawRuleSpacing*4, drawRuleStartY + 1, 30, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadImage(image);
+    UnloadTexture(texture);
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
diff --git a/raylib/examples/textures/textures_draw_tiled.c b/raylib/examples/textures/textures_draw_tiled.c
deleted file mode 100644
--- a/raylib/examples/textures/textures_draw_tiled.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [textures] example - Draw part of the texture tiled
-*
-*   Example originally created with raylib 3.0, last time updated with raylib 4.2
-*
-*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2020-2024 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#define SIZEOF(A) (sizeof(A)/sizeof(A[0]))
-#define OPT_WIDTH       220       // Max width for the options container
-#define MARGIN_SIZE       8       // Size for the margins
-#define COLOR_SIZE       16       // Size of the color select buttons
-
-// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.
-void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    SetConfigFlags(FLAG_WINDOW_RESIZABLE); // Make the window resizable
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - Draw part of a texture tiled");
-
-    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
-    Texture texPattern = LoadTexture("resources/patterns.png");
-    SetTextureFilter(texPattern, TEXTURE_FILTER_TRILINEAR); // Makes the texture smoother when upscaled
-
-    // Coordinates for all patterns inside the texture
-    const Rectangle recPattern[] = {
-        (Rectangle){ 3, 3, 66, 66 },
-        (Rectangle){ 75, 3, 100, 100 },
-        (Rectangle){ 3, 75, 66, 66 },
-        (Rectangle){ 7, 156, 50, 50 },
-        (Rectangle){ 85, 106, 90, 45 },
-        (Rectangle){ 75, 154, 100, 60}
-    };
-
-    // Setup colors
-    const Color colors[] = { BLACK, MAROON, ORANGE, BLUE, PURPLE, BEIGE, LIME, RED, DARKGRAY, SKYBLUE };
-    enum { MAX_COLORS = SIZEOF(colors) };
-    Rectangle colorRec[MAX_COLORS] = { 0 };
-
-    // Calculate rectangle for each color
-    for (int i = 0, x = 0, y = 0; i < MAX_COLORS; i++)
-    {
-        colorRec[i].x = 2.0f + MARGIN_SIZE + x;
-        colorRec[i].y = 22.0f + 256.0f + MARGIN_SIZE + y;
-        colorRec[i].width = COLOR_SIZE*2.0f;
-        colorRec[i].height = (float)COLOR_SIZE;
-
-        if (i == (MAX_COLORS/2 - 1))
-        {
-            x = 0;
-            y += COLOR_SIZE + MARGIN_SIZE;
-        }
-        else x += (COLOR_SIZE*2 + MARGIN_SIZE);
-    }
-
-    int activePattern = 0, activeCol = 0;
-    float scale = 1.0f, rotation = 0.0f;
-
-    SetTargetFPS(60);
-    //---------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // Handle mouse
-        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
-        {
-            const Vector2 mouse = GetMousePosition();
-
-            // Check which pattern was clicked and set it as the active pattern
-            for (int i = 0; i < SIZEOF(recPattern); i++)
-            {
-                if (CheckCollisionPointRec(mouse, (Rectangle){ 2 + MARGIN_SIZE + recPattern[i].x, 40 + MARGIN_SIZE + recPattern[i].y, recPattern[i].width, recPattern[i].height }))
-                {
-                    activePattern = i;
-                    break;
-                }
-            }
-
-            // Check to see which color was clicked and set it as the active color
-            for (int i = 0; i < MAX_COLORS; ++i)
-            {
-                if (CheckCollisionPointRec(mouse, colorRec[i]))
-                {
-                    activeCol = i;
-                    break;
-                }
-            }
-        }
-
-        // Handle keys
-
-        // Change scale
-        if (IsKeyPressed(KEY_UP)) scale += 0.25f;
-        if (IsKeyPressed(KEY_DOWN)) scale -= 0.25f;
-        if (scale > 10.0f) scale = 10.0f;
-        else if ( scale <= 0.0f) scale = 0.25f;
-
-        // Change rotation
-        if (IsKeyPressed(KEY_LEFT)) rotation -= 25.0f;
-        if (IsKeyPressed(KEY_RIGHT)) rotation += 25.0f;
-
-        // Reset
-        if (IsKeyPressed(KEY_SPACE)) { rotation = 0.0f; scale = 1.0f; }
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-            ClearBackground(RAYWHITE);
-
-            // Draw the tiled area
-            DrawTextureTiled(texPattern, recPattern[activePattern], (Rectangle){(float)OPT_WIDTH+MARGIN_SIZE, (float)MARGIN_SIZE, GetScreenWidth() - OPT_WIDTH - 2.0f*MARGIN_SIZE, GetScreenHeight() - 2.0f*MARGIN_SIZE},
-                (Vector2){0.0f, 0.0f}, rotation, scale, colors[activeCol]);
-
-            // Draw options
-            DrawRectangle(MARGIN_SIZE, MARGIN_SIZE, OPT_WIDTH - MARGIN_SIZE, GetScreenHeight() - 2*MARGIN_SIZE, ColorAlpha(LIGHTGRAY, 0.5f));
-
-            DrawText("Select Pattern", 2 + MARGIN_SIZE, 30 + MARGIN_SIZE, 10, BLACK);
-            DrawTexture(texPattern, 2 + MARGIN_SIZE, 40 + MARGIN_SIZE, BLACK);
-            DrawRectangle(2 + MARGIN_SIZE + (int)recPattern[activePattern].x, 40 + MARGIN_SIZE + (int)recPattern[activePattern].y, (int)recPattern[activePattern].width, (int)recPattern[activePattern].height, ColorAlpha(DARKBLUE, 0.3f));
-
-            DrawText("Select Color", 2+MARGIN_SIZE, 10+256+MARGIN_SIZE, 10, BLACK);
-            for (int i = 0; i < MAX_COLORS; i++)
-            {
-                DrawRectangleRec(colorRec[i], colors[i]);
-                if (activeCol == i) DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(WHITE, 0.5f));
-            }
-
-            DrawText("Scale (UP/DOWN to change)", 2 + MARGIN_SIZE, 80 + 256 + MARGIN_SIZE, 10, BLACK);
-            DrawText(TextFormat("%.2fx", scale), 2 + MARGIN_SIZE, 92 + 256 + MARGIN_SIZE, 20, BLACK);
-
-            DrawText("Rotation (LEFT/RIGHT to change)", 2 + MARGIN_SIZE, 122 + 256 + MARGIN_SIZE, 10, BLACK);
-            DrawText(TextFormat("%.0f degrees", rotation), 2 + MARGIN_SIZE, 134 + 256 + MARGIN_SIZE, 20, BLACK);
-
-            DrawText("Press [SPACE] to reset", 2 + MARGIN_SIZE, 164 + 256 + MARGIN_SIZE, 10, DARKBLUE);
-
-            // Draw FPS
-            DrawText(TextFormat("%i FPS", GetFPS()), 2 + MARGIN_SIZE, 2 + MARGIN_SIZE, 20, BLACK);
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texPattern);        // Unload texture
-
-    CloseWindow();              // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest.
-void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
-{
-    if ((texture.id <= 0) || (scale <= 0.0f)) return;  // Wanna see a infinite loop?!...just delete this line!
-    if ((source.width == 0) || (source.height == 0)) return;
-
-    int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
-    if ((dest.width < tileWidth) && (dest.height < tileHeight))
-    {
-        // Can fit only one tile
-        DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
-                    (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint);
-    }
-    else if (dest.width <= tileWidth)
-    {
-        // Tiled vertically (one column)
-        int dy = 0;
-        for (;dy+tileHeight < dest.height; dy += tileHeight)
-        {
-            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint);
-        }
-
-        // Fit last tile
-        if (dy < dest.height)
-        {
-            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
-                        (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint);
-        }
-    }
-    else if (dest.height <= tileHeight)
-    {
-        // Tiled horizontally (one row)
-        int dx = 0;
-        for (;dx+tileWidth < dest.width; dx += tileWidth)
-        {
-            DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint);
-        }
-
-        // Fit last tile
-        if (dx < dest.width)
-        {
-            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
-                        (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint);
-        }
-    }
-    else
-    {
-        // Tiled both horizontally and vertically (rows and columns)
-        int dx = 0;
-        for (;dx+tileWidth < dest.width; dx += tileWidth)
-        {
-            int dy = 0;
-            for (;dy+tileHeight < dest.height; dy += tileHeight)
-            {
-                DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint);
-            }
-
-            if (dy < dest.height)
-            {
-                DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
-                    (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint);
-            }
-        }
-
-        // Fit last column of tiles
-        if (dx < dest.width)
-        {
-            int dy = 0;
-            for (;dy+tileHeight < dest.height; dy += tileHeight)
-            {
-                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height},
-                        (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint);
-            }
-
-            // Draw final tile in the bottom right corner
-            if (dy < dest.height)
-            {
-                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
-                    (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint);
-            }
-        }
-    }
-}
diff --git a/raylib/examples/textures/textures_fog_of_war.c b/raylib/examples/textures/textures_fog_of_war.c
--- a/raylib/examples/textures/textures_fog_of_war.c
+++ b/raylib/examples/textures/textures_fog_of_war.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Fog of war
+*   raylib [textures] example - fog of war
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -19,6 +21,9 @@
 #define PLAYER_SIZE      16         // Player size
 #define PLAYER_TILE_VISIBILITY  2   // Player can see 2 tiles around its position
 
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
 // Map data type
 typedef struct Map {
     unsigned int tilesX;            // Number of tiles in X axis
diff --git a/raylib/examples/textures/textures_framebuffer_rendering.c b/raylib/examples/textures/textures_framebuffer_rendering.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_framebuffer_rendering.c
@@ -0,0 +1,208 @@
+﻿/*******************************************************************************************
+*
+*   raylib [textures] example - framebuffer rendering
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6, last time updated with raylib 5.6
+*
+*   Example contributed by Jack Boakes (@jackboakes) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2026 Jack Boakes (@jackboakes)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+
+//------------------------------------------------------------------------------------
+// Module Functions Declaration
+//------------------------------------------------------------------------------------
+static void DrawCameraPrism(Camera3D camera, float aspect, Color color);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+    const int splitWidth = screenWidth/2;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - framebuffer rendering");
+
+    // Camera to look at the 3D world
+    Camera3D subjectCamera = { 0 };
+    subjectCamera.position = (Vector3){ 5.0f, 5.0f, 5.0f };
+    subjectCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    subjectCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    subjectCamera.fovy = 45.0f;
+    subjectCamera.projection = CAMERA_PERSPECTIVE;
+
+    // Camera to observe the subject camera and 3D world
+    Camera3D observerCamera = { 0 };
+    observerCamera.position = (Vector3){ 10.0f, 10.0f, 10.0f };
+    observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+    observerCamera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
+    observerCamera.fovy = 45.0f;
+    observerCamera.projection = CAMERA_PERSPECTIVE;
+
+    // Set up render textures
+    RenderTexture2D observerTarget = LoadRenderTexture(splitWidth, screenHeight);
+    Rectangle observerSource = { 0.0f, 0.0f, (float)observerTarget.texture.width, -(float)observerTarget.texture.height };
+    Rectangle observerDest = { 0.0f, 0.0f, (float)splitWidth, (float)screenHeight };
+
+    RenderTexture2D subjectTarget = LoadRenderTexture(splitWidth, screenHeight);
+    Rectangle subjectSource = { 0.0f, 0.0f, (float)subjectTarget.texture.width, -(float)subjectTarget.texture.height };
+    Rectangle subjectDest = { (float)splitWidth, 0.0f, (float)splitWidth, (float)screenHeight };
+    const float textureAspectRatio = (float)subjectTarget.texture.width/(float)subjectTarget.texture.height;
+
+    // Rectangles for cropping render texture
+    const float captureSize = 128.0f;
+    Rectangle cropSource = { (subjectTarget.texture.width - captureSize)/2.0f, (subjectTarget.texture.height - captureSize)/2.0f, captureSize, -captureSize };
+    Rectangle cropDest = { splitWidth + 20, 20, captureSize, captureSize};
+
+    SetTargetFPS(60);
+    DisableCursor();
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        UpdateCamera(&observerCamera, CAMERA_FREE);
+        UpdateCamera(&subjectCamera, CAMERA_ORBITAL);
+
+        if (IsKeyPressed(KEY_R)) observerCamera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
+
+        // Build LHS observer view texture
+        BeginTextureMode(observerTarget);
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(observerCamera);
+
+                DrawGrid(10, 1.0f);
+                DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD);
+                DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK);
+                DrawCameraPrism(subjectCamera, textureAspectRatio, GREEN);
+
+            EndMode3D();
+
+            DrawText("Observer View", 10, observerTarget.texture.height - 30, 20, BLACK);
+            DrawText("WASD + Mouse to Move", 10, 10, 20, DARKGRAY);
+            DrawText("Scroll to Zoom", 10, 30, 20, DARKGRAY);
+            DrawText("R to Reset Observer Target", 10, 50, 20, DARKGRAY);
+
+        EndTextureMode();
+
+        // Build RHS subject view texture
+        BeginTextureMode(subjectTarget);
+
+            ClearBackground(RAYWHITE);
+
+            BeginMode3D(subjectCamera);
+
+                DrawCube((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, GOLD);
+                DrawCubeWires((Vector3){ 0.0f, 0.0f, 0.0f }, 2.0f, 2.0f, 2.0f, PINK);
+                DrawGrid(10, 1.0f);
+
+            EndMode3D();
+
+            DrawRectangleLines((subjectTarget.texture.width - captureSize)/2, (subjectTarget.texture.height - captureSize)/2, captureSize, captureSize, GREEN);
+            DrawText("Subject View", 10, subjectTarget.texture.height - 30, 20, BLACK);
+
+        EndTextureMode();
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(BLACK);
+
+            // Draw observer texture LHS
+            DrawTexturePro(observerTarget.texture, observerSource, observerDest, (Vector2){0.0f, 0.0f }, 0.0f, WHITE);
+
+            // Draw subject texture RHS
+            DrawTexturePro(subjectTarget.texture, subjectSource, subjectDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
+
+            // Draw the small crop overlay on top
+            DrawTexturePro(subjectTarget.texture, cropSource, cropDest, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
+            DrawRectangleLinesEx(cropDest, 2, BLACK);
+
+            // Draw split screen divider line
+            DrawLine(splitWidth, 0, splitWidth, screenHeight, BLACK);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadRenderTexture(observerTarget);
+    UnloadRenderTexture(subjectTarget);
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+static void DrawCameraPrism(Camera3D camera, float aspect, Color color)
+{
+    float length = Vector3Distance(camera.position, camera.target);
+    // Define the 4 corners of the camera's prism plane sliced at the target in Normalized Device Coordinates
+    Vector3 planeNDC[4] = {
+        { -1.0f, -1.0f, 1.0f }, // Bottom Left
+        {  1.0f, -1.0f, 1.0f }, // Bottom Right
+        {  1.0f,  1.0f, 1.0f }, // Top Right
+        { -1.0f,  1.0f, 1.0f }  // Top Left
+    };
+
+    // Build the matrices
+    Matrix view = GetCameraMatrix(camera);
+    Matrix proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect, 0.05f, length);
+    // Combine view and projection so we can reverse the full camera transform
+    Matrix viewProj = MatrixMultiply(view, proj);
+    // Invert the view-projection matrix to unproject points from NDC space back into world space
+    Matrix inverseViewProj = MatrixInvert(viewProj);
+
+    // Transform the 4 plane corners from NDC into world space
+    Vector3 corners[4];
+    for (int i = 0; i < 4; i++)
+    {
+        float x = planeNDC[i].x;
+        float y = planeNDC[i].y;
+        float z = planeNDC[i].z;
+
+        // Multiply NDC position by the inverse view-projection matrix
+        // This produces a homogeneous (x, y, z, w) position in world space
+        float vx = inverseViewProj.m0*x + inverseViewProj.m4*y + inverseViewProj.m8*z + inverseViewProj.m12;
+        float vy = inverseViewProj.m1*x + inverseViewProj.m5*y + inverseViewProj.m9*z + inverseViewProj.m13;
+        float vz = inverseViewProj.m2*x + inverseViewProj.m6*y + inverseViewProj.m10*z + inverseViewProj.m14;
+        float vw = inverseViewProj.m3*x + inverseViewProj.m7*y + inverseViewProj.m11*z + inverseViewProj.m15;
+
+        corners[i] = (Vector3){ vx/vw, vy/vw, vz/vw };
+    }
+
+    // Draw the far plane sliced at the target
+    DrawLine3D(corners[0], corners[1], color);
+    DrawLine3D(corners[1], corners[2], color);
+    DrawLine3D(corners[2], corners[3], color);
+    DrawLine3D(corners[3], corners[0], color);
+
+    // Draw the prism lines from the far plane to the camera position
+    for (int i = 0; i < 4; i++)
+    {
+        DrawLine3D(camera.position, corners[i], color);
+    }
+}
diff --git a/raylib/examples/textures/textures_gif_player.c b/raylib/examples/textures/textures_gif_player.c
--- a/raylib/examples/textures/textures_gif_player.c
+++ b/raylib/examples/textures/textures_gif_player.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - gif playing
+*   raylib [textures] example - gif player
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 4.2, last time updated with raylib 4.2
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2021-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -26,7 +28,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - gif playing");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - gif player");
 
     int animFrames = 0;
 
diff --git a/raylib/examples/textures/textures_image_channel.c b/raylib/examples/textures/textures_image_channel.c
--- a/raylib/examples/textures/textures_image_channel.c
+++ b/raylib/examples/textures/textures_image_channel.c
@@ -1,21 +1,23 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Retrive image channel (mask)
+*   raylib [textures] example - image channel
 *
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
-*   Example originally created with raylib 5.1-dev, last time updated with raylib 5.1-dev
+*   Example complexity rating: [★★☆☆] 2/4
 *
-*   Example contributed by Bruno Cabral (github.com/brccabral) and reviewed by Ramon Santamaria (@raysan5)
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
 *
+*   Example contributed by Bruno Cabral (@brccabral) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2024-2024 Bruno Cabral (github.com/brccabral) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2024-2025 Bruno Cabral (@brccabral) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
-#include <raylib.h>
+#include "raylib.h"
 
 //------------------------------------------------------------------------------------
 // Program main entry point
@@ -24,11 +26,10 @@
 {
     // Initialization
     //--------------------------------------------------------------------------------------
-
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - extract channel from image");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image channel");
 
     Image fudesumiImage = LoadImage("resources/fudesumi.png");
 
@@ -60,21 +61,25 @@
     UnloadImage(imageBlue);
     UnloadImage(backgroundImage);
 
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-
-    Rectangle fudesumiRec = {0, 0, fudesumiImage.width, fudesumiImage.height};
+    Rectangle fudesumiRec = {0, 0, (float)fudesumiImage.width, (float)fudesumiImage.height};
 
     Rectangle fudesumiPos = {50, 10, fudesumiImage.width*0.8f, fudesumiImage.height*0.8f};
-    Rectangle redPos = { 410, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 };
-    Rectangle greenPos = { 600, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 };
-    Rectangle bluePos = { 410, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 };
-    Rectangle alphaPos = { 600, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 };
+    Rectangle redPos = { 410, 10, fudesumiPos.width/2.0f, fudesumiPos.height/2.0f };
+    Rectangle greenPos = { 600, 10, fudesumiPos.width/2.0f, fudesumiPos.height/2.0f };
+    Rectangle bluePos = { 410, 230, fudesumiPos.width/2.0f, fudesumiPos.height/2.0f };
+    Rectangle alphaPos = { 600, 230, fudesumiPos.width/2.0f, fudesumiPos.height/2.0f };
 
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
 
     // Main game loop
     while (!WindowShouldClose())    // Detect window close button or ESC key
     {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Nothing to update...
+        //----------------------------------------------------------------------------------
+
         // Draw
         //----------------------------------------------------------------------------------
         BeginDrawing();
@@ -99,6 +104,7 @@
     UnloadTexture(textureGreen);
     UnloadTexture(textureBlue);
     UnloadTexture(textureAlpha);
+
     CloseWindow();        // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
 
diff --git a/raylib/examples/textures/textures_image_drawing.c b/raylib/examples/textures/textures_image_drawing.c
--- a/raylib/examples/textures/textures_image_drawing.c
+++ b/raylib/examples/textures/textures_image_drawing.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Image loading and drawing on it
+*   raylib [textures] example - image drawing
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 1.4, last time updated with raylib 1.4
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_image_generation.c b/raylib/examples/textures/textures_image_generation.c
--- a/raylib/examples/textures/textures_image_generation.c
+++ b/raylib/examples/textures/textures_image_generation.c
@@ -1,13 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Procedural images generation
+*   raylib [textures] example - image generation
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.8, last time updated with raylib 1.8
 *
+*   Example contributed by Wilhem Barbier (@nounoursheureux) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2O17-2024 Wilhem Barbier (@nounoursheureux) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Wilhem Barbier (@nounoursheureux) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -25,7 +29,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - procedural images generation");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image generation");
 
     Image verticalGradient = GenImageGradientLinear(screenWidth, screenHeight, 0, RED, BLUE);
     Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, RED, BLUE);
@@ -88,7 +92,7 @@
             DrawRectangleLines(30, 400, 325, 30, Fade(WHITE, 0.5f));
             DrawText("MOUSE LEFT BUTTON to CYCLE PROCEDURAL TEXTURES", 40, 410, 10, WHITE);
 
-            switch(currentTexture)
+            switch (currentTexture)
             {
                 case 0: DrawText("VERTICAL GRADIENT", 560, 10, 20, RAYWHITE); break;
                 case 1: DrawText("HORIZONTAL GRADIENT", 540, 10, 20, RAYWHITE); break;
diff --git a/raylib/examples/textures/textures_image_kernel.c b/raylib/examples/textures/textures_image_kernel.c
--- a/raylib/examples/textures/textures_image_kernel.c
+++ b/raylib/examples/textures/textures_image_kernel.c
@@ -1,34 +1,32 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Image loading and texture creation
+*   raylib [textures] example - image kernel
 *
+*   Example complexity rating: [★★★★] 4/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
+*   Example contributed by Karim Salem (@kimo-s) and reviewed by Ramon Santamaria (@raysan5)
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 1.3
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Karim Salem (@kimo-s)
+*   Copyright (c) 2015-2025 Karim Salem (@kimo-s)
 *
 ********************************************************************************************/
 
 #include "raylib.h"
 
 //------------------------------------------------------------------------------------
-// Program main entry point
+// Module Functions Declaration
 //------------------------------------------------------------------------------------
-void NormalizeKernel(float *kernel, int size)
-{
-    float sum = 0.0f;
-    for (int i = 0; i < size; i++) sum += kernel[i]; 
-
-    if (sum != 0.0f)
-    {
-        for (int i = 0; i < size; i++) kernel[i] /= sum; 
-    }
-}
+static void NormalizeKernel(float *kernel, int size);
 
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
 int main(void)
 {
     // Initialization
@@ -36,24 +34,27 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image convolution");
-        
-    Image image = LoadImage("resources/cat.png");     // Loaded in CPU memory (RAM)
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image kernel");
 
-    float gaussiankernel[] = { 
+    Image image = LoadImage("resources/cat.png"); // Loaded in CPU memory (RAM)
+
+    float gaussiankernel[] = {
         1.0f, 2.0f, 1.0f,
         2.0f, 4.0f, 2.0f,
-        1.0f, 2.0f, 1.0f };
+        1.0f, 2.0f, 1.0f
+    };
 
     float sobelkernel[] = {
         1.0f, 0.0f, -1.0f,
         2.0f, 0.0f, -2.0f,
-        1.0f, 0.0f, -1.0f };
+        1.0f, 0.0f, -1.0f
+    };
 
     float sharpenkernel[] = {
         0.0f, -1.0f, 0.0f,
        -1.0f, 5.0f, -1.0f,
-        0.0f, -1.0f, 0.0f };
+        0.0f, -1.0f, 0.0f
+    };
 
     NormalizeKernel(gaussiankernel, 9);
     NormalizeKernel(sharpenkernel, 9);
@@ -61,12 +62,12 @@
 
     Image catSharpend = ImageCopy(image);
     ImageKernelConvolution(&catSharpend, sharpenkernel, 9);
- 
+
     Image catSobel = ImageCopy(image);
     ImageKernelConvolution(&catSobel, sobelkernel, 9);
 
     Image catGaussian = ImageCopy(image);
-    
+
     for (int i = 0; i < 6; i++)
     {
         ImageKernelConvolution(&catGaussian, gaussiankernel, 9);
@@ -76,14 +77,14 @@
     ImageCrop(&catGaussian, (Rectangle){ 0, 0, (float)200, (float)450 });
     ImageCrop(&catSobel, (Rectangle){ 0, 0, (float)200, (float)450 });
     ImageCrop(&catSharpend, (Rectangle){ 0, 0, (float)200, (float)450 });
-    
+
     // Images converted to texture, GPU memory (VRAM)
     Texture2D texture = LoadTextureFromImage(image);
     Texture2D catSharpendTexture = LoadTextureFromImage(catSharpend);
     Texture2D catSobelTexture = LoadTextureFromImage(catSobel);
     Texture2D catGaussianTexture = LoadTextureFromImage(catGaussian);
-    
-    // Once images have been converted to texture and uploaded to VRAM, 
+
+    // Once images have been converted to texture and uploaded to VRAM,
     // they can be unloaded from RAM
     UnloadImage(image);
     UnloadImage(catGaussian);
@@ -127,4 +128,18 @@
     //--------------------------------------------------------------------------------------
 
     return 0;
+}
+
+//------------------------------------------------------------------------------------
+// Module Functions Definition
+//------------------------------------------------------------------------------------
+static void NormalizeKernel(float *kernel, int size)
+{
+    float sum = 0.0f;
+    for (int i = 0; i < size; i++) sum += kernel[i];
+
+    if (sum != 0.0f)
+    {
+        for (int i = 0; i < size; i++) kernel[i] /= sum;
+    }
 }
diff --git a/raylib/examples/textures/textures_image_loading.c b/raylib/examples/textures/textures_image_loading.c
--- a/raylib/examples/textures/textures_image_loading.c
+++ b/raylib/examples/textures/textures_image_loading.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Image loading and texture creation
+*   raylib [textures] example - image loading
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 1.3, last time updated with raylib 1.3
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_image_processing.c b/raylib/examples/textures/textures_image_processing.c
--- a/raylib/examples/textures/textures_image_processing.c
+++ b/raylib/examples/textures/textures_image_processing.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Image processing
+*   raylib [textures] example - image processing
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 1.4, last time updated with raylib 3.5
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2016-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_image_rotate.c b/raylib/examples/textures/textures_image_rotate.c
--- a/raylib/examples/textures/textures_image_rotate.c
+++ b/raylib/examples/textures/textures_image_rotate.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Image Rotation
+*   raylib [textures] example - image rotate
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -25,7 +27,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture rotation");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image rotate");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
     Image image45 = LoadImage("resources/raylib_logo.png");
diff --git a/raylib/examples/textures/textures_image_text.c b/raylib/examples/textures/textures_image_text.c
--- a/raylib/examples/textures/textures_image_text.c
+++ b/raylib/examples/textures/textures_image_text.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [texture] example - Image text drawing using TTF generated font
+*   raylib [textures] example - image text
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 1.8, last time updated with raylib 4.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [texture] example - image text drawing");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - image text");
 
     Image parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM)
 
diff --git a/raylib/examples/textures/textures_logo_raylib.c b/raylib/examples/textures/textures_logo_raylib.c
--- a/raylib/examples/textures/textures_logo_raylib.c
+++ b/raylib/examples/textures/textures_logo_raylib.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Texture loading and drawing
+*   raylib [textures] example - logo raylib
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.0, last time updated with raylib 1.0
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - logo raylib");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
     Texture2D texture = LoadTexture("resources/raylib_logo.png");        // Texture loading
diff --git a/raylib/examples/textures/textures_mouse_painting.c b/raylib/examples/textures/textures_mouse_painting.c
--- a/raylib/examples/textures/textures_mouse_painting.c
+++ b/raylib/examples/textures/textures_mouse_painting.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Mouse painting
+*   raylib [textures] example - mouse painting
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 3.0, last time updated with raylib 3.0
 *
 *   Example contributed by Chris Dill (@MysteriousSpace) and reviewed by Ramon Santamaria (@raysan5)
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Chris Dill (@MysteriousSpace) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -177,7 +179,7 @@
         ClearBackground(RAYWHITE);
 
         // NOTE: Render texture must be y-flipped due to default OpenGL coordinates (left-bottom)
-        DrawTextureRec(target.texture, (Rectangle) { 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2) { 0, 0 }, WHITE);
+        DrawTextureRec(target.texture, (Rectangle){ 0, 0, (float)target.texture.width, (float)-target.texture.height }, (Vector2) { 0, 0 }, WHITE);
 
         // Draw drawing circle for reference
         if (mousePos.y > 50)
@@ -208,7 +210,7 @@
         {
             DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f));
             DrawRectangle(0, 150, GetScreenWidth(), 80, BLACK);
-            DrawText("IMAGE SAVED:  my_amazing_texture_painting.png", 150, 180, 20, RAYWHITE);
+            DrawText("IMAGE SAVED!", 150, 180, 20, RAYWHITE);
         }
 
         EndDrawing();
diff --git a/raylib/examples/textures/textures_npatch_drawing.c b/raylib/examples/textures/textures_npatch_drawing.c
--- a/raylib/examples/textures/textures_npatch_drawing.c
+++ b/raylib/examples/textures/textures_npatch_drawing.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - N-patch drawing
+*   raylib [textures] example - npatch drawing
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 2.0, last time updated with raylib 2.5
@@ -11,7 +13,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2018-2024 Jorge A. Gomes (@overdev) and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2025 Jorge A. Gomes (@overdev) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -27,7 +29,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - N-patch drawing");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - npatch drawing");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
     Texture2D nPatchTexture = LoadTexture("resources/ninepatch_button.png");
diff --git a/raylib/examples/textures/textures_particles_blending.c b/raylib/examples/textures/textures_particles_blending.c
--- a/raylib/examples/textures/textures_particles_blending.c
+++ b/raylib/examples/textures/textures_particles_blending.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib example - particles blending
+*   raylib [textures] example - particles blending
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   Example originally created with raylib 1.7, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -15,7 +17,10 @@
 
 #define MAX_PARTICLES 200
 
-// Particle structure with basic data
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Particle structure
 typedef struct {
     Vector2 position;
     Color color;
@@ -69,7 +74,7 @@
         // Activate one particle every frame and Update active particles
         // NOTE: Particles initial position should be mouse position when activated
         // NOTE: Particles fall down with gravity and rotation... and disappear after 2 seconds (alpha = 0)
-        // NOTE: When a particle disappears, active = false and it can be reused.
+        // NOTE: When a particle disappears, active = false and it can be reused
         for (int i = 0; i < MAX_PARTICLES; i++)
         {
             if (!mouseTail[i].active)
diff --git a/raylib/examples/textures/textures_polygon.c b/raylib/examples/textures/textures_polygon.c
deleted file mode 100644
--- a/raylib/examples/textures/textures_polygon.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [shapes] example - Draw Textured Polygon
-*
-*   Example originally created with raylib 3.7, last time updated with raylib 3.7
-*
-*   Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5)
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2021-2024 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#include "rlgl.h"           // Required for: Vertex definition
-#include "raymath.h"
-
-#define MAX_POINTS  11      // 10 points and back to the start
-
-// Draw textured polygon, defined by vertex and texture coordinates
-void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint);
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-    
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - textured polygon");
-
-    // Define texture coordinates to map our texture to poly
-    Vector2 texcoords[MAX_POINTS] = {
-        (Vector2){ 0.75f, 0.0f },
-        (Vector2){ 0.25f, 0.0f },
-        (Vector2){ 0.0f, 0.5f },
-        (Vector2){ 0.0f, 0.75f },
-        (Vector2){ 0.25f, 1.0f},
-        (Vector2){ 0.375f, 0.875f},
-        (Vector2){ 0.625f, 0.875f},
-        (Vector2){ 0.75f, 1.0f},
-        (Vector2){ 1.0f, 0.75f},
-        (Vector2){ 1.0f, 0.5f},
-        (Vector2){ 0.75f, 0.0f}  // Close the poly
-    };
-
-    // Define the base poly vertices from the UV's
-    // NOTE: They can be specified in any other way
-    Vector2 points[MAX_POINTS] = { 0 };
-    for (int i = 0; i < MAX_POINTS; i++)
-    {
-        points[i].x = (texcoords[i].x - 0.5f)*256.0f;
-        points[i].y = (texcoords[i].y - 0.5f)*256.0f;
-    }
-    
-    // Define the vertices drawing position
-    // NOTE: Initially same as points but updated every frame
-    Vector2 positions[MAX_POINTS] = { 0 };
-    for (int i = 0; i < MAX_POINTS; i++) positions[i] = points[i];
-
-    // Load texture to be mapped to poly
-    Texture texture = LoadTexture("resources/cat.png");
-
-    float angle = 0.0f;             // Rotation angle (in degrees)
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        // Update points rotation with an angle transform
-        // NOTE: Base points position are not modified
-        angle++;
-        for (int i = 0; i < MAX_POINTS; i++) positions[i] = Vector2Rotate(points[i], angle*DEG2RAD);
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawText("textured polygon", 20, 20, 20, DARKGRAY);
-
-            DrawTexturePoly(texture, (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f },
-                            positions, texcoords, MAX_POINTS, WHITE);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(texture); // Unload texture
-
-    CloseWindow();          // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
-
-// Draw textured polygon, defined by vertex and texture coordinates
-// NOTE: Polygon center must have straight line path to all points
-// without crossing perimeter, points must be in anticlockwise order
-void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint)
-{
-    rlSetTexture(texture.id);
-
-    // Texturing is only supported on RL_QUADS
-    rlBegin(RL_QUADS);
-
-        rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-
-        for (int i = 0; i < pointCount - 1; i++)
-        {
-            rlTexCoord2f(0.5f, 0.5f);
-            rlVertex2f(center.x, center.y);
-
-            rlTexCoord2f(texcoords[i].x, texcoords[i].y);
-            rlVertex2f(points[i].x + center.x, points[i].y + center.y);
-
-            rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
-            rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
-
-            rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
-            rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
-        }
-    rlEnd();
-
-    rlSetTexture(0);
-}
diff --git a/raylib/examples/textures/textures_polygon_drawing.c b/raylib/examples/textures/textures_polygon_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_polygon_drawing.c
@@ -0,0 +1,138 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - polygon drawing
+*
+*   Example complexity rating: [★☆☆☆] 1/4
+*
+*   Example originally created with raylib 3.7, last time updated with raylib 3.7
+*
+*   Example contributed by Chris Camacho (@chriscamacho) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2021-2025 Chris Camacho (@chriscamacho) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "rlgl.h"           // Required for: Vertex definition
+#include "raymath.h"
+
+#define MAX_POINTS  11      // 10 points and back to the start
+
+// Draw textured polygon, defined by vertex and texture coordinates
+void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - polygon drawing");
+
+    // Define texture coordinates to map our texture to poly
+    Vector2 texcoords[MAX_POINTS] = {
+        (Vector2){ 0.75f, 0.0f },
+        (Vector2){ 0.25f, 0.0f },
+        (Vector2){ 0.0f, 0.5f },
+        (Vector2){ 0.0f, 0.75f },
+        (Vector2){ 0.25f, 1.0f},
+        (Vector2){ 0.375f, 0.875f},
+        (Vector2){ 0.625f, 0.875f},
+        (Vector2){ 0.75f, 1.0f},
+        (Vector2){ 1.0f, 0.75f},
+        (Vector2){ 1.0f, 0.5f},
+        (Vector2){ 0.75f, 0.0f}  // Close the poly
+    };
+
+    // Define the base poly vertices from the UV's
+    // NOTE: They can be specified in any other way
+    Vector2 points[MAX_POINTS] = { 0 };
+    for (int i = 0; i < MAX_POINTS; i++)
+    {
+        points[i].x = (texcoords[i].x - 0.5f)*256.0f;
+        points[i].y = (texcoords[i].y - 0.5f)*256.0f;
+    }
+
+    // Define the vertices drawing position
+    // NOTE: Initially same as points but updated every frame
+    Vector2 positions[MAX_POINTS] = { 0 };
+    for (int i = 0; i < MAX_POINTS; i++) positions[i] = points[i];
+
+    // Load texture to be mapped to poly
+    Texture texture = LoadTexture("resources/cat.png");
+
+    float angle = 0.0f;             // Rotation angle (in degrees)
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Update points rotation with an angle transform
+        // NOTE: Base points position are not modified
+        angle++;
+        for (int i = 0; i < MAX_POINTS; i++) positions[i] = Vector2Rotate(points[i], angle*DEG2RAD);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawText("textured polygon", 20, 20, 20, DARKGRAY);
+
+            DrawTexturePoly(texture, (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f },
+                            positions, texcoords, MAX_POINTS, WHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texture); // Unload texture
+
+    CloseWindow();          // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Draw textured polygon, defined by vertex and texture coordinates
+// NOTE: Polygon center must have straight line path to all points
+// without crossing perimeter, points must be in anticlockwise order
+void DrawTexturePoly(Texture2D texture, Vector2 center, Vector2 *points, Vector2 *texcoords, int pointCount, Color tint)
+{
+    rlSetTexture(texture.id);
+    rlBegin(RL_TRIANGLES);
+
+
+        rlColor4ub(tint.r, tint.g, tint.b, tint.a);
+
+        for (int i = 0; i < pointCount - 1; i++)
+        {
+            rlTexCoord2f(0.5f, 0.5f);
+            rlVertex2f(center.x, center.y);
+
+            rlTexCoord2f(texcoords[i].x, texcoords[i].y);
+            rlVertex2f(points[i].x + center.x, points[i].y + center.y);
+
+            rlTexCoord2f(texcoords[i + 1].x, texcoords[i + 1].y);
+            rlVertex2f(points[i + 1].x + center.x, points[i + 1].y + center.y);
+        }
+    rlEnd();
+
+    rlSetTexture(0);
+}
diff --git a/raylib/examples/textures/textures_raw_data.c b/raylib/examples/textures/textures_raw_data.c
--- a/raylib/examples/textures/textures_raw_data.c
+++ b/raylib/examples/textures/textures_raw_data.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Load textures from raw data
+*   raylib [textures] example - raw data
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 1.3, last time updated with raylib 3.5
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -27,7 +29,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture from raw data");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - raw data");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
 
diff --git a/raylib/examples/textures/textures_screen_buffer.c b/raylib/examples/textures/textures_screen_buffer.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_screen_buffer.c
@@ -0,0 +1,150 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - screen buffer
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.5, last time updated with raylib 5.5
+*
+*   Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2025 Agnis Aldiņš (@nezvers)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdlib.h>     // Required for: calloc(), free()
+
+#define MAX_COLORS      256
+#define SCALE_FACTOR      2
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - screen buffer");
+
+    int imageWidth = screenWidth/SCALE_FACTOR;
+    int imageHeight = screenHeight/SCALE_FACTOR;
+    int flameWidth = screenWidth/SCALE_FACTOR;
+
+    Color palette[MAX_COLORS] = { 0 };
+    unsigned char *indexBuffer = RL_CALLOC(imageWidth*imageWidth, sizeof(unsigned char));
+    unsigned char *flameRootBuffer = RL_CALLOC(flameWidth, sizeof(unsigned char));
+
+    Image screenImage = GenImageColor(imageWidth, imageHeight, BLACK);
+    Texture screenTexture = LoadTextureFromImage(screenImage);
+
+    // Generate flame color palette
+    for (int i = 0; i < MAX_COLORS; i++)
+    {
+        float t = (float)i/(float)(MAX_COLORS - 1);
+        float hue = t*t;
+        float saturation = t;
+        float value = t;
+        palette[i] = ColorFromHSV(250.0f + 150.0f*hue, saturation, value);
+    }
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Grow flameRoot
+        for (int x = 2; x < flameWidth; x++)
+        {
+            unsigned char flame = flameRootBuffer[x];
+            if (flame == 255) continue;
+            flame += GetRandomValue(0, 2);
+            if (flame > 255) flame = 255;
+            flameRootBuffer[x] = flame;
+        }
+
+        // Transfer flameRoot to indexBuffer
+        for (int x = 0; x < flameWidth; x++)
+        {
+            int i = x + (imageHeight - 1)*imageWidth;
+            indexBuffer[i] = flameRootBuffer[x];
+        }
+
+        // Clear top row, because it can't move any higher
+        for (int x = 0; x < imageWidth; x++)
+        {
+            if (indexBuffer[x] == 0) continue;
+            indexBuffer[x] = 0;
+        }
+
+        // Skip top row, it is already cleared
+        for (int y = 1; y < imageHeight; y++)
+        {
+            for (int x = 0; x < imageWidth; x++)
+            {
+                unsigned int i = x + y*imageWidth;
+                unsigned char colorIndex = indexBuffer[i];
+                if (colorIndex == 0) continue;
+
+                // Move pixel a row above
+                indexBuffer[i] = 0;
+                int moveX = GetRandomValue(0, 2) - 1;
+                int newX = x + moveX;
+                if (newX < 0 || newX >= imageWidth) continue;
+
+                unsigned int iabove = i - imageWidth + moveX;
+                int decay = GetRandomValue(0, 3);
+                colorIndex -= (decay < colorIndex)? decay : colorIndex;
+                indexBuffer[iabove] = colorIndex;
+            }
+        }
+
+        // Update screenImage with palette colors
+        for (int y = 1; y < imageHeight; y++)
+        {
+            for (int x = 0; x < imageWidth; x++)
+            {
+                unsigned int i = x + y*imageWidth;
+                unsigned char colorIndex = indexBuffer[i];
+                Color col = palette[colorIndex];
+                ImageDrawPixel(&screenImage, x, y, col);
+            }
+        }
+
+        UpdateTexture(screenTexture, screenImage.data);
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            
+            ClearBackground(RAYWHITE);
+
+            DrawTextureEx(screenTexture, (Vector2){ 0, 0 }, 0.0f, 2.0f, WHITE);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    RL_FREE(indexBuffer);
+    RL_FREE(flameRootBuffer);
+    UnloadTexture(screenTexture);
+    UnloadImage(screenImage);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/textures/textures_sprite_anim.c b/raylib/examples/textures/textures_sprite_anim.c
deleted file mode 100644
--- a/raylib/examples/textures/textures_sprite_anim.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*******************************************************************************************
-*
-*   raylib [textures] example - Sprite animation
-*
-*   Example originally created with raylib 1.3, last time updated with raylib 1.3
-*
-*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-*   BSD-like license that allows static linking with closed source software
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-********************************************************************************************/
-
-#include "raylib.h"
-
-#define MAX_FRAME_SPEED     15
-#define MIN_FRAME_SPEED      1
-
-//------------------------------------------------------------------------------------
-// Program main entry point
-//------------------------------------------------------------------------------------
-int main(void)
-{
-    // Initialization
-    //--------------------------------------------------------------------------------------
-    const int screenWidth = 800;
-    const int screenHeight = 450;
-
-    InitWindow(screenWidth, screenHeight, "raylib [texture] example - sprite anim");
-
-    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
-    Texture2D scarfy = LoadTexture("resources/scarfy.png");        // Texture loading
-
-    Vector2 position = { 350.0f, 280.0f };
-    Rectangle frameRec = { 0.0f, 0.0f, (float)scarfy.width/6, (float)scarfy.height };
-    int currentFrame = 0;
-
-    int framesCounter = 0;
-    int framesSpeed = 8;            // Number of spritesheet frames shown by second
-
-    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
-    //--------------------------------------------------------------------------------------
-
-    // Main game loop
-    while (!WindowShouldClose())    // Detect window close button or ESC key
-    {
-        // Update
-        //----------------------------------------------------------------------------------
-        framesCounter++;
-
-        if (framesCounter >= (60/framesSpeed))
-        {
-            framesCounter = 0;
-            currentFrame++;
-
-            if (currentFrame > 5) currentFrame = 0;
-
-            frameRec.x = (float)currentFrame*(float)scarfy.width/6;
-        }
-
-        // Control frames speed
-        if (IsKeyPressed(KEY_RIGHT)) framesSpeed++;
-        else if (IsKeyPressed(KEY_LEFT)) framesSpeed--;
-
-        if (framesSpeed > MAX_FRAME_SPEED) framesSpeed = MAX_FRAME_SPEED;
-        else if (framesSpeed < MIN_FRAME_SPEED) framesSpeed = MIN_FRAME_SPEED;
-        //----------------------------------------------------------------------------------
-
-        // Draw
-        //----------------------------------------------------------------------------------
-        BeginDrawing();
-
-            ClearBackground(RAYWHITE);
-
-            DrawTexture(scarfy, 15, 40, WHITE);
-            DrawRectangleLines(15, 40, scarfy.width, scarfy.height, LIME);
-            DrawRectangleLines(15 + (int)frameRec.x, 40 + (int)frameRec.y, (int)frameRec.width, (int)frameRec.height, RED);
-
-            DrawText("FRAME SPEED: ", 165, 210, 10, DARKGRAY);
-            DrawText(TextFormat("%02i FPS", framesSpeed), 575, 210, 10, DARKGRAY);
-            DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, DARKGRAY);
-
-            for (int i = 0; i < MAX_FRAME_SPEED; i++)
-            {
-                if (i < framesSpeed) DrawRectangle(250 + 21*i, 205, 20, 20, RED);
-                DrawRectangleLines(250 + 21*i, 205, 20, 20, MAROON);
-            }
-
-            DrawTextureRec(scarfy, frameRec, position, WHITE);  // Draw part of the texture
-
-            DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY);
-
-        EndDrawing();
-        //----------------------------------------------------------------------------------
-    }
-
-    // De-Initialization
-    //--------------------------------------------------------------------------------------
-    UnloadTexture(scarfy);       // Texture unloading
-
-    CloseWindow();                // Close window and OpenGL context
-    //--------------------------------------------------------------------------------------
-
-    return 0;
-}
diff --git a/raylib/examples/textures/textures_sprite_animation.c b/raylib/examples/textures/textures_sprite_animation.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_sprite_animation.c
@@ -0,0 +1,107 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - sprite animation
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 1.3, last time updated with raylib 1.3
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2014-2025 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define MAX_FRAME_SPEED     15
+#define MIN_FRAME_SPEED      1
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite animation");
+
+    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
+    Texture2D scarfy = LoadTexture("resources/scarfy.png");        // Texture loading
+
+    Vector2 position = { 350.0f, 280.0f };
+    Rectangle frameRec = { 0.0f, 0.0f, (float)scarfy.width/6, (float)scarfy.height };
+    int currentFrame = 0;
+
+    int framesCounter = 0;
+    int framesSpeed = 8;            // Number of spritesheet frames shown by second
+
+    SetTargetFPS(60);               // Set our game to run at 60 frames-per-second
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        framesCounter++;
+
+        if (framesCounter >= (60/framesSpeed))
+        {
+            framesCounter = 0;
+            currentFrame++;
+
+            if (currentFrame > 5) currentFrame = 0;
+
+            frameRec.x = (float)currentFrame*(float)scarfy.width/6;
+        }
+
+        // Control frames speed
+        if (IsKeyPressed(KEY_RIGHT)) framesSpeed++;
+        else if (IsKeyPressed(KEY_LEFT)) framesSpeed--;
+
+        if (framesSpeed > MAX_FRAME_SPEED) framesSpeed = MAX_FRAME_SPEED;
+        else if (framesSpeed < MIN_FRAME_SPEED) framesSpeed = MIN_FRAME_SPEED;
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            DrawTexture(scarfy, 15, 40, WHITE);
+            DrawRectangleLines(15, 40, scarfy.width, scarfy.height, LIME);
+            DrawRectangleLines(15 + (int)frameRec.x, 40 + (int)frameRec.y, (int)frameRec.width, (int)frameRec.height, RED);
+
+            DrawText("FRAME SPEED: ", 165, 210, 10, DARKGRAY);
+            DrawText(TextFormat("%02i FPS", framesSpeed), 575, 210, 10, DARKGRAY);
+            DrawText("PRESS RIGHT/LEFT KEYS to CHANGE SPEED!", 290, 240, 10, DARKGRAY);
+
+            for (int i = 0; i < MAX_FRAME_SPEED; i++)
+            {
+                if (i < framesSpeed) DrawRectangle(250 + 21*i, 205, 20, 20, RED);
+                DrawRectangleLines(250 + 21*i, 205, 20, 20, MAROON);
+            }
+
+            DrawTextureRec(scarfy, frameRec, position, WHITE);  // Draw part of the texture
+
+            DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(scarfy);       // Texture unloading
+
+    CloseWindow();                // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/textures/textures_sprite_button.c b/raylib/examples/textures/textures_sprite_button.c
--- a/raylib/examples/textures/textures_sprite_button.c
+++ b/raylib/examples/textures/textures_sprite_button.c
@@ -2,12 +2,14 @@
 *
 *   raylib [textures] example - sprite button
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 2.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_sprite_explosion.c b/raylib/examples/textures/textures_sprite_explosion.c
--- a/raylib/examples/textures/textures_sprite_explosion.c
+++ b/raylib/examples/textures/textures_sprite_explosion.c
@@ -2,12 +2,14 @@
 *
 *   raylib [textures] example - sprite explosion
 *
+*   Example complexity rating: [★★☆☆] 2/4
+*
 *   Example originally created with raylib 2.5, last time updated with raylib 3.5
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2019-2024 Anata and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2019-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
diff --git a/raylib/examples/textures/textures_sprite_stacking.c b/raylib/examples/textures/textures_sprite_stacking.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_sprite_stacking.c
@@ -0,0 +1,105 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - sprite stacking
+*
+*   Example complexity rating: [★★☆☆] 2/4
+*
+*   Example originally created with raylib 5.6-dev, last time updated with raylib 6.0
+*
+*   Example contributed by Robin (@RobinsAviary) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Redbooth model (c) 2017-2025 @kluchek under https://creativecommons.org/licenses/by/4.0/ https://github.com/kluchek/vox-models/
+*   Copyright (c) 2025 Robin (@RobinsAviary)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#include "raymath.h"    // Required for: Clamp()
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - sprite stacking");
+
+    Texture2D booth = LoadTexture("resources/booth.png");
+
+    float stackScale = 3.0f; // Overall scale of the stacked sprite
+    float stackSpacing = 2.0f; // Vertical spacing between each layer
+    unsigned int stackCount = 122; // Number of layers, used for calculating the size of a single slice
+    float rotationSpeed = 30.0f; // Stacked sprites rotation speed
+    float rotation = 0.0f; // Current rotation of the stacked sprite
+    const float speedChange = 0.25f; // Amount speed will change by when the user presses A/D
+
+    SetTargetFPS(60);
+    //--------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Use mouse wheel to affect stack separation
+        stackSpacing += GetMouseWheelMove()*0.1f;
+        stackSpacing = Clamp(stackSpacing, 0.0f, 5.0f);
+
+        // Add a positive/negative offset to spin right/left at different speeds
+        if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) rotationSpeed -= speedChange;
+        if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) rotationSpeed += speedChange;
+
+        rotation += rotationSpeed*GetFrameTime();
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+
+            ClearBackground(RAYWHITE);
+
+            // Get the size of a single slice
+            float frameWidth = (float)booth.width;
+            float frameHeight = (float)booth.height/(float)stackCount;
+
+            // Get the scaled resolution to draw at
+            float scaledWidth = frameWidth*stackScale;
+            float scaledHeight = frameHeight*stackScale;
+
+            // Draw the stacked sprite, rotated to the correct angle, with an vertical offset applied based on its y location
+            for (int i = stackCount - 1; i >= 0; i--)
+            {
+                // Center vertically
+                Rectangle source = { 0.0f, (float)i*frameHeight, frameWidth, frameHeight };
+                Rectangle dest = { screenWidth/2.0f, (screenHeight/2.0f) + (i*stackSpacing) - (stackSpacing*stackCount/2.0f), scaledWidth, scaledHeight };
+                Vector2 origin = { scaledWidth/2.0f, scaledHeight/2.0f };
+
+                DrawTexturePro(booth, source, dest, origin, rotation, WHITE);
+            }
+
+            DrawText("A/D to spin\nmouse wheel to change separation (aka 'angle')", 10, 10, 20, DARKGRAY);
+            DrawText(TextFormat("current spacing: %.01f", stackSpacing), 10, 50, 20, DARKGRAY);
+            DrawText(TextFormat("current speed: %.02f", rotationSpeed), 10, 70, 20, DARKGRAY);
+            DrawText("redbooth model (c) kluchek under cc 4.0", 10, 420, 20, DARKGRAY);
+
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(booth);
+
+    CloseWindow();        // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
diff --git a/raylib/examples/textures/textures_srcrec_dstrec.c b/raylib/examples/textures/textures_srcrec_dstrec.c
--- a/raylib/examples/textures/textures_srcrec_dstrec.c
+++ b/raylib/examples/textures/textures_srcrec_dstrec.c
@@ -1,13 +1,15 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Texture source and destination rectangles
+*   raylib [textures] example - srcrec dstrec
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 1.3, last time updated with raylib 1.3
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -23,7 +25,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] examples - texture source and destination rectangles");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - srcrec dstrec");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
 
diff --git a/raylib/examples/textures/textures_textured_curve.c b/raylib/examples/textures/textures_textured_curve.c
--- a/raylib/examples/textures/textures_textured_curve.c
+++ b/raylib/examples/textures/textures_textured_curve.c
@@ -1,15 +1,17 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Draw a texture along a segmented curve
+*   raylib [textures] example - textured curve
 *
+*   Example complexity rating: [★★★☆] 3/4
+*
 *   Example originally created with raylib 4.5, last time updated with raylib 4.5
 *
-*   Example contributed by Jeffery Myers and reviewed by Ramon Santamaria (@raysan5)
+*   Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5)
 *
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2022-2024 Jeffery Myers and Ramon Santamaria (@raysan5)
+*   Copyright (c) 2022-2025 Jeffery Myers (@JeffM2501) and Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -47,7 +49,7 @@
 //------------------------------------------------------------------------------------
 // Program main entry point
 //------------------------------------------------------------------------------------
-int main()
+int main(void)
 {
     // Initialization
     //--------------------------------------------------------------------------------------
@@ -55,7 +57,7 @@
     const int screenHeight = 450;
 
     SetConfigFlags(FLAG_VSYNC_HINT | FLAG_MSAA_4X_HINT);
-    InitWindow(screenWidth, screenHeight, "raylib [textures] examples - textured curve");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - textured curve");
 
     // Load the road texture
     texRoad = LoadTexture("resources/road.png");
@@ -110,7 +112,7 @@
             ClearBackground(RAYWHITE);
 
             DrawTexturedCurve();    // Draw a textured Spline Cubic Bezier
-            
+
             // Draw spline for reference
             if (showCurve) DrawSplineSegmentBezierCubic(curveStartPosition, curveEndPosition, curveStartPositionTangent, curveEndPositionTangent, 2, BLUE);
 
@@ -118,7 +120,7 @@
             DrawLineV(curveStartPosition, curveStartPositionTangent, SKYBLUE);
             DrawLineV(curveStartPositionTangent, curveEndPositionTangent, Fade(LIGHTGRAY, 0.4f));
             DrawLineV(curveEndPosition, curveEndPositionTangent, PURPLE);
-            
+
             if (CheckCollisionPointCircle(mouse, curveStartPosition, 6)) DrawCircleV(curveStartPosition, 7, YELLOW);
             DrawCircleV(curveStartPosition, 5, RED);
 
@@ -135,7 +137,7 @@
             DrawText("Drag points to move curve, press SPACE to show/hide base curve", 10, 10, 10, DARKGRAY);
             DrawText(TextFormat("Curve width: %2.0f (Use + and - to adjust)", curveWidth), 10, 30, 10, DARKGRAY);
             DrawText(TextFormat("Curve segments: %d (Use LEFT and RIGHT to adjust)", curveSegments), 10, 50, 10, DARKGRAY);
-            
+
         EndDrawing();
         //----------------------------------------------------------------------------------
     }
@@ -143,7 +145,7 @@
     // De-Initialization
     //--------------------------------------------------------------------------------------
     UnloadTexture(texRoad);
-    
+
     CloseWindow();              // Close window and OpenGL context
     //--------------------------------------------------------------------------------------
 
@@ -153,7 +155,6 @@
 //----------------------------------------------------------------------------------
 // Module Functions Definition
 //----------------------------------------------------------------------------------
-
 // Draw textured curve using Spline Cubic Bezier
 static void DrawTexturedCurve(void)
 {
@@ -189,7 +190,7 @@
         Vector2 normal = Vector2Normalize((Vector2){ -delta.y, delta.x });
 
         // The v texture coordinate of the segment (add up the length of all the segments so far)
-        float v = previousV + Vector2Length(delta);
+        float v = previousV + Vector2Length(delta) / (float)(texRoad.height * 2);
 
         // Make sure the start point has a normal
         if (!tangentSet)
diff --git a/raylib/examples/textures/textures_tiled_drawing.c b/raylib/examples/textures/textures_tiled_drawing.c
new file mode 100644
--- /dev/null
+++ b/raylib/examples/textures/textures_tiled_drawing.c
@@ -0,0 +1,256 @@
+/*******************************************************************************************
+*
+*   raylib [textures] example - tiled drawing
+*
+*   Example complexity rating: [★★★☆] 3/4
+*
+*   Example originally created with raylib 3.0, last time updated with raylib 4.2
+*
+*   Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
+*
+*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software
+*
+*   Copyright (c) 2020-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define SIZEOF(A) (sizeof(A)/sizeof(A[0]))
+#define OPT_WIDTH       220       // Max width for the options container
+#define MARGIN_SIZE       8       // Size for the margins
+#define COLOR_SIZE       16       // Size of the color select buttons
+
+// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest
+void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+    // Initialization
+    //--------------------------------------------------------------------------------------
+    const int screenWidth = 800;
+    const int screenHeight = 450;
+
+    SetConfigFlags(FLAG_WINDOW_RESIZABLE); // Make the window resizable
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - tiled drawing");
+
+    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
+    Texture texPattern = LoadTexture("resources/patterns.png");
+    SetTextureFilter(texPattern, TEXTURE_FILTER_BILINEAR); // Makes the texture smoother when upscaled
+
+    // Coordinates for all patterns inside the texture
+    const Rectangle recPattern[] = {
+        (Rectangle){ 3, 3, 66, 66 },
+        (Rectangle){ 75, 3, 100, 100 },
+        (Rectangle){ 3, 75, 66, 66 },
+        (Rectangle){ 7, 156, 50, 50 },
+        (Rectangle){ 85, 106, 90, 45 },
+        (Rectangle){ 75, 154, 100, 60}
+    };
+
+    // Setup colors
+    const Color colors[] = { BLACK, MAROON, ORANGE, BLUE, PURPLE, BEIGE, LIME, RED, DARKGRAY, SKYBLUE };
+    enum { MAX_COLORS = SIZEOF(colors) };
+    Rectangle colorRec[MAX_COLORS] = { 0 };
+
+    // Calculate rectangle for each color
+    for (int i = 0, x = 0, y = 0; i < MAX_COLORS; i++)
+    {
+        colorRec[i].x = 2.0f + MARGIN_SIZE + x;
+        colorRec[i].y = 22.0f + 256.0f + MARGIN_SIZE + y;
+        colorRec[i].width = COLOR_SIZE*2.0f;
+        colorRec[i].height = (float)COLOR_SIZE;
+
+        if (i == (MAX_COLORS/2 - 1))
+        {
+            x = 0;
+            y += COLOR_SIZE + MARGIN_SIZE;
+        }
+        else x += (COLOR_SIZE*2 + MARGIN_SIZE);
+    }
+
+    int activePattern = 0, activeCol = 0;
+    float scale = 1.0f, rotation = 0.0f;
+
+    SetTargetFPS(60);
+    //---------------------------------------------------------------------------------------
+
+    // Main game loop
+    while (!WindowShouldClose())    // Detect window close button or ESC key
+    {
+        // Update
+        //----------------------------------------------------------------------------------
+        // Handle mouse
+        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
+        {
+            const Vector2 mouse = GetMousePosition();
+
+            // Check which pattern was clicked and set it as the active pattern
+            for (int i = 0; i < SIZEOF(recPattern); i++)
+            {
+                if (CheckCollisionPointRec(mouse, (Rectangle){ 2 + MARGIN_SIZE + recPattern[i].x, 40 + MARGIN_SIZE + recPattern[i].y, recPattern[i].width, recPattern[i].height }))
+                {
+                    activePattern = i;
+                    break;
+                }
+            }
+
+            // Check to see which color was clicked and set it as the active color
+            for (int i = 0; i < MAX_COLORS; i++)
+            {
+                if (CheckCollisionPointRec(mouse, colorRec[i]))
+                {
+                    activeCol = i;
+                    break;
+                }
+            }
+        }
+
+        // Handle keys: change scale
+        if (IsKeyPressed(KEY_UP)) scale += 0.25f;
+        if (IsKeyPressed(KEY_DOWN)) scale -= 0.25f;
+        if (scale > 10.0f) scale = 10.0f;
+        else if ( scale <= 0.0f) scale = 0.25f;
+
+        // Handle keys: change rotation
+        if (IsKeyPressed(KEY_LEFT)) rotation -= 25.0f;
+        if (IsKeyPressed(KEY_RIGHT)) rotation += 25.0f;
+
+        // Handle keys: reset
+        if (IsKeyPressed(KEY_SPACE)) { rotation = 0.0f; scale = 1.0f; }
+        //----------------------------------------------------------------------------------
+
+        // Draw
+        //----------------------------------------------------------------------------------
+        BeginDrawing();
+            ClearBackground(RAYWHITE);
+
+            // Draw the tiled area
+            DrawTextureTiled(texPattern, recPattern[activePattern], (Rectangle){(float)OPT_WIDTH+MARGIN_SIZE, (float)MARGIN_SIZE, GetScreenWidth() - OPT_WIDTH - 2.0f*MARGIN_SIZE, GetScreenHeight() - 2.0f*MARGIN_SIZE},
+                (Vector2){0.0f, 0.0f}, rotation, scale, colors[activeCol]);
+
+            // Draw options
+            DrawRectangle(MARGIN_SIZE, MARGIN_SIZE, OPT_WIDTH - MARGIN_SIZE, GetScreenHeight() - 2*MARGIN_SIZE, ColorAlpha(LIGHTGRAY, 0.5f));
+
+            DrawText("Select Pattern", 2 + MARGIN_SIZE, 30 + MARGIN_SIZE, 10, BLACK);
+            DrawTexture(texPattern, 2 + MARGIN_SIZE, 40 + MARGIN_SIZE, BLACK);
+            DrawRectangle(2 + MARGIN_SIZE + (int)recPattern[activePattern].x, 40 + MARGIN_SIZE + (int)recPattern[activePattern].y, (int)recPattern[activePattern].width, (int)recPattern[activePattern].height, ColorAlpha(DARKBLUE, 0.3f));
+
+            DrawText("Select Color", 2+MARGIN_SIZE, 10+256+MARGIN_SIZE, 10, BLACK);
+            for (int i = 0; i < MAX_COLORS; i++)
+            {
+                DrawRectangleRec(colorRec[i], colors[i]);
+                if (activeCol == i) DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(WHITE, 0.5f));
+            }
+
+            DrawText("Scale (UP/DOWN to change)", 2 + MARGIN_SIZE, 80 + 256 + MARGIN_SIZE, 10, BLACK);
+            DrawText(TextFormat("%.2fx", scale), 2 + MARGIN_SIZE, 92 + 256 + MARGIN_SIZE, 20, BLACK);
+
+            DrawText("Rotation (LEFT/RIGHT to change)", 2 + MARGIN_SIZE, 122 + 256 + MARGIN_SIZE, 10, BLACK);
+            DrawText(TextFormat("%.0f degrees", rotation), 2 + MARGIN_SIZE, 134 + 256 + MARGIN_SIZE, 20, BLACK);
+
+            DrawText("Press [SPACE] to reset", 2 + MARGIN_SIZE, 164 + 256 + MARGIN_SIZE, 10, DARKBLUE);
+
+            // Draw FPS
+            DrawText(TextFormat("%i FPS", GetFPS()), 2 + MARGIN_SIZE, 2 + MARGIN_SIZE, 20, BLACK);
+        EndDrawing();
+        //----------------------------------------------------------------------------------
+    }
+
+    // De-Initialization
+    //--------------------------------------------------------------------------------------
+    UnloadTexture(texPattern);  // Unload texture
+
+    CloseWindow();              // Close window and OpenGL context
+    //--------------------------------------------------------------------------------------
+
+    return 0;
+}
+
+// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest
+void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
+{
+    if ((texture.id <= 0) || (scale <= 0.0f)) return;  // Wanna see a infinite loop?!...just delete this line!
+    if ((source.width == 0) || (source.height == 0)) return;
+
+    int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
+    if ((dest.width < tileWidth) && (dest.height < tileHeight))
+    {
+        // Can fit only one tile
+        DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
+                    (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint);
+    }
+    else if (dest.width <= tileWidth)
+    {
+        // Tiled vertically (one column)
+        int dy = 0;
+        for (;dy+tileHeight < dest.height; dy += tileHeight)
+        {
+            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint);
+        }
+
+        // Fit last tile
+        if (dy < dest.height)
+        {
+            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
+                        (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint);
+        }
+    }
+    else if (dest.height <= tileHeight)
+    {
+        // Tiled horizontally (one row)
+        int dx = 0;
+        for (;dx+tileWidth < dest.width; dx += tileWidth)
+        {
+            DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint);
+        }
+
+        // Fit last tile
+        if (dx < dest.width)
+        {
+            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
+                        (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint);
+        }
+    }
+    else
+    {
+        // Tiled both horizontally and vertically (rows and columns)
+        int dx = 0;
+        for (;dx+tileWidth < dest.width; dx += tileWidth)
+        {
+            int dy = 0;
+            for (;dy+tileHeight < dest.height; dy += tileHeight)
+            {
+                DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint);
+            }
+
+            if (dy < dest.height)
+            {
+                DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
+                    (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint);
+            }
+        }
+
+        // Fit last column of tiles
+        if (dx < dest.width)
+        {
+            int dy = 0;
+            for (;dy+tileHeight < dest.height; dy += tileHeight)
+            {
+                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height},
+                        (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint);
+            }
+
+            // Draw final tile in the bottom right corner
+            if (dy < dest.height)
+            {
+                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
+                    (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint);
+            }
+        }
+    }
+}
diff --git a/raylib/examples/textures/textures_to_image.c b/raylib/examples/textures/textures_to_image.c
--- a/raylib/examples/textures/textures_to_image.c
+++ b/raylib/examples/textures/textures_to_image.c
@@ -1,7 +1,9 @@
 /*******************************************************************************************
 *
-*   raylib [textures] example - Retrieve image data from texture: LoadImageFromTexture()
+*   raylib [textures] example - to image
 *
+*   Example complexity rating: [★☆☆☆] 1/4
+*
 *   NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM)
 *
 *   Example originally created with raylib 1.3, last time updated with raylib 4.0
@@ -9,7 +11,7 @@
 *   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2025 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -25,7 +27,7 @@
     const int screenWidth = 800;
     const int screenHeight = 450;
 
-    InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture to image");
+    InitWindow(screenWidth, screenHeight, "raylib [textures] example - to image");
 
     // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
 
diff --git a/raylib/parser/raylib_parser.c b/raylib/parser/raylib_parser.c
deleted file mode 100644
--- a/raylib/parser/raylib_parser.c
+++ /dev/null
@@ -1,2000 +0,0 @@
-/**********************************************************************************************
-
-    raylib API parser
-
-    This parser scans raylib.h to get API information about defines, structs, aliases, enums, callbacks and functions.
-    All data is divided into pieces, usually as strings. The following types are used for data:
-
-     - struct DefineInfo
-     - struct StructInfo
-     - struct AliasInfo
-     - struct EnumInfo
-     - struct FunctionInfo
-
-    CONSTRAINTS:
-
-    This parser is specifically designed to work with raylib.h, so, it has some constraints:
-
-     - Functions are expected as a single line with the following structure:
-
-       <retType> <name>(<paramType[0]> <paramName[0]>, <paramType[1]> <paramName[1]>);  <desc>
-
-       Be careful with functions broken into several lines, it breaks the process!
-
-     - Structures are expected as several lines with the following form:
-
-       <desc>
-       typedef struct <name> {
-           <fieldType[0]> <fieldName[0]>;  <fieldDesc[0]>
-           <fieldType[1]> <fieldName[1]>;  <fieldDesc[1]>
-           <fieldType[2]> <fieldName[2]>;  <fieldDesc[2]>
-       } <name>;
-
-     - Enums are expected as several lines with the following form:
-
-       <desc>
-       typedef enum {
-           <valueName[0]> = <valueInteger[0]>, <valueDesc[0]>
-           <valueName[1]>,
-           <valueName[2]>, <valueDesc[2]>
-           <valueName[3]>  <valueDesc[3]>
-       } <name>;
-
-       NOTE: Multiple options are supported for enums:
-          - If value is not provided, (<valueInteger[i -1]> + 1) is assigned
-          - Value description can be provided or not
-
-    OTHER NOTES:
-
-     - This parser could work with other C header files if mentioned constraints are followed.
-     - This parser does not require <string.h> library, all data is parsed directly from char buffers.
-
-    LICENSE: zlib/libpng
-
-    raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
-    BSD-like license that allows static linking with closed source software:
-
-    Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)
-
-**********************************************************************************************/
-
-#define _CRT_SECURE_NO_WARNINGS
-
-#include <stdlib.h>             // Required for: malloc(), calloc(), realloc(), free(), atoi(), strtol()
-#include <stdio.h>              // Required for: printf(), fopen(), fseek(), ftell(), fread(), fclose()
-#include <stdbool.h>            // Required for: bool
-#include <ctype.h>              // Required for: isdigit()
-
-#define MAX_DEFINES_TO_PARSE    2048    // Maximum number of defines to parse
-#define MAX_STRUCTS_TO_PARSE      64    // Maximum number of structures to parse
-#define MAX_ALIASES_TO_PARSE      64    // Maximum number of aliases to parse
-#define MAX_ENUMS_TO_PARSE        64    // Maximum number of enums to parse
-#define MAX_CALLBACKS_TO_PARSE    64    // Maximum number of callbacks to parse
-#define MAX_FUNCS_TO_PARSE      1024    // Maximum number of functions to parse
-
-#define MAX_LINE_LENGTH         1024    // Maximum length of one line (including comments)
-
-#define MAX_STRUCT_FIELDS         64    // Maximum number of struct fields
-#define MAX_ENUM_VALUES          512    // Maximum number of enum values
-#define MAX_FUNCTION_PARAMETERS   12    // Maximum number of function parameters
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-
-// Type of parsed define
-typedef enum {
-    UNKNOWN = 0,
-    MACRO,
-    GUARD,
-    INT,
-    INT_MATH,
-    LONG,
-    LONG_MATH,
-    FLOAT,
-    FLOAT_MATH,
-    DOUBLE,
-    DOUBLE_MATH,
-    CHAR,
-    STRING,
-    COLOR
-} DefineType;
-
-// Define info data
-typedef struct DefineInfo {
-    char name[64];              // Define name
-    int type;                   // Define type
-    char value[256];            // Define value
-    char desc[128];             // Define description
-    bool isHex;                 // Define is hex number (for types INT, LONG)
-} DefineInfo;
-
-// Struct info data
-typedef struct StructInfo {
-    char name[64];              // Struct name
-    char desc[128];             // Struct type description
-    int fieldCount;             // Number of fields in the struct
-    char fieldType[MAX_STRUCT_FIELDS][64];     // Field type
-    char fieldName[MAX_STRUCT_FIELDS][64];     // Field name
-    char fieldDesc[MAX_STRUCT_FIELDS][128];    // Field description
-} StructInfo;
-
-// Alias info data
-typedef struct AliasInfo {
-    char type[64];              // Alias type
-    char name[64];              // Alias name
-    char desc[128];             // Alias description
-} AliasInfo;
-
-// Enum info data
-typedef struct EnumInfo {
-    char name[64];              // Enum name
-    char desc[128];             // Enum description
-    int valueCount;             // Number of values in enumerator
-    char valueName[MAX_ENUM_VALUES][64];    // Value name definition
-    int valueInteger[MAX_ENUM_VALUES];      // Value integer
-    char valueDesc[MAX_ENUM_VALUES][128];   // Value description
-} EnumInfo;
-
-// Function info data
-typedef struct FunctionInfo {
-    char name[64];              // Function name
-    char desc[512];             // Function description (comment at the end)
-    char retType[32];           // Return value type
-    int paramCount;             // Number of function parameters
-    char paramType[MAX_FUNCTION_PARAMETERS][32];   // Parameters type
-    char paramName[MAX_FUNCTION_PARAMETERS][32];   // Parameters name
-    char paramDesc[MAX_FUNCTION_PARAMETERS][128];  // Parameters description
-} FunctionInfo;
-
-// Output format for parsed data
-typedef enum { DEFAULT = 0, JSON, XML, LUA, CODE } OutputFormat;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-static int defineCount = 0;
-static int structCount = 0;
-static int aliasCount = 0;
-static int enumCount = 0;
-static int callbackCount = 0;
-static int funcCount = 0;
-static DefineInfo *defines = NULL;
-static StructInfo *structs = NULL;
-static AliasInfo *aliases = NULL;
-static EnumInfo *enums = NULL;
-static FunctionInfo *callbacks = NULL;
-static FunctionInfo *funcs = NULL;
-
-// Command line variables
-static char apiDefine[32] = { 0 };         // Functions define (i.e. RLAPI for raylib.h, RMDEF for raymath.h, etc.)
-static char truncAfter[32] = { 0 };        // Truncate marker (i.e. "RLGL IMPLEMENTATION" for rlgl.h)
-static int outputFormat = DEFAULT;
-
-// NOTE: Max length depends on OS, in Windows MAX_PATH = 256
-static char inFileName[512] = { 0 };        // Input file name (required in case of drag & drop over executable)
-static char outFileName[512] = { 0 };       // Output file name (required for file save/export)
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-static void ShowCommandLineInfo(void);                      // Show command line usage info
-static void ProcessCommandLine(int argc, char *argv[]);     // Process command line input
-
-static char *LoadFileText(const char *fileName, int *length);
-static char **GetTextLines(const char *buffer, int length, int *linesCount);
-static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name);
-static void GetDescription(const char *source, char *description);
-static void MoveArraySize(char *name, char *type);          // Move array size from name to type
-static unsigned int TextLength(const char *text);           // Get text length in bytes, check for \0 character
-static bool IsTextEqual(const char *text1, const char *text2, unsigned int count);
-static int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string
-static void MemoryCopy(void *dest, const void *src, unsigned int count);
-static char *EscapeBackslashes(char *text);                 // Replace '\' by "\\" when exporting to JSON and XML
-static const char *StrDefineType(DefineType type);          // Get string of define type
-
-static void ExportParsedData(const char *fileName, int format); // Export parsed data in desired format
-
-//----------------------------------------------------------------------------------
-// Program main entry point
-//----------------------------------------------------------------------------------
-int main(int argc, char* argv[])
-{
-    if (argc > 1) ProcessCommandLine(argc, argv);
-
-    const char *raylibhPath = "../src/raylib.h\0";
-    const char *raylibapiPath = "raylib_api.txt\0";
-    const char *rlapiPath = "RLAPI\0";
-    if (inFileName[0] == '\0') MemoryCopy(inFileName, raylibhPath, TextLength(raylibhPath) + 1);
-    if (outFileName[0] == '\0') MemoryCopy(outFileName, raylibapiPath, TextLength(raylibapiPath) + 1);
-    if (apiDefine[0] == '\0') MemoryCopy(apiDefine, rlapiPath, TextLength(rlapiPath) + 1);
-
-    int length = 0;
-    char *buffer = LoadFileText(inFileName, &length);
-
-    if (buffer == NULL)
-    {
-        printf("Could not read input file: %s\n", inFileName);
-        return 1;
-    }
-
-    // Preprocess buffer to get separate lines
-    // NOTE: GetTextLines() also removes leading spaces/tabs
-    int linesCount = 0;
-    char **lines = GetTextLines(buffer, length, &linesCount);
-
-    // Truncate lines
-    if (truncAfter[0] != '\0')
-    {
-        int newCount = -1;
-        for (int i = 0; i < linesCount; i++)
-        {
-            if (newCount > -1) free(lines[i]);
-            else if (TextFindIndex(lines[i], truncAfter) > -1) newCount = i;
-        }
-        if (newCount > -1) linesCount = newCount;
-        printf("Number of truncated text lines: %i\n", linesCount);
-    }
-
-    // Defines line indices
-    int *defineLines = (int *)malloc(MAX_DEFINES_TO_PARSE*sizeof(int));
-
-    // Structs line indices
-    int *structLines = (int *)malloc(MAX_STRUCTS_TO_PARSE*sizeof(int));
-
-    // Aliases line indices
-    int *aliasLines = (int *)malloc(MAX_ALIASES_TO_PARSE*sizeof(int));
-
-    // Enums line indices
-    int *enumLines = (int *)malloc(MAX_ENUMS_TO_PARSE*sizeof(int));
-
-    // Callbacks line indices
-    int *callbackLines = (int *)malloc(MAX_CALLBACKS_TO_PARSE*sizeof(int));
-
-    // Function line indices
-    int *funcLines = (int *)malloc(MAX_FUNCS_TO_PARSE*sizeof(int));
-
-    // Prepare required lines for parsing
-    //----------------------------------------------------------------------------------
-
-    // Read define lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        int j = 0;
-        while ((lines[i][j] == ' ') || (lines[i][j] == '\t')) j++; // skip spaces and tabs in the begining
-        // Read define line
-        if (IsTextEqual(lines[i]+j, "#define ", 8))
-        {
-            // Keep the line position in the array of lines,
-            // so, we can scan that position and following lines
-            defineLines[defineCount] = i;
-            defineCount++;
-        }
-    }
-
-    // Read struct lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        // Find structs
-        // starting with "typedef struct ... {" or "typedef struct ... ; \n struct ... {"
-        // ending with "} ... ;"
-        // i.e. excluding "typedef struct rAudioBuffer rAudioBuffer;" -> Typedef and forward declaration only
-        if (IsTextEqual(lines[i], "typedef struct", 14))
-        {
-            bool validStruct = IsTextEqual(lines[i + 1], "struct", 6);
-            if (!validStruct)
-            {
-                for (int c = 0; c < MAX_LINE_LENGTH; c++)
-                {
-                    char v = lines[i][c];
-                    if (v == '{') validStruct = true;
-                    if ((v == '{') || (v == ';') || (v == '\0')) break;
-                }
-            }
-            if (!validStruct) continue;
-            structLines[structCount] = i;
-            while (lines[i][0] != '}') i++;
-            while (lines[i][0] != '\0') i++;
-            structCount++;
-        }
-    }
-
-    // Read alias lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        // Find aliases (lines with "typedef ... ...;")
-        if (IsTextEqual(lines[i], "typedef", 7))
-        {
-            int spaceCount = 0;
-            bool validAlias = false;
-
-            for (int c = 0; c < MAX_LINE_LENGTH; c++)
-            {
-                char v = lines[i][c];
-                if (v == ' ') spaceCount++;
-                if ((v == ';') && (spaceCount == 2)) validAlias = true;
-                if ((v == ';') || (v == '(') || (v == '\0')) break;
-            }
-            if (!validAlias) continue;
-            aliasLines[aliasCount] = i;
-            aliasCount++;
-        }
-    }
-
-    // Read enum lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        // Read enum line
-        if (IsTextEqual(lines[i], "typedef enum {", 14) && (lines[i][TextLength(lines[i])-1] != ';')) // ignore inline enums
-        {
-            // Keep the line position in the array of lines,
-            // so, we can scan that position and following lines
-            enumLines[enumCount] = i;
-            enumCount++;
-        }
-    }
-
-    // Read callback lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        // Find callbacks (lines with "typedef ... (* ... )( ... );")
-        if (IsTextEqual(lines[i], "typedef", 7))
-        {
-            bool hasBeginning = false;
-            bool hasMiddle = false;
-            bool hasEnd = false;
-
-            for (int c = 0; c < MAX_LINE_LENGTH; c++)
-            {
-                if ((lines[i][c] == '(') && (lines[i][c + 1] == '*')) hasBeginning = true;
-                if ((lines[i][c] == ')') && (lines[i][c + 1] == '(')) hasMiddle = true;
-                if ((lines[i][c] == ')') && (lines[i][c + 1] == ';')) hasEnd = true;
-                if (hasEnd) break;
-            }
-
-            if (hasBeginning && hasMiddle && hasEnd)
-            {
-                callbackLines[callbackCount] = i;
-                callbackCount++;
-            }
-        }
-    }
-
-    // Read function lines
-    for (int i = 0; i < linesCount; i++)
-    {
-        // Read function line (starting with `define`, i.e. for raylib.h "RLAPI")
-        if (IsTextEqual(lines[i], apiDefine, TextLength(apiDefine)))
-        {
-            funcLines[funcCount] = i;
-            funcCount++;
-        }
-    }
-
-    // At this point we have all raylib defines, structs, aliases, enums, callbacks, functions lines data to start parsing
-
-    free(buffer);       // Unload text buffer
-
-    // Parsing raylib data
-    //----------------------------------------------------------------------------------
-
-    // Define info data
-    defines = (DefineInfo *)calloc(MAX_DEFINES_TO_PARSE, sizeof(DefineInfo));
-    int defineIndex = 0;
-
-    for (int i = 0; i < defineCount; i++)
-    {
-        char *linePtr = lines[defineLines[i]];
-        int j = 0;
-
-        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs in the begining
-        j += 8;                                                  // Skip "#define "
-        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs after "#define "
-
-        // Extract name
-        int defineNameStart = j;
-        int openBraces = 0;
-        while (linePtr[j] != '\0')
-        {
-            if (((linePtr[j] == ' ') || (linePtr[j] == '\t')) && (openBraces == 0)) break;
-            if (linePtr[j] == '(') openBraces++;
-            if (linePtr[j] == ')') openBraces--;
-            j++;
-        }
-        int defineNameEnd = j-1;
-
-        // Skip duplicates
-        unsigned int nameLen = defineNameEnd - defineNameStart + 1;
-        bool isDuplicate = false;
-        for (int k = 0; k < defineIndex; k++)
-        {
-            if ((nameLen == TextLength(defines[k].name)) && IsTextEqual(defines[k].name, &linePtr[defineNameStart], nameLen))
-            {
-                isDuplicate = true;
-                break;
-            }
-        }
-        if (isDuplicate) continue;
-
-        MemoryCopy(defines[defineIndex].name, &linePtr[defineNameStart], nameLen);
-
-        // Determine type
-        if (linePtr[defineNameEnd] == ')') defines[defineIndex].type = MACRO;
-
-        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs after name
-
-        int defineValueStart = j;
-        if ((linePtr[j] == '\0') || (linePtr[j] == '/')) defines[defineIndex].type = GUARD;
-        if (linePtr[j] == '"') defines[defineIndex].type = STRING;
-        else if (linePtr[j] == '\'') defines[defineIndex].type = CHAR;
-        else if (IsTextEqual(linePtr+j, "CLITERAL(Color)", 15)) defines[defineIndex].type = COLOR;
-        else if (isdigit(linePtr[j])) // Parsing numbers
-        {
-            bool isFloat = false, isNumber = true, isHex = false;
-            while ((linePtr[j] != ' ') && (linePtr[j] != '\t') && (linePtr[j] != '\0'))
-            {
-                char ch = linePtr[j];
-                if (ch == '.') isFloat = true;
-                if (ch == 'x') isHex = true;
-                if (!(isdigit(ch) ||
-                      ((ch >= 'a') && (ch <= 'f')) ||
-                      ((ch >= 'A') && (ch <= 'F')) ||
-                      (ch == 'x') ||
-                      (ch == 'L') ||
-                      (ch == '.') ||
-                      (ch == '+') ||
-                      (ch == '-'))) isNumber = false;
-                j++;
-            }
-            if (isNumber)
-            {
-                if (isFloat)
-                {
-                    defines[defineIndex].type = (linePtr[j-1] == 'f')? FLOAT : DOUBLE;
-                }
-                else
-                {
-                    defines[defineIndex].type = (linePtr[j-1] == 'L')? LONG : INT;
-                    defines[defineIndex].isHex = isHex;
-                }
-            }
-        }
-
-        // Extracting value
-        while ((linePtr[j] != '\\') && (linePtr[j] != '\0') && !((linePtr[j] == '/') && (linePtr[j+1] == '/'))) j++;
-        int defineValueEnd = j-1;
-        while ((linePtr[defineValueEnd] == ' ') || (linePtr[defineValueEnd] == '\t')) defineValueEnd--; // Remove trailing spaces and tabs
-        if ((defines[defineIndex].type == LONG) || (defines[defineIndex].type == FLOAT)) defineValueEnd--; // Remove number postfix
-        int valueLen = defineValueEnd - defineValueStart + 1;
-        if (valueLen > 255) valueLen = 255;
-
-        if (valueLen > 0) MemoryCopy(defines[defineIndex].value, &linePtr[defineValueStart], valueLen);
-
-        // Extracting description
-        if ((linePtr[j] == '/') && linePtr[j + 1] == '/')
-        {
-            j += 2;
-            while (linePtr[j] == ' ') j++;
-            int commentStart = j;
-            while ((linePtr[j] != '\\') && (linePtr[j] != '\0')) j++;
-            int commentEnd = j-1;
-            int commentLen = commentEnd - commentStart + 1;
-            if (commentLen > 127) commentLen = 127;
-
-            MemoryCopy(defines[defineIndex].desc, &linePtr[commentStart], commentLen);
-        }
-
-        // Parse defines of type UNKNOWN to find calculated numbers
-        if (defines[defineIndex].type == UNKNOWN)
-        {
-            int largestType = UNKNOWN;
-            bool isMath = true;
-            char *valuePtr = defines[defineIndex].value;
-
-            for (unsigned int c = 0; c < TextLength(valuePtr); c++)
-            {
-                char ch = valuePtr[c];
-
-                // Skip operators and whitespace
-                if ((ch == '(') ||
-                    (ch == ')') ||
-                    (ch == '+') ||
-                    (ch == '-') ||
-                    (ch == '*') ||
-                    (ch == '/') ||
-                    (ch == ' ') ||
-                    (ch == '\t')) continue;
-
-                // Read number operand
-                else if (isdigit(ch))
-                {
-                    bool isNumber = true, isFloat = false;
-                    while (!((ch == '(') ||
-                             (ch == ')') ||
-                             (ch == '*') ||
-                             (ch == '/') ||
-                             (ch == ' ') ||
-                             (ch == '\t') ||
-                             (ch == '\0')))
-                    {
-                        if (ch == '.') isFloat = true;
-                        if (!(isdigit(ch) ||
-                            ((ch >= 'a') && (ch <= 'f')) ||
-                            ((ch >= 'A') && (ch <= 'F')) ||
-                            (ch == 'x') ||
-                            (ch == 'L') ||
-                            (ch == '.') ||
-                            (ch == '+') ||
-                            (ch == '-')))
-                        {
-                            isNumber = false;
-                            break;
-                        }
-                        c++;
-                        ch = valuePtr[c];
-                    }
-                    if (isNumber)
-                    {
-                        // Found a valid number -> update largestType
-                        int numberType;
-                        if (isFloat) numberType = (valuePtr[c - 1] == 'f')? FLOAT_MATH : DOUBLE_MATH;
-                        else numberType = (valuePtr[c - 1] == 'L')? LONG_MATH : INT_MATH;
-
-                        if (numberType > largestType) largestType = numberType;
-                    }
-                    else
-                    {
-                        isMath = false;
-                        break;
-                    }
-                }
-                else    // Read string operand
-                {
-                    int operandStart = c;
-                    while (!((ch == '\0') ||
-                             (ch == ' ') ||
-                             (ch == '(') ||
-                             (ch == ')') ||
-                             (ch == '+') ||
-                             (ch == '-') ||
-                             (ch == '*') ||
-                             (ch == '/')))
-                    {
-                        c++;
-                        ch = valuePtr[c];
-                    }
-                    int operandEnd = c;
-                    int operandLength = operandEnd - operandStart;
-
-                    // Search previous defines for operand
-                    bool foundOperand = false;
-                    for (int previousDefineIndex = 0; previousDefineIndex < defineIndex; previousDefineIndex++)
-                    {
-                        if (IsTextEqual(defines[previousDefineIndex].name, &valuePtr[operandStart], operandLength))
-                        {
-                            if ((defines[previousDefineIndex].type >= INT) && (defines[previousDefineIndex].type <= DOUBLE_MATH))
-                            {
-                                // Found operand and it's a number -> update largestType
-                                if (defines[previousDefineIndex].type > largestType) largestType = defines[previousDefineIndex].type;
-                                foundOperand = true;
-                            }
-                            break;
-                        }
-                    }
-                    if (!foundOperand)
-                    {
-                        isMath = false;
-                        break;
-                    }
-                }
-            }
-
-            if (isMath)
-            {
-                // Define is a calculated number -> update type
-                if (largestType == INT) largestType = INT_MATH;
-                else if (largestType == LONG) largestType = LONG_MATH;
-                else if (largestType == FLOAT) largestType = FLOAT_MATH;
-                else if (largestType == DOUBLE) largestType = DOUBLE_MATH;
-                defines[defineIndex].type = largestType;
-            }
-        }
-
-        defineIndex++;
-    }
-    defineCount = defineIndex;
-    free(defineLines);
-
-    // Structs info data
-    structs = (StructInfo *)calloc(MAX_STRUCTS_TO_PARSE, sizeof(StructInfo));
-
-    for (int i = 0; i < structCount; i++)
-    {
-        char **linesPtr = &lines[structLines[i]];
-
-        // Parse struct description
-        GetDescription(linesPtr[-1], structs[i].desc);
-
-        // Get struct name: typedef struct name {
-        const int TDS_LEN = 15; // length of "typedef struct "
-        for (int c = TDS_LEN; c < 64 + TDS_LEN; c++)
-        {
-            if ((linesPtr[0][c] == '{') || (linesPtr[0][c] == ' '))
-            {
-                int nameLen = c - TDS_LEN;
-                while (linesPtr[0][TDS_LEN + nameLen - 1] == ' ') nameLen--;
-                MemoryCopy(structs[i].name, &linesPtr[0][TDS_LEN], nameLen);
-                break;
-            }
-        }
-
-        // Get struct fields and count them -> fields finish with ;
-        int l = 1;
-        while (linesPtr[l][0] != '}')
-        {
-            // WARNING: Some structs have empty spaces and comments -> OK, processed
-            if ((linesPtr[l][0] != ' ') && (linesPtr[l][0] != '\0'))
-            {
-                // Scan one field line
-                char *fieldLine = linesPtr[l];
-                int fieldEndPos = 0;
-                while (fieldLine[fieldEndPos] != ';') fieldEndPos++;
-
-                if ((fieldLine[0] != '/') && !IsTextEqual(fieldLine, "struct", 6)) // Field line is not a comment and not a struct declaration
-                {
-                    //printf("Struct field: %s_\n", fieldLine);     // OK!
-
-                    // Get struct field type and name
-                    GetDataTypeAndName(fieldLine, fieldEndPos, structs[i].fieldType[structs[i].fieldCount], structs[i].fieldName[structs[i].fieldCount]);
-
-                    // Get the field description
-                    GetDescription(&fieldLine[fieldEndPos], structs[i].fieldDesc[structs[i].fieldCount]);
-
-                    structs[i].fieldCount++;
-
-                    // Split field names containing multiple fields (like Matrix)
-                    int additionalFields = 0;
-                    int originalIndex = structs[i].fieldCount - 1;
-                    for (unsigned int c = 0; c < TextLength(structs[i].fieldName[originalIndex]); c++)
-                    {
-                        if (structs[i].fieldName[originalIndex][c] == ',') additionalFields++;
-                    }
-
-                    if (additionalFields > 0)
-                    {
-                        int originalLength = -1;
-                        int lastStart;
-                        for (unsigned int c = 0; c < TextLength(structs[i].fieldName[originalIndex]) + 1; c++)
-                        {
-                            char v = structs[i].fieldName[originalIndex][c];
-                            bool isEndOfString = (v == '\0');
-                            if ((v == ',') || isEndOfString)
-                            {
-                                if (originalLength == -1)
-                                {
-                                    // Save length of original field name
-                                    // Don't truncate yet, still needed for copying
-                                    originalLength = c;
-                                }
-                                else
-                                {
-                                    // Copy field data from original field
-                                    int nameLength = c - lastStart;
-                                    MemoryCopy(structs[i].fieldName[structs[i].fieldCount], &structs[i].fieldName[originalIndex][lastStart], nameLength);
-                                    MemoryCopy(structs[i].fieldType[structs[i].fieldCount], &structs[i].fieldType[originalIndex][0], TextLength(structs[i].fieldType[originalIndex]));
-                                    MemoryCopy(structs[i].fieldDesc[structs[i].fieldCount], &structs[i].fieldDesc[originalIndex][0], TextLength(structs[i].fieldDesc[originalIndex]));
-                                    structs[i].fieldCount++;
-                                }
-                                if (!isEndOfString)
-                                {
-                                    // Skip comma and spaces
-                                    c++;
-                                    while (structs[i].fieldName[originalIndex][c] == ' ') c++;
-
-                                    // Save position for next field
-                                    lastStart = c;
-                                }
-                            }
-                        }
-                        // Set length of original field to truncate the first field name
-                        structs[i].fieldName[originalIndex][originalLength] = '\0';
-                    }
-
-                    // Split field types containing multiple fields (like MemNode)
-                    additionalFields = 0;
-                    originalIndex = structs[i].fieldCount - 1;
-                    for (unsigned int c = 0; c < TextLength(structs[i].fieldType[originalIndex]); c++)
-                    {
-                        if (structs[i].fieldType[originalIndex][c] == ',') additionalFields++;
-                    }
-
-                    if (additionalFields > 0)
-                    {
-                        // Copy original name to last additional field
-                        structs[i].fieldCount += additionalFields;
-                        MemoryCopy(structs[i].fieldName[originalIndex + additionalFields], &structs[i].fieldName[originalIndex][0], TextLength(structs[i].fieldName[originalIndex]));
-
-                        // Copy names from type to additional fields
-                        int fieldsRemaining = additionalFields;
-                        int nameStart = -1;
-                        int nameEnd = -1;
-                        for (int k = TextLength(structs[i].fieldType[originalIndex]); k > 0; k--)
-                        {
-                            char v = structs[i].fieldType[originalIndex][k];
-                            if ((v == '*') || (v == ' ') || (v == ','))
-                            {
-                                if (nameEnd != -1) {
-                                    // Don't copy to last additional field
-                                    if (fieldsRemaining != additionalFields)
-                                    {
-                                        nameStart = k + 1;
-                                        MemoryCopy(structs[i].fieldName[originalIndex + fieldsRemaining], &structs[i].fieldType[originalIndex][nameStart], nameEnd - nameStart + 1);
-                                    }
-                                    nameEnd = -1;
-                                    fieldsRemaining--;
-                                }
-                            }
-                            else if (nameEnd == -1) nameEnd = k;
-                        }
-
-                        // Truncate original field type
-                        int fieldTypeLength = nameStart;
-                        structs[i].fieldType[originalIndex][fieldTypeLength] = '\0';
-
-                        // Set field type and description of additional fields
-                        for (int j = 1; j <= additionalFields; j++)
-                        {
-                            MemoryCopy(structs[i].fieldType[originalIndex + j], &structs[i].fieldType[originalIndex][0], fieldTypeLength);
-                            MemoryCopy(structs[i].fieldDesc[originalIndex + j], &structs[i].fieldDesc[originalIndex][0], TextLength(structs[i].fieldDesc[originalIndex]));
-                        }
-                    }
-                }
-            }
-
-            l++;
-        }
-
-        // Move array sizes from name to type
-        for (int j = 0; j < structs[i].fieldCount; j++)
-        {
-            MoveArraySize(structs[i].fieldName[j], structs[i].fieldType[j]);
-        }
-    }
-    free(structLines);
-
-    // Alias info data
-    aliases = (AliasInfo *)calloc(MAX_ALIASES_TO_PARSE, sizeof(AliasInfo));
-
-    for (int i = 0; i < aliasCount; i++)
-    {
-        // Description from previous line
-        GetDescription(lines[aliasLines[i] - 1], aliases[i].desc);
-
-        char *linePtr = lines[aliasLines[i]];
-
-        // Skip "typedef "
-        int c = 8;
-
-        // Type
-        int typeStart = c;
-        while(linePtr[c] != ' ') c++;
-        int typeLen = c - typeStart;
-        MemoryCopy(aliases[i].type, &linePtr[typeStart], typeLen);
-
-        // Skip space
-        c++;
-
-        // Name
-        int nameStart = c;
-        while(linePtr[c] != ';') c++;
-        int nameLen = c - nameStart;
-        MemoryCopy(aliases[i].name, &linePtr[nameStart], nameLen);
-
-        // Description
-        GetDescription(&linePtr[c], aliases[i].desc);
-    }
-    free(aliasLines);
-
-    // Enum info data
-    enums = (EnumInfo *)calloc(MAX_ENUMS_TO_PARSE, sizeof(EnumInfo));
-
-    for (int i = 0; i < enumCount; i++)
-    {
-
-        // Parse enum description
-        // NOTE: This is not necessarily from the line immediately before,
-        // some of the enums have extra lines between the "description"
-        // and the typedef enum
-        for (int j = enumLines[i] - 1; j > 0; j--)
-        {
-            char *linePtr = lines[j];
-            if ((linePtr[0] != '/') || (linePtr[2] != ' '))
-            {
-                GetDescription(&lines[j + 1][0], enums[i].desc);
-                break;
-            }
-        }
-
-        for (int j = 1; j < MAX_ENUM_VALUES*2; j++)   // Maximum number of lines following enum first line
-        {
-            char *linePtr = lines[enumLines[i] + j];
-
-            if ((linePtr[0] >= 'A') && (linePtr[0] <= 'Z'))
-            {
-                // Parse enum value line, possible options:
-                //ENUM_VALUE_NAME,
-                //ENUM_VALUE_NAME
-                //ENUM_VALUE_NAME     = 99
-                //ENUM_VALUE_NAME     = 99,
-                //ENUM_VALUE_NAME     = 0x00000040,   // Value description
-
-                // We start reading the value name
-                int c = 0;
-                while ((linePtr[c] != ',') &&
-                       (linePtr[c] != ' ') &&
-                       (linePtr[c] != '=') &&
-                       (linePtr[c] != '\0'))
-                {
-                    enums[i].valueName[enums[i].valueCount][c] = linePtr[c];
-                    c++;
-                }
-
-                // After the name we can have:
-                //  '='  -> value is provided
-                //  ','  -> value is equal to previous + 1, there could be a description if not '\0'
-                //  ' '  -> value is equal to previous + 1, there could be a description if not '\0'
-                //  '\0' -> value is equal to previous + 1
-
-                // Let's start checking if the line is not finished
-                if ((linePtr[c] != ',') && (linePtr[c] != '\0'))
-                {
-                    // Two options:
-                    //  '='  -> value is provided
-                    //  ' '  -> value is equal to previous + 1, there could be a description if not '\0'
-                    bool foundValue = false;
-                    while ((linePtr[c] != '\0') && (linePtr[c] != '/'))
-                    {
-                        if (linePtr[c] == '=')
-                        {
-                            foundValue = true;
-                            break;
-                        }
-                        c++;
-                    }
-
-                    if (foundValue)
-                    {
-                        if (linePtr[c + 1] == ' ') c += 2;
-                        else c++;
-
-                        // Parse integer value
-                        int n = 0;
-                        char integer[16] = { 0 };
-
-                        while ((linePtr[c] != ',') && (linePtr[c] != ' ') && (linePtr[c] != '\0'))
-                        {
-                            integer[n] = linePtr[c];
-                            c++; n++;
-                        }
-
-                        if (integer[1] == 'x') enums[i].valueInteger[enums[i].valueCount] = (int)strtol(integer, NULL, 16);
-                        else enums[i].valueInteger[enums[i].valueCount] = atoi(integer);
-                    }
-                    else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
-                }
-                else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
-
-                // Parse value description
-                GetDescription(&linePtr[c], enums[i].valueDesc[enums[i].valueCount]);
-
-                enums[i].valueCount++;
-            }
-            else if (linePtr[0] == '}')
-            {
-                // Get enum name from typedef
-                int c = 0;
-                while (linePtr[2 + c] != ';')
-                {
-                    enums[i].name[c] = linePtr[2 + c];
-                    c++;
-                }
-
-                break;  // Enum ended, break for() loop
-            }
-        }
-    }
-    free(enumLines);
-
-    // Callback info data
-    callbacks = (FunctionInfo *)calloc(MAX_CALLBACKS_TO_PARSE, sizeof(FunctionInfo));
-
-    for (int i = 0; i < callbackCount; i++)
-    {
-        char *linePtr = lines[callbackLines[i]];
-
-        // Skip "typedef "
-        unsigned int c = 8;
-
-        // Return type
-        int retTypeStart = c;
-        while(linePtr[c] != '(') c++;
-        int retTypeLen = c - retTypeStart;
-        while(linePtr[retTypeStart + retTypeLen - 1] == ' ') retTypeLen--;
-        MemoryCopy(callbacks[i].retType, &linePtr[retTypeStart], retTypeLen);
-
-        // Skip "(*"
-        c += 2;
-
-        // Name
-        int nameStart = c;
-        while(linePtr[c] != ')') c++;
-        int nameLen = c - nameStart;
-        MemoryCopy(callbacks[i].name, &linePtr[nameStart], nameLen);
-
-        // Skip ")("
-        c += 2;
-
-        // Params
-        int paramStart = c;
-        for (; c < MAX_LINE_LENGTH; c++)
-        {
-            if ((linePtr[c] == ',') || (linePtr[c] == ')'))
-            {
-                // Get parameter type + name, extract info
-                int paramLen = c - paramStart;
-                GetDataTypeAndName(&linePtr[paramStart], paramLen, callbacks[i].paramType[callbacks[i].paramCount], callbacks[i].paramName[callbacks[i].paramCount]);
-                callbacks[i].paramCount++;
-                paramStart = c + 1;
-                while(linePtr[paramStart] == ' ') paramStart++;
-            }
-            if (linePtr[c] == ')') break;
-        }
-
-        // Description
-        GetDescription(&linePtr[c], callbacks[i].desc);
-
-        // Move array sizes from name to type
-        for (int j = 0; j < callbacks[i].paramCount; j++)
-        {
-            MoveArraySize(callbacks[i].paramName[j], callbacks[i].paramType[j]);
-        }
-    }
-    free(callbackLines);
-
-    // Functions info data
-    funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo));
-
-    for (int i = 0; i < funcCount; i++)
-    {
-        char *linePtr = lines[funcLines[i]];
-
-        int funcParamsStart = 0;
-        int funcEnd = 0;
-
-        // Get return type and function name from func line
-        for (int c = 0; (c < MAX_LINE_LENGTH) && (linePtr[c] != '\n'); c++)
-        {
-            if (linePtr[c] == '(')     // Starts function parameters
-            {
-                funcParamsStart = c + 1;
-
-                // At this point we have function return type and function name
-                char funcRetTypeName[128] = { 0 };
-                int dc = TextLength(apiDefine) + 1;
-                int funcRetTypeNameLen = c - dc;     // Substract `define` ("RLAPI " for raylib.h)
-                MemoryCopy(funcRetTypeName, &linePtr[dc], funcRetTypeNameLen);
-
-                GetDataTypeAndName(funcRetTypeName, funcRetTypeNameLen, funcs[i].retType, funcs[i].name);
-                break;
-            }
-        }
-
-        // Get parameters from func line
-        for (int c = funcParamsStart; c < MAX_LINE_LENGTH; c++)
-        {
-            if (linePtr[c] == ',')     // Starts function parameters
-            {
-                // Get parameter type + name, extract info
-                char funcParamTypeName[128] = { 0 };
-                int funcParamTypeNameLen = c - funcParamsStart;
-                MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
-
-                GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
-
-                funcParamsStart = c + 1;
-                if (linePtr[c + 1] == ' ') funcParamsStart += 1;
-                funcs[i].paramCount++;      // Move to next parameter
-            }
-            else if (linePtr[c] == ')')
-            {
-                funcEnd = c + 2;
-
-                // Check if there are no parameters
-                if ((funcEnd - funcParamsStart == 2) ||
-                    ((linePtr[c - 4] == 'v') &&
-                     (linePtr[c - 3] == 'o') &&
-                     (linePtr[c - 2] == 'i') &&
-                     (linePtr[c - 1] == 'd'))) {
-                  break;
-                }
-
-                // Get parameter type + name, extract info
-                char funcParamTypeName[128] = { 0 };
-                int funcParamTypeNameLen = c - funcParamsStart;
-                MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
-
-                GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
-
-                funcs[i].paramCount++;      // Move to next parameter
-                break;
-            }
-        }
-
-        // Get function description
-        GetDescription(&linePtr[funcEnd], funcs[i].desc);
-
-        // Move array sizes from name to type
-        for (int j = 0; j < funcs[i].paramCount; j++)
-        {
-            MoveArraySize(funcs[i].paramName[j], funcs[i].paramType[j]);
-        }
-    }
-    free(funcLines);
-
-    for (int i = 0; i < linesCount; i++) free(lines[i]);
-    free(lines);
-
-    // At this point, all raylib data has been parsed!
-    //----------------------------------------------------------------------------------
-    // defines[]   -> We have all the defines decomposed into pieces for further analysis
-    // structs[]   -> We have all the structs decomposed into pieces for further analysis
-    // aliases[]   -> We have all the aliases decomposed into pieces for further analysis
-    // enums[]     -> We have all the enums decomposed into pieces for further analysis
-    // callbacks[] -> We have all the callbacks decomposed into pieces for further analysis
-    // funcs[]     -> We have all the functions decomposed into pieces for further analysis
-
-    printf("\nInput file:       %s", inFileName);
-    printf("\nOutput file:      %s", outFileName);
-    if (outputFormat == DEFAULT) printf("\nOutput format:    DEFAULT\n\n");
-    else if (outputFormat == JSON) printf("\nOutput format:    JSON\n\n");
-    else if (outputFormat == XML) printf("\nOutput format:    XML\n\n");
-    else if (outputFormat == LUA) printf("\nOutput format:    LUA\n\n");
-    else if (outputFormat == CODE) printf("\nOutput format:    CODE\n\n");
-
-    ExportParsedData(outFileName, outputFormat);
-
-    free(defines);
-    free(structs);
-    free(aliases);
-    free(enums);
-    free(callbacks);
-    free(funcs);
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-
-// Show command line usage info
-static void ShowCommandLineInfo(void)
-{
-    printf("\n//////////////////////////////////////////////////////////////////////////////////\n");
-    printf("//                                                                              //\n");
-    printf("// raylib API parser                                                            //\n");
-    printf("//                                                                              //\n");
-    printf("// more info and bugs-report: github.com/raysan5/raylib/parser                  //\n");
-    printf("//                                                                              //\n");
-    printf("// Copyright (c) 2021-2024 Ramon Santamaria (@raysan5)                          //\n");
-    printf("//                                                                              //\n");
-    printf("//////////////////////////////////////////////////////////////////////////////////\n\n");
-
-    printf("USAGE:\n\n");
-    printf("    > raylib_parser [--help] [--input <filename.h>] [--output <filename.ext>] [--format <type>]\n");
-
-    printf("\nOPTIONS:\n\n");
-    printf("    -h, --help                      : Show tool version and command line usage help\n\n");
-    printf("    -i, --input <filename.h>        : Define input header file to parse.\n");
-    printf("                                      NOTE: If not specified, defaults to: raylib.h\n\n");
-    printf("    -o, --output <filename.ext>     : Define output file and format.\n");
-    printf("                                      Supported extensions: .txt, .json, .xml, .lua, .h\n");
-    printf("                                      NOTE: If not specified, defaults to: raylib_api.txt\n\n");
-    printf("    -f, --format <type>             : Define output format for parser data.\n");
-    printf("                                      Supported types: DEFAULT, JSON, XML, LUA, CODE\n\n");
-    printf("    -d, --define <DEF>              : Define functions specifiers (i.e. RLAPI for raylib.h, RMAPI for raymath.h, etc.)\n");
-    printf("                                      NOTE: If no specifier defined, defaults to: RLAPI\n\n");
-    printf("    -t, --truncate <after>          : Define string to truncate input after (i.e. \"RLGL IMPLEMENTATION\" for rlgl.h)\n");
-    printf("                                      NOTE: If not specified, the full input file is parsed.\n\n");
-
-    printf("\nEXAMPLES:\n\n");
-    printf("    > raylib_parser --input raylib.h --output api.json\n");
-    printf("        Process <raylib.h> to generate <api.json>\n\n");
-    printf("    > raylib_parser --output raylib_data.info --format XML\n");
-    printf("        Process <raylib.h> to generate <raylib_data.info> as XML text data\n\n");
-    printf("    > raylib_parser --input raymath.h --output raymath_data.info --format XML --define RMAPI\n");
-    printf("        Process <raymath.h> to generate <raymath_data.info> as XML text data\n\n");
-}
-
-// Process command line arguments
-static void ProcessCommandLine(int argc, char *argv[])
-{
-    for (int i = 1; i < argc; i++)
-    {
-        if (IsTextEqual(argv[i], "-h", 2) || IsTextEqual(argv[i], "--help", 6))
-        {
-            // Show info
-            ShowCommandLineInfo();
-            exit(0);
-        }
-        else if (IsTextEqual(argv[i], "-i", 2) || IsTextEqual(argv[i], "--input", 7))
-        {
-            // Check for valid argument and valid file extension
-            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
-            {
-                MemoryCopy(inFileName, argv[i + 1], TextLength(argv[i + 1])); // Read input filename
-                i++;
-            }
-            else printf("WARNING: No input file provided\n");
-        }
-        else if (IsTextEqual(argv[i], "-o", 2) || IsTextEqual(argv[i], "--output", 8))
-        {
-            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
-            {
-                MemoryCopy(outFileName, argv[i + 1], TextLength(argv[i + 1])); // Read output filename
-                i++;
-            }
-            else printf("WARNING: No output file provided\n");
-        }
-        else if (IsTextEqual(argv[i], "-f", 2) || IsTextEqual(argv[i], "--format", 8))
-        {
-            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
-            {
-                if (IsTextEqual(argv[i + 1], "DEFAULT\0", 8)) outputFormat = DEFAULT;
-                else if (IsTextEqual(argv[i + 1], "JSON\0", 5)) outputFormat = JSON;
-                else if (IsTextEqual(argv[i + 1], "XML\0", 4)) outputFormat = XML;
-                else if (IsTextEqual(argv[i + 1], "LUA\0", 4)) outputFormat = LUA;
-                else if (IsTextEqual(argv[i + 1], "CODE\0", 4)) outputFormat = CODE;
-            }
-            else printf("WARNING: No format parameters provided\n");
-        }
-        else if (IsTextEqual(argv[i], "-d", 2) || IsTextEqual(argv[i], "--define", 8))
-        {
-            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
-            {
-                MemoryCopy(apiDefine, argv[i + 1], TextLength(argv[i + 1])); // Read functions define
-                apiDefine[TextLength(argv[i + 1])] = '\0';
-                i++;
-            }
-            else printf("WARNING: No define key provided\n");
-        }
-        else if (IsTextEqual(argv[i], "-t", 2) || IsTextEqual(argv[i], "--truncate", 10))
-        {
-            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
-            {
-                MemoryCopy(truncAfter, argv[i + 1], TextLength(argv[i + 1])); // Read truncate marker
-                truncAfter[TextLength(argv[i + 1])] = '\0';
-                i++;
-            }
-        }
-    }
-}
-
-// Load text data from file, returns a '\0' terminated string
-// NOTE: text chars array should be freed manually
-static char *LoadFileText(const char *fileName, int *length)
-{
-    char *text = NULL;
-
-    if (fileName != NULL)
-    {
-        FILE *file = fopen(fileName, "rt");
-
-        if (file != NULL)
-        {
-            // WARNING: When reading a file as 'text' file,
-            // text mode causes carriage return-linefeed translation...
-            // ...but using fseek() should return correct byte-offset
-            fseek(file, 0, SEEK_END);
-            int size = ftell(file);
-            fseek(file, 0, SEEK_SET);
-
-            if (size > 0)
-            {
-                text = (char *)calloc((size + 1), sizeof(char));
-                unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
-
-                // WARNING: \r\n is converted to \n on reading, so,
-                // read bytes count gets reduced by the number of lines
-                if (count < (unsigned int)size)
-                {
-                    text = realloc(text, count + 1);
-                    *length = count;
-                }
-                else *length = size;
-
-                // Zero-terminate the string
-                text[count] = '\0';
-            }
-
-            fclose(file);
-        }
-    }
-
-    return text;
-}
-
-// Get all lines from a text buffer (expecting lines ending with '\n')
-static char **GetTextLines(const char *buffer, int length, int *linesCount)
-{
-    // Get the number of lines in the text
-    int count = 0;
-    for (int i = 0; i < length; i++) if (buffer[i] == '\n') count++;
-
-    printf("Number of text lines in buffer: %i\n", count);
-
-    // Allocate as many pointers as lines
-    char **lines = (char **)malloc(count*sizeof(char **));
-
-    char *bufferPtr = (char *)buffer;
-
-    for (int i = 0; (i < count) || (bufferPtr[0] != '\0'); i++)
-    {
-        lines[i] = (char *)calloc(MAX_LINE_LENGTH, sizeof(char));
-
-        // Remove line leading spaces
-        // Find last index of space/tab character
-        int index = 0;
-        while ((bufferPtr[index] == ' ') || (bufferPtr[index] == '\t')) index++;
-
-        int j = 0;
-        while (bufferPtr[index + j] != '\n' && bufferPtr[index + j] != '\0')
-        {
-            lines[i][j] = bufferPtr[index + j];
-            j++;
-        }
-
-        bufferPtr += (index + j + 1);
-    }
-
-    *linesCount = count;
-    return lines;
-}
-
-// Get data type and name from a string containing both
-// NOTE: Useful to parse function parameters and struct fields
-static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name)
-{
-    for (int k = typeNameLen; k > 0; k--)
-    {
-        if ((typeName[k] == ' ') && (typeName[k - 1] != ','))
-        {
-            // Function name starts at this point (and ret type finishes at this point)
-            MemoryCopy(type, typeName, k);
-            MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
-            break;
-        }
-        else if (typeName[k] == '*')
-        {
-            MemoryCopy(type, typeName, k + 1);
-            MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
-            break;
-        }
-        else if ((typeName[k] == '.') && (typeNameLen == 3)) // Handle varargs ...);
-        {
-	    const char *varargsDots = "...";
-	    const char *varargsArg = "args";
-            MemoryCopy(type, varargsDots, TextLength(varargsDots));
-            MemoryCopy(name, varargsArg, TextLength(varargsArg));
-            break;
-        }
-    }
-}
-
-// Get comment from a line, do nothing if no comment in line
-static void GetDescription(const char *line, char *description)
-{
-    int c = 0;
-    int descStart = -1;
-    int lastSlash = -2;
-    bool isValid = false;
-    while (line[c] != '\0')
-    {
-        if (isValid && (descStart == -1) && (line[c] != ' ')) descStart = c;
-        else if (line[c] == '/')
-        {
-            if (lastSlash == c - 1) isValid = true;
-            lastSlash = c;
-        }
-        c++;
-    }
-    if (descStart != -1) MemoryCopy(description, &line[descStart], c - descStart);
-}
-
-// Move array size from name to type
-static void MoveArraySize(char *name, char *type)
-{
-    int nameLength = TextLength(name);
-    if (name[nameLength - 1] == ']')
-    {
-        for (int k = nameLength; k > 0; k--)
-        {
-            if (name[k] == '[')
-            {
-                int sizeLength = nameLength - k;
-                MemoryCopy(&type[TextLength(type)], &name[k], sizeLength);
-                name[k] = '\0';
-            }
-        }
-    }
-}
-
-// Get text length in bytes, check for \0 character
-static unsigned int TextLength(const char *text)
-{
-    unsigned int length = 0;
-
-    if (text != NULL) while (*text++) length++;
-
-    return length;
-}
-
-// Compare two text strings, requires number of characters to compare
-static bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
-{
-    bool result = true;
-
-    for (unsigned int i = 0; i < count; i++)
-    {
-        if (text1[i] != text2[i])
-        {
-            result = false;
-            break;
-        }
-    }
-
-    return result;
-}
-
-// Find first text occurrence within a string
-int TextFindIndex(const char *text, const char *find)
-{
-    int textLen = TextLength(text);
-    int findLen = TextLength(find);
-
-    for (int i = 0; i <= textLen - findLen; i++)
-    {
-        if (IsTextEqual(&text[i], find, findLen)) return i;
-    }
-
-    return -1;
-}
-
-// Custom memcpy() to avoid <string.h>
-static void MemoryCopy(void *dest, const void *src, unsigned int count)
-{
-    char *srcPtr = (char *)src;
-    char *destPtr = (char *)dest;
-
-    for (unsigned int i = 0; i < count; i++) destPtr[i] = srcPtr[i];
-}
-
-// Escape backslashes in a string, writing the escaped string into a static buffer
-static char *EscapeBackslashes(char *text)
-{
-    static char buffer[256] = { 0 };
-
-    int count = 0;
-
-    for (int i = 0; (text[i] != '\0') && (i < 255); i++, count++)
-    {
-        buffer[count] = text[i];
-
-        if (text[i] == '\\')
-        {
-            buffer[count + 1] = '\\';
-            count++;
-        }
-    }
-
-    buffer[count] = '\0';
-
-    return buffer;
-}
-
-// Get string of define type
-static const char *StrDefineType(DefineType type)
-{
-    switch (type)
-    {
-        case UNKNOWN:     return "UNKNOWN";
-        case GUARD:       return "GUARD";
-        case MACRO:       return "MACRO";
-        case INT:         return "INT";
-        case INT_MATH:    return "INT_MATH";
-        case LONG:        return "LONG";
-        case LONG_MATH:   return "LONG_MATH";
-        case FLOAT:       return "FLOAT";
-        case FLOAT_MATH:  return "FLOAT_MATH";
-        case DOUBLE:      return "DOUBLE";
-        case DOUBLE_MATH: return "DOUBLE_MATH";
-        case CHAR:        return "CHAR";
-        case STRING:      return "STRING";
-        case COLOR:       return "COLOR";
-    }
-    return "";
-}
-
-/*
-// Replace text string
-// REQUIRES: strlen(), strstr(), strncpy(), strcpy() -> TODO: Replace by custom implementations!
-// WARNING: Returned buffer must be freed by the user (if return != NULL)
-static char *TextReplace(char *text, const char *replace, const char *by)
-{
-    // Sanity checks and initialization
-    if (!text || !replace || !by) return NULL;
-
-    char *result;
-
-    char *insertPoint;      // Next insert point
-    char *temp;             // Temp pointer
-    int replaceLen;         // Replace string length of (the string to remove)
-    int byLen;              // Replacement length (the string to replace replace by)
-    int lastReplacePos;     // Distance between replace and end of last replace
-    int count;              // Number of replacements
-
-    replaceLen = strlen(replace);
-    if (replaceLen == 0) return NULL;  // Empty replace causes infinite loop during count
-
-    byLen = strlen(by);
-
-    // Count the number of replacements needed
-    insertPoint = text;
-    for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
-
-    // Allocate returning string and point temp to it
-    temp = result = (char *)malloc(strlen(text) + (byLen - replaceLen)*count + 1);
-
-    if (!result) return NULL;   // Memory could not be allocated
-
-    // First time through the loop, all the variable are set correctly from here on,
-    //  - 'temp' points to the end of the result string
-    //  - 'insertPoint' points to the next occurrence of replace in text
-    //  - 'text' points to the remainder of text after "end of replace"
-    while (count--)
-    {
-        insertPoint = strstr(text, replace);
-        lastReplacePos = (int)(insertPoint - text);
-        temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
-        temp = strcpy(temp, by) + byLen;
-        text += lastReplacePos + replaceLen; // Move to next "end of replace"
-    }
-
-    // Copy remaind text part after replacement to result (pointed by moving temp)
-    strcpy(temp, text);
-
-    return result;
-}
-*/
-
-// Export parsed data in desired format
-static void ExportParsedData(const char *fileName, int format)
-{
-    FILE *outFile = fopen(fileName, "wt");
-
-    switch (format)
-    {
-        case DEFAULT:
-        {
-            // Print defines info
-            fprintf(outFile, "\nDefines found: %i\n\n", defineCount);
-            for (int i = 0; i < defineCount; i++)
-            {
-                fprintf(outFile, "Define %03i: %s\n", i + 1, defines[i].name);
-                fprintf(outFile, "  Name: %s\n", defines[i].name);
-                fprintf(outFile, "  Type: %s\n", StrDefineType(defines[i].type));
-                fprintf(outFile, "  Value: %s\n", defines[i].value);
-                fprintf(outFile, "  Description: %s\n", defines[i].desc);
-            }
-
-            // Print structs info
-            fprintf(outFile, "\nStructures found: %i\n\n", structCount);
-            for (int i = 0; i < structCount; i++)
-            {
-                fprintf(outFile, "Struct %02i: %s (%i fields)\n", i + 1, structs[i].name, structs[i].fieldCount);
-                fprintf(outFile, "  Name: %s\n", structs[i].name);
-                fprintf(outFile, "  Description: %s\n", structs[i].desc);
-                for (int f = 0; f < structs[i].fieldCount; f++)
-                {
-                    fprintf(outFile, "  Field[%i]: %s %s ", f + 1, structs[i].fieldType[f], structs[i].fieldName[f]);
-                    if (structs[i].fieldDesc[f][0]) fprintf(outFile, "// %s\n", structs[i].fieldDesc[f]);
-                    else fprintf(outFile, "\n");
-                }
-            }
-
-            // Print aliases info
-            fprintf(outFile, "\nAliases found: %i\n\n", aliasCount);
-            for (int i = 0; i < aliasCount; i++)
-            {
-                fprintf(outFile, "Alias %03i: %s\n", i + 1, aliases[i].name);
-                fprintf(outFile, "  Type: %s\n", aliases[i].type);
-                fprintf(outFile, "  Name: %s\n", aliases[i].name);
-                fprintf(outFile, "  Description: %s\n", aliases[i].desc);
-            }
-
-            // Print enums info
-            fprintf(outFile, "\nEnums found: %i\n\n", enumCount);
-            for (int i = 0; i < enumCount; i++)
-            {
-                fprintf(outFile, "Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount);
-                fprintf(outFile, "  Name: %s\n", enums[i].name);
-                fprintf(outFile, "  Description: %s\n", enums[i].desc);
-                for (int e = 0; e < enums[i].valueCount; e++) fprintf(outFile, "  Value[%s]: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]);
-            }
-
-            // Print callbacks info
-            fprintf(outFile, "\nCallbacks found: %i\n\n", callbackCount);
-            for (int i = 0; i < callbackCount; i++)
-            {
-                fprintf(outFile, "Callback %03i: %s() (%i input parameters)\n", i + 1, callbacks[i].name, callbacks[i].paramCount);
-                fprintf(outFile, "  Name: %s\n", callbacks[i].name);
-                fprintf(outFile, "  Return type: %s\n", callbacks[i].retType);
-                fprintf(outFile, "  Description: %s\n", callbacks[i].desc);
-                for (int p = 0; p < callbacks[i].paramCount; p++) fprintf(outFile, "  Param[%i]: %s (type: %s)\n", p + 1, callbacks[i].paramName[p], callbacks[i].paramType[p]);
-                if (callbacks[i].paramCount == 0) fprintf(outFile, "  No input parameters\n");
-            }
-
-            // Print functions info
-            fprintf(outFile, "\nFunctions found: %i\n\n", funcCount);
-            for (int i = 0; i < funcCount; i++)
-            {
-                fprintf(outFile, "Function %03i: %s() (%i input parameters)\n", i + 1, funcs[i].name, funcs[i].paramCount);
-                fprintf(outFile, "  Name: %s\n", funcs[i].name);
-                fprintf(outFile, "  Return type: %s\n", funcs[i].retType);
-                fprintf(outFile, "  Description: %s\n", funcs[i].desc);
-                for (int p = 0; p < funcs[i].paramCount; p++) fprintf(outFile, "  Param[%i]: %s (type: %s)\n", p + 1, funcs[i].paramName[p], funcs[i].paramType[p]);
-                if (funcs[i].paramCount == 0) fprintf(outFile, "  No input parameters\n");
-            }
-        } break;
-        case JSON:
-        {
-            fprintf(outFile, "{\n");
-
-            // Print defines info
-            fprintf(outFile, "  \"defines\": [\n");
-            for (int i = 0; i < defineCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"name\": \"%s\",\n", defines[i].name);
-                fprintf(outFile, "      \"type\": \"%s\",\n", StrDefineType(defines[i].type));
-                if (defines[i].isHex) // INT or LONG
-                {
-                    fprintf(outFile, "      \"value\": %ld,\n", strtol(defines[i].value, NULL, 16));
-                }
-                else if ((defines[i].type == INT) ||
-                         (defines[i].type == LONG) ||
-                         (defines[i].type == FLOAT) ||
-                         (defines[i].type == DOUBLE) ||
-                         (defines[i].type == STRING))
-                {
-                    fprintf(outFile, "      \"value\": %s,\n", defines[i].value);
-                }
-                else
-                {
-                    fprintf(outFile, "      \"value\": \"%s\",\n", defines[i].value);
-                }
-                fprintf(outFile, "      \"description\": \"%s\"\n", defines[i].desc);
-                fprintf(outFile, "    }");
-
-                if (i < defineCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ],\n");
-
-            // Print structs info
-            fprintf(outFile, "  \"structs\": [\n");
-            for (int i = 0; i < structCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"name\": \"%s\",\n", structs[i].name);
-                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(structs[i].desc));
-                fprintf(outFile, "      \"fields\": [\n");
-                for (int f = 0; f < structs[i].fieldCount; f++)
-                {
-                    fprintf(outFile, "        {\n");
-                    fprintf(outFile, "          \"type\": \"%s\",\n", structs[i].fieldType[f]);
-                    fprintf(outFile, "          \"name\": \"%s\",\n", structs[i].fieldName[f]);
-                    fprintf(outFile, "          \"description\": \"%s\"\n", EscapeBackslashes(structs[i].fieldDesc[f]));
-                    fprintf(outFile, "        }");
-                    if (f < structs[i].fieldCount - 1) fprintf(outFile, ",\n");
-                    else fprintf(outFile, "\n");
-                }
-                fprintf(outFile, "      ]\n");
-                fprintf(outFile, "    }");
-                if (i < structCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ],\n");
-
-            // Print aliases info
-            fprintf(outFile, "  \"aliases\": [\n");
-            for (int i = 0; i < aliasCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"type\": \"%s\",\n", aliases[i].type);
-                fprintf(outFile, "      \"name\": \"%s\",\n", aliases[i].name);
-                fprintf(outFile, "      \"description\": \"%s\"\n", aliases[i].desc);
-                fprintf(outFile, "    }");
-
-                if (i < aliasCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ],\n");
-
-            // Print enums info
-            fprintf(outFile, "  \"enums\": [\n");
-            for (int i = 0; i < enumCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"name\": \"%s\",\n", enums[i].name);
-                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(enums[i].desc));
-                fprintf(outFile, "      \"values\": [\n");
-                for (int e = 0; e < enums[i].valueCount; e++)
-                {
-                    fprintf(outFile, "        {\n");
-                    fprintf(outFile, "          \"name\": \"%s\",\n", enums[i].valueName[e]);
-                    fprintf(outFile, "          \"value\": %i,\n", enums[i].valueInteger[e]);
-                    fprintf(outFile, "          \"description\": \"%s\"\n", EscapeBackslashes(enums[i].valueDesc[e]));
-                    fprintf(outFile, "        }");
-                    if (e < enums[i].valueCount - 1) fprintf(outFile, ",\n");
-                    else fprintf(outFile, "\n");
-                }
-                fprintf(outFile, "      ]\n");
-                fprintf(outFile, "    }");
-                if (i < enumCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ],\n");
-
-            // Print callbacks info
-            fprintf(outFile, "  \"callbacks\": [\n");
-            for (int i = 0; i < callbackCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"name\": \"%s\",\n", callbacks[i].name);
-                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(callbacks[i].desc));
-                fprintf(outFile, "      \"returnType\": \"%s\"", callbacks[i].retType);
-
-                if (callbacks[i].paramCount == 0) fprintf(outFile, "\n");
-                else
-                {
-                    fprintf(outFile, ",\n      \"params\": [\n");
-                    for (int p = 0; p < callbacks[i].paramCount; p++)
-                    {
-                        fprintf(outFile, "        {\n");
-                        fprintf(outFile, "          \"type\": \"%s\",\n", callbacks[i].paramType[p]);
-                        fprintf(outFile, "          \"name\": \"%s\"\n", callbacks[i].paramName[p]);
-                        fprintf(outFile, "        }");
-                        if (p < callbacks[i].paramCount - 1) fprintf(outFile, ",\n");
-                        else fprintf(outFile, "\n");
-                    }
-                    fprintf(outFile, "      ]\n");
-                }
-                fprintf(outFile, "    }");
-
-                if (i < callbackCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ],\n");
-
-            // Print functions info
-            fprintf(outFile, "  \"functions\": [\n");
-            for (int i = 0; i < funcCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      \"name\": \"%s\",\n", funcs[i].name);
-                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(funcs[i].desc));
-                fprintf(outFile, "      \"returnType\": \"%s\"", funcs[i].retType);
-
-                if (funcs[i].paramCount == 0) fprintf(outFile, "\n");
-                else
-                {
-                    fprintf(outFile, ",\n      \"params\": [\n");
-                    for (int p = 0; p < funcs[i].paramCount; p++)
-                    {
-                        fprintf(outFile, "        {\n");
-                        fprintf(outFile, "          \"type\": \"%s\",\n", funcs[i].paramType[p]);
-                        fprintf(outFile, "          \"name\": \"%s\"\n", funcs[i].paramName[p]);
-                        fprintf(outFile, "        }");
-                        if (p < funcs[i].paramCount - 1) fprintf(outFile, ",\n");
-                        else fprintf(outFile, "\n");
-                    }
-                    fprintf(outFile, "      ]\n");
-                }
-                fprintf(outFile, "    }");
-
-                if (i < funcCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  ]\n");
-            fprintf(outFile, "}\n");
-        } break;
-        case XML:
-        {
-            // XML format to export data:
-            /*
-            <?xml version="1.0" encoding="Windows-1252" ?>
-            <raylibAPI>
-                <Defines count="">
-                    <Define name="" type="" value="" desc="" />
-                </Defines>
-                <Structs count="">
-                    <Struct name="" fieldCount="" desc="">
-                        <Field type="" name="" desc="" />
-                        <Field type="" name="" desc="" />
-                    </Struct>
-                <Structs>
-                <Aliases count="">
-                    <Alias type="" name="" desc="" />
-                </Aliases>
-                <Enums count="">
-                    <Enum name="" valueCount="" desc="">
-                        <Value name="" integer="" desc="" />
-                        <Value name="" integer="" desc="" />
-                    </Enum>
-                </Enums>
-                <Callbacks count="">
-                    <Callback name="" retType="" paramCount="" desc="">
-                        <Param type="" name="" desc="" />
-                        <Param type="" name="" desc="" />
-                    </Callback>
-                </Callbacks>
-                <Functions count="">
-                    <Function name="" retType="" paramCount="" desc="">
-                        <Param type="" name="" desc="" />
-                        <Param type="" name="" desc="" />
-                    </Function>
-                </Functions>
-            </raylibAPI>
-            */
-
-            fprintf(outFile, "<?xml version=\"1.0\" encoding=\"Windows-1252\" ?>\n");
-            fprintf(outFile, "<raylibAPI>\n");
-
-            // Print defines info
-            fprintf(outFile, "    <Defines count=\"%i\">\n", defineCount);
-            for (int i = 0; i < defineCount; i++)
-            {
-                fprintf(outFile, "        <Define name=\"%s\" type=\"%s\" ", defines[i].name, StrDefineType(defines[i].type));
-                if (defines[i].type == STRING)
-                {
-                    fprintf(outFile, "value=%s", defines[i].value);
-                }
-                else
-                {
-                    fprintf(outFile, "value=\"%s\"", defines[i].value);
-                }
-                fprintf(outFile, " desc=\"%s\" />\n", defines[i].desc);
-            }
-            fprintf(outFile, "    </Defines>\n");
-
-            // Print structs info
-            fprintf(outFile, "    <Structs count=\"%i\">\n", structCount);
-            for (int i = 0; i < structCount; i++)
-            {
-                fprintf(outFile, "        <Struct name=\"%s\" fieldCount=\"%i\" desc=\"%s\">\n", structs[i].name, structs[i].fieldCount, structs[i].desc);
-                for (int f = 0; f < structs[i].fieldCount; f++)
-                {
-                    fprintf(outFile, "            <Field type=\"%s\" name=\"%s\" desc=\"%s\" />\n", structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f]);
-                }
-                fprintf(outFile, "        </Struct>\n");
-            }
-            fprintf(outFile, "    </Structs>\n");
-
-            // Print aliases info
-            fprintf(outFile, "    <Aliases count=\"%i\">\n", aliasCount);
-            for (int i = 0; i < aliasCount; i++)
-            {
-                fprintf(outFile, "        <Alias type=\"%s\" name=\"%s\" desc=\"%s\" />\n", aliases[i].name, aliases[i].type, aliases[i].desc);
-            }
-            fprintf(outFile, "    </Aliases>\n");
-
-            // Print enums info
-            fprintf(outFile, "    <Enums count=\"%i\">\n", enumCount);
-            for (int i = 0; i < enumCount; i++)
-            {
-                fprintf(outFile, "        <Enum name=\"%s\" valueCount=\"%i\" desc=\"%s\">\n", enums[i].name, enums[i].valueCount, enums[i].desc);
-                for (int v = 0; v < enums[i].valueCount; v++)
-                {
-                    fprintf(outFile, "            <Value name=\"%s\" integer=\"%i\" desc=\"%s\" />\n", enums[i].valueName[v], enums[i].valueInteger[v], enums[i].valueDesc[v]);
-                }
-                fprintf(outFile, "        </Enum>\n");
-            }
-            fprintf(outFile, "    </Enums>\n");
-
-            // Print callbacks info
-            fprintf(outFile, "    <Callbacks count=\"%i\">\n", callbackCount);
-            for (int i = 0; i < callbackCount; i++)
-            {
-                fprintf(outFile, "        <Callback name=\"%s\" retType=\"%s\" paramCount=\"%i\" desc=\"%s\">\n", callbacks[i].name, callbacks[i].retType, callbacks[i].paramCount, callbacks[i].desc);
-                for (int p = 0; p < callbacks[i].paramCount; p++)
-                {
-                    fprintf(outFile, "            <Param type=\"%s\" name=\"%s\" desc=\"%s\" />\n", callbacks[i].paramType[p], callbacks[i].paramName[p], callbacks[i].paramDesc[p]);
-                }
-                fprintf(outFile, "        </Callback>\n");
-            }
-            fprintf(outFile, "    </Callbacks>\n");
-
-            // Print functions info
-            fprintf(outFile, "    <Functions count=\"%i\">\n", funcCount);
-            for (int i = 0; i < funcCount; i++)
-            {
-                fprintf(outFile, "        <Function name=\"%s\" retType=\"%s\" paramCount=\"%i\" desc=\"%s\">\n", funcs[i].name, funcs[i].retType, funcs[i].paramCount, funcs[i].desc);
-                for (int p = 0; p < funcs[i].paramCount; p++)
-                {
-                    fprintf(outFile, "            <Param type=\"%s\" name=\"%s\" desc=\"%s\" />\n", funcs[i].paramType[p], funcs[i].paramName[p], funcs[i].paramDesc[p]);
-                }
-                fprintf(outFile, "        </Function>\n");
-            }
-            fprintf(outFile, "    </Functions>\n");
-
-            fprintf(outFile, "</raylibAPI>\n");
-
-        } break;
-        case LUA:
-        {
-            fprintf(outFile, "return {\n");
-
-            // Print defines info
-            fprintf(outFile, "  defines = {\n");
-            for (int i = 0; i < defineCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      name = \"%s\",\n", defines[i].name);
-                fprintf(outFile, "      type = \"%s\",\n", StrDefineType(defines[i].type));
-                if ((defines[i].type == INT) ||
-                    (defines[i].type == LONG) ||
-                    (defines[i].type == FLOAT) ||
-                    (defines[i].type == DOUBLE) ||
-                    (defines[i].type == STRING))
-                {
-                    fprintf(outFile, "      value = %s,\n", defines[i].value);
-                }
-                else
-                {
-                    fprintf(outFile, "      value = \"%s\",\n", defines[i].value);
-                }
-                fprintf(outFile, "      description = \"%s\"\n", defines[i].desc);
-                fprintf(outFile, "    }");
-
-                if (i < defineCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  },\n");
-
-            // Print structs info
-            fprintf(outFile, "  structs = {\n");
-            for (int i = 0; i < structCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      name = \"%s\",\n", structs[i].name);
-                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(structs[i].desc));
-                fprintf(outFile, "      fields = {\n");
-                for (int f = 0; f < structs[i].fieldCount; f++)
-                {
-                    fprintf(outFile, "        {\n");
-                    fprintf(outFile, "          type = \"%s\",\n", structs[i].fieldType[f]);
-                    fprintf(outFile, "          name = \"%s\",\n", structs[i].fieldName[f]);
-                    fprintf(outFile, "          description = \"%s\"\n", EscapeBackslashes(structs[i].fieldDesc[f]));
-                    fprintf(outFile, "        }");
-                    if (f < structs[i].fieldCount - 1) fprintf(outFile, ",\n");
-                    else fprintf(outFile, "\n");
-                }
-                fprintf(outFile, "      }\n");
-                fprintf(outFile, "    }");
-                if (i < structCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  },\n");
-
-            // Print aliases info
-            fprintf(outFile, "  aliases = {\n");
-            for (int i = 0; i < aliasCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      type = \"%s\",\n", aliases[i].type);
-                fprintf(outFile, "      name = \"%s\",\n", aliases[i].name);
-                fprintf(outFile, "      description = \"%s\"\n", aliases[i].desc);
-                fprintf(outFile, "    }");
-
-                if (i < aliasCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  },\n");
-
-            // Print enums info
-            fprintf(outFile, "  enums = {\n");
-            for (int i = 0; i < enumCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      name = \"%s\",\n", enums[i].name);
-                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(enums[i].desc));
-                fprintf(outFile, "      values = {\n");
-                for (int e = 0; e < enums[i].valueCount; e++)
-                {
-                    fprintf(outFile, "        {\n");
-                    fprintf(outFile, "          name = \"%s\",\n", enums[i].valueName[e]);
-                    fprintf(outFile, "          value = %i,\n", enums[i].valueInteger[e]);
-                    fprintf(outFile, "          description = \"%s\"\n", EscapeBackslashes(enums[i].valueDesc[e]));
-                    fprintf(outFile, "        }");
-                    if (e < enums[i].valueCount - 1) fprintf(outFile, ",\n");
-                    else fprintf(outFile, "\n");
-                }
-                fprintf(outFile, "      }\n");
-                fprintf(outFile, "    }");
-                if (i < enumCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  },\n");
-
-            // Print callbacks info
-            fprintf(outFile, "  callbacks = {\n");
-            for (int i = 0; i < callbackCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      name = \"%s\",\n", callbacks[i].name);
-                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(callbacks[i].desc));
-                fprintf(outFile, "      returnType = \"%s\"", callbacks[i].retType);
-
-                if (callbacks[i].paramCount == 0) fprintf(outFile, "\n");
-                else
-                {
-                    fprintf(outFile, ",\n      params = {\n");
-                    for (int p = 0; p < callbacks[i].paramCount; p++)
-                    {
-                        fprintf(outFile, "        {type = \"%s\", name = \"%s\"}", callbacks[i].paramType[p], callbacks[i].paramName[p]);
-                        if (p < callbacks[i].paramCount - 1) fprintf(outFile, ",\n");
-                        else fprintf(outFile, "\n");
-                    }
-                    fprintf(outFile, "      }\n");
-                }
-                fprintf(outFile, "    }");
-
-                if (i < callbackCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  },\n");
-
-            // Print functions info
-            fprintf(outFile, "  functions = {\n");
-            for (int i = 0; i < funcCount; i++)
-            {
-                fprintf(outFile, "    {\n");
-                fprintf(outFile, "      name = \"%s\",\n", funcs[i].name);
-                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(funcs[i].desc));
-                fprintf(outFile, "      returnType = \"%s\"", funcs[i].retType);
-
-                if (funcs[i].paramCount == 0) fprintf(outFile, "\n");
-                else
-                {
-                    fprintf(outFile, ",\n      params = {\n");
-                    for (int p = 0; p < funcs[i].paramCount; p++)
-                    {
-                        fprintf(outFile, "        {type = \"%s\", name = \"%s\"}", funcs[i].paramType[p], funcs[i].paramName[p]);
-                        if (p < funcs[i].paramCount - 1) fprintf(outFile, ",\n");
-                        else fprintf(outFile, "\n");
-                    }
-                    fprintf(outFile, "      }\n");
-                }
-                fprintf(outFile, "    }");
-
-                if (i < funcCount - 1) fprintf(outFile, ",\n");
-                else fprintf(outFile, "\n");
-            }
-            fprintf(outFile, "  }\n");
-            fprintf(outFile, "}\n");
-        } break;
-        case CODE:
-        default: break;
-    }
-
-    fclose(outFile);
-}
diff --git a/raylib/projects/4coder/main.c b/raylib/projects/4coder/main.c
--- a/raylib/projects/4coder/main.c
+++ b/raylib/projects/4coder/main.c
@@ -18,8 +18,8 @@
     SetTargetFPS(60);
 
     while (!WindowShouldClose()) {
-        cam.position.x = sin(GetTime()) * 10.0f;
-        cam.position.z = cos(GetTime()) * 10.0f;
+        cam.position.x = sin(GetTime())*10.0f;
+        cam.position.z = cos(GetTime())*10.0f;
 
         BeginDrawing();
             ClearBackground(RAYWHITE);
diff --git a/raylib/projects/CMake/core_basic_window.c b/raylib/projects/CMake/core_basic_window.c
--- a/raylib/projects/CMake/core_basic_window.c
+++ b/raylib/projects/CMake/core_basic_window.c
@@ -31,7 +31,7 @@
 void UpdateDrawFrame(void);     // Update and Draw one frame
 
 //----------------------------------------------------------------------------------
-// Main Entry Point
+// Program main entry point
 //----------------------------------------------------------------------------------
 int main()
 {
diff --git a/raylib/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/raylib/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
--- a/raylib/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
+++ b/raylib/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
@@ -8,36 +8,36 @@
 RLAPI bool WindowShouldClose(void);                               // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
 RLAPI bool IsWindowReady(void);                                   // Check if window has been initialized successfully
 RLAPI bool IsWindowFullscreen(void);                              // Check if window is currently fullscreen
-RLAPI bool IsWindowHidden(void);                                  // Check if window is currently hidden (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowMinimized(void);                               // Check if window is currently minimized (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowMaximized(void);                               // Check if window is currently maximized (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowFocused(void);                                 // Check if window is currently focused (only PLATFORM_DESKTOP)
+RLAPI bool IsWindowHidden(void);                                  // Check if window is currently hidden
+RLAPI bool IsWindowMinimized(void);                               // Check if window is currently minimized
+RLAPI bool IsWindowMaximized(void);                               // Check if window is currently maximized
+RLAPI bool IsWindowFocused(void);                                 // Check if window is currently focused
 RLAPI bool IsWindowResized(void);                                 // Check if window has been resized last frame
 RLAPI bool IsWindowState(unsigned int flag);                      // Check if one specific window flag is enabled
-RLAPI void SetWindowState(unsigned int flags);                    // Set window configuration state using flags (only PLATFORM_DESKTOP)
+RLAPI void SetWindowState(unsigned int flags);                    // Set window configuration state using flags
 RLAPI void ClearWindowState(unsigned int flags);                  // Clear window configuration state flags
-RLAPI void ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)
-RLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed (only PLATFORM_DESKTOP)
-RLAPI void MaximizeWindow(void);                                  // Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
-RLAPI void MinimizeWindow(void);                                  // Set window state: minimized, if resizable (only PLATFORM_DESKTOP)
-RLAPI void RestoreWindow(void);                                   // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
-RLAPI void SetWindowIcon(Image image);                            // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)
-RLAPI void SetWindowIcons(Image *images, int count);              // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)
-RLAPI void SetWindowTitle(const char *title);                     // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)
-RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
+RLAPI void ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution
+RLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed, resizes window to match monitor resolution
+RLAPI void MaximizeWindow(void);                                  // Set window state: maximized, if resizable
+RLAPI void MinimizeWindow(void);                                  // Set window state: minimized, if resizable
+RLAPI void RestoreWindow(void);                                   // Restore window from being minimized/maximized
+RLAPI void SetWindowIcon(Image image);                            // Set icon for window (single image, RGBA 32bit)
+RLAPI void SetWindowIcons(Image *images, int count);              // Set icon for window (multiple images, RGBA 32bit)
+RLAPI void SetWindowTitle(const char *title);                     // Set title for window
+RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowMaxSize(int width, int height);               // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowSize(int width, int height);                  // Set window dimensions
-RLAPI void SetWindowOpacity(float opacity);                       // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
-RLAPI void SetWindowFocused(void);                                // Set window focused (only PLATFORM_DESKTOP)
+RLAPI void SetWindowOpacity(float opacity);                       // Set window opacity [0.0f..1.0f]
+RLAPI void SetWindowFocused(void);                                // Set window focused
 RLAPI void *GetWindowHandle(void);                                // Get native window handle
 RLAPI int GetScreenWidth(void);                                   // Get current screen width
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 RLAPI int GetRenderWidth(void);                                   // Get current render width (it considers HiDPI)
 RLAPI int GetRenderHeight(void);                                  // Get current render height (it considers HiDPI)
 RLAPI int GetMonitorCount(void);                                  // Get number of connected monitors
-RLAPI int GetCurrentMonitor(void);                                // Get current connected monitor
+RLAPI int GetCurrentMonitor(void);                                // Get current monitor where window is placed
 RLAPI Vector2 GetMonitorPosition(int monitor);                    // Get specified monitor position
 RLAPI int GetMonitorWidth(int monitor);                           // Get specified monitor width (current video mode used by monitor)
 RLAPI int GetMonitorHeight(int monitor);                          // Get specified monitor height (current video mode used by monitor)
@@ -49,6 +49,7 @@
 RLAPI const char *GetMonitorName(int monitor);                    // Get the human-readable, UTF-8 encoded name of the specified monitor
 RLAPI void SetClipboardText(const char *text);                    // Set clipboard text content
 RLAPI const char *GetClipboardText(void);                         // Get clipboard text content
+RLAPI Image GetClipboardImage(void);                              // Get clipboard image
 RLAPI void EnableEventWaiting(void);                              // Enable waiting for events on EndDrawing(), no automatic event polling
 RLAPI void DisableEventWaiting(void);                             // Disable waiting for events on EndDrawing(), automatic events polling
 
@@ -87,7 +88,7 @@
 // NOTE: Shader functionality is not available on OpenGL 1.1
 RLAPI Shader LoadShader(const char *vsFileName, const char *fsFileName);   // Load shader from files and bind default locations
 RLAPI Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode); // Load shader from code strings and bind default locations
-RLAPI bool IsShaderReady(Shader shader);                                   // Check if a shader is ready
+RLAPI bool IsShaderValid(Shader shader);                                   // Check if a shader is valid (loaded on GPU)
 RLAPI int GetShaderLocation(Shader shader, const char *uniformName);       // Get shader uniform location
 RLAPI int GetShaderLocationAttrib(Shader shader, const char *attribName);  // Get shader attribute location
 RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType);               // Set shader uniform value
@@ -97,13 +98,15 @@
 RLAPI void UnloadShader(Shader shader);                                    // Unload shader from GPU memory (VRAM)
 
 // Screen-space-related functions
-RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera);      // Get a ray trace from mouse position
-RLAPI Matrix GetCameraMatrix(Camera camera);                      // Get camera transform matrix (view matrix)
-RLAPI Matrix GetCameraMatrix2D(Camera2D camera);                  // Get camera 2d transform matrix
-RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera);  // Get the screen space position for a 3d world space position
-RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera); // Get the world space position for a 2d camera screen space position
+#define GetMouseRay GetScreenToWorldRay     // Compatibility hack for previous raylib versions
+RLAPI Ray GetScreenToWorldRay(Vector2 position, Camera camera);         // Get a ray trace from screen position (i.e mouse)
+RLAPI Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height); // Get a ray trace from screen position (i.e mouse) in a viewport
+RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera);        // Get the screen space position for a 3d world space position
 RLAPI Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height); // Get size position for a 3d world space position
-RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the screen space position for a 2d camera world space position
+RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera);    // Get the screen space position for a 2d camera world space position
+RLAPI Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera);    // Get the world space position for a 2d camera screen space position
+RLAPI Matrix GetCameraMatrix(Camera camera);                            // Get camera transform matrix (view matrix)
+RLAPI Matrix GetCameraMatrix2D(Camera2D camera);                        // Get camera 2d transform matrix
 
 // Timing-related functions
 RLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)
@@ -112,6 +115,9 @@
 RLAPI int GetFPS(void);                                           // Get current FPS
 
 // Custom frame control functions
+// NOTE: Those functions are intended for advanced users that want full control over the frame processing
+// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
+// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
 RLAPI void SwapScreenBuffer(void);                                // Swap back buffer with front buffer (screen drawing)
 RLAPI void PollInputEvents(void);                                 // Register all input events
 RLAPI void WaitTime(double seconds);                              // Wait for some time (halt program execution)
@@ -127,6 +133,8 @@
 RLAPI void SetConfigFlags(unsigned int flags);                    // Setup init configuration flags (view FLAGS)
 RLAPI void OpenURL(const char *url);                              // Open URL with default system browser (if available)
 
+// NOTE: Following functions implemented in module [utils]
+//------------------------------------------------------------------
 RLAPI void TraceLog(int logLevel, const char *text, ...);         // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
 RLAPI void SetTraceLogLevel(int logLevel);                        // Set the current threshold (minimum) log level
 RLAPI void *MemAlloc(unsigned int size);                          // Internal memory allocator
@@ -149,6 +157,7 @@
 RLAPI char *LoadFileText(const char *fileName);                   // Load text data from file (read), returns a '\0' terminated string
 RLAPI void UnloadFileText(char *text);                            // Unload file text data allocated by LoadFileText()
 RLAPI bool SaveFileText(const char *fileName, char *text);        // Save text data to file (write), string must be '\0' terminated, returns true on success
+//------------------------------------------------------------------
 
 // File system functions
 RLAPI bool FileExists(const char *fileName);                      // Check if file exists
@@ -162,10 +171,12 @@
 RLAPI const char *GetPrevDirectoryPath(const char *dirPath);      // Get previous directory path for a given path (uses static string)
 RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory (uses static string)
 RLAPI const char *GetApplicationDirectory(void);                  // Get the directory of the running application (uses static string)
+RLAPI int MakeDirectory(const char *dirPath);                     // Create directories (including full path requested), returns 0 on success
 RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, return true on success
 RLAPI bool IsPathFile(const char *path);                          // Check if a given path is a file or a directory
+RLAPI bool IsFileNameValid(const char *fileName);                 // Check if fileName is valid for the platform/OS
 RLAPI FilePathList LoadDirectoryFiles(const char *dirPath);       // Load directory filepaths
-RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan
+RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
 RLAPI void UnloadDirectoryFiles(FilePathList files);              // Unload filepaths
 RLAPI bool IsFileDropped(void);                                   // Check if a file has been dropped into window
 RLAPI FilePathList LoadDroppedFiles(void);                        // Load dropped filepaths
@@ -177,10 +188,14 @@
 RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize);  // Decompress data (DEFLATE algorithm), memory must be MemFree()
 RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize);               // Encode data to Base64 string, memory must be MemFree()
 RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize);                    // Decode Base64 string data, memory must be MemFree()
+RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize);     // Compute CRC32 hash code
+RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize);      // Compute MD5 hash code, returns static int[4] (16 bytes)
+RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize);     // Compute SHA1 hash code, returns static int[5] (20 bytes)
 
+
 // Automation events functionality
 RLAPI AutomationEventList LoadAutomationEventList(const char *fileName);                // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
-RLAPI void UnloadAutomationEventList(AutomationEventList *list);                        // Unload automation events list from file
+RLAPI void UnloadAutomationEventList(AutomationEventList list);                         // Unload automation events list from file
 RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName);   // Export automation events list as text file
 RLAPI void SetAutomationEventList(AutomationEventList *list);                           // Set automation event list to record to
 RLAPI void SetAutomationEventBaseFrame(int frame);                                      // Set automation event internal base frame to start recording
@@ -194,25 +209,27 @@
 
 // Input-related functions: keyboard
 RLAPI bool IsKeyPressed(int key);                             // Check if a key has been pressed once
-RLAPI bool IsKeyPressedRepeat(int key);                       // Check if a key has been pressed again (Only PLATFORM_DESKTOP)
+RLAPI bool IsKeyPressedRepeat(int key);                       // Check if a key has been pressed again
 RLAPI bool IsKeyDown(int key);                                // Check if a key is being pressed
 RLAPI bool IsKeyReleased(int key);                            // Check if a key has been released once
 RLAPI bool IsKeyUp(int key);                                  // Check if a key is NOT being pressed
 RLAPI int GetKeyPressed(void);                                // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
 RLAPI int GetCharPressed(void);                               // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
 RLAPI void SetExitKey(int key);                               // Set a custom key to exit program (default is ESC)
+RLAPI const char *GetKeyName(int key);                        // Get name of a QWERTY key on the current keyboard layout (eg returns string "q" for KEY_A on an AZERTY keyboard)
 
 // Input-related functions: gamepads
-RLAPI bool IsGamepadAvailable(int gamepad);                   // Check if a gamepad is available
-RLAPI const char *GetGamepadName(int gamepad);                // Get gamepad internal name id
-RLAPI bool IsGamepadButtonPressed(int gamepad, int button);   // Check if a gamepad button has been pressed once
-RLAPI bool IsGamepadButtonDown(int gamepad, int button);      // Check if a gamepad button is being pressed
-RLAPI bool IsGamepadButtonReleased(int gamepad, int button);  // Check if a gamepad button has been released once
-RLAPI bool IsGamepadButtonUp(int gamepad, int button);        // Check if a gamepad button is NOT being pressed
-RLAPI int GetGamepadButtonPressed(void);                      // Get the last gamepad button pressed
-RLAPI int GetGamepadAxisCount(int gamepad);                   // Get gamepad axis count for a gamepad
-RLAPI float GetGamepadAxisMovement(int gamepad, int axis);    // Get axis movement value for a gamepad axis
-RLAPI int SetGamepadMappings(const char *mappings);           // Set internal gamepad mappings (SDL_GameControllerDB)
+RLAPI bool IsGamepadAvailable(int gamepad);                                        // Check if a gamepad is available
+RLAPI const char *GetGamepadName(int gamepad);                                     // Get gamepad internal name id
+RLAPI bool IsGamepadButtonPressed(int gamepad, int button);                        // Check if a gamepad button has been pressed once
+RLAPI bool IsGamepadButtonDown(int gamepad, int button);                           // Check if a gamepad button is being pressed
+RLAPI bool IsGamepadButtonReleased(int gamepad, int button);                       // Check if a gamepad button has been released once
+RLAPI bool IsGamepadButtonUp(int gamepad, int button);                             // Check if a gamepad button is NOT being pressed
+RLAPI int GetGamepadButtonPressed(void);                                           // Get the last gamepad button pressed
+RLAPI int GetGamepadAxisCount(int gamepad);                                        // Get gamepad axis count for a gamepad
+RLAPI float GetGamepadAxisMovement(int gamepad, int axis);                         // Get axis movement value for a gamepad axis
+RLAPI int SetGamepadMappings(const char *mappings);                                // Set internal gamepad mappings (SDL_GameControllerDB)
+RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds)
 
 // Input-related functions: mouse
 RLAPI bool IsMouseButtonPressed(int button);                  // Check if a mouse button has been pressed once
@@ -243,7 +260,7 @@
 RLAPI void SetGesturesEnabled(unsigned int flags);      // Enable a set of gestures using flags
 RLAPI bool IsGestureDetected(unsigned int gesture);     // Check if a gesture have been detected
 RLAPI int GetGestureDetected(void);                     // Get latest detected gesture
-RLAPI float GetGestureHoldDuration(void);               // Get gesture hold time in milliseconds
+RLAPI float GetGestureHoldDuration(void);               // Get gesture hold time in seconds
 RLAPI Vector2 GetGestureDragVector(void);               // Get gesture drag vector
 RLAPI float GetGestureDragAngle(void);                  // Get gesture drag angle
 RLAPI Vector2 GetGesturePinchVector(void);              // Get gesture pinch delta
@@ -262,19 +279,21 @@
 // NOTE: It can be useful when using basic shapes and one single font,
 // defining a font char white rectangle would allow drawing everything in a single draw call
 RLAPI void SetShapesTexture(Texture2D texture, Rectangle source);       // Set texture and rectangle to be used on shapes drawing
+RLAPI Texture2D GetShapesTexture(void);                                 // Get texture that is used for shapes drawing
+RLAPI Rectangle GetShapesTextureRectangle(void);                        // Get texture source rectangle that is used for shapes drawing
 
 // Basic shapes drawing functions
-RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel
-RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel (Vector version)
+RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel using geometry [Can be slow, use with care]
+RLAPI void DrawPixelV(Vector2 position, Color color);                                                    // Draw a pixel using geometry (Vector version) [Can be slow, use with care]
 RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);                // Draw a line
 RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color);                                     // Draw a line (using gl lines)
 RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line (using triangles/quads)
-RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color);                                  // Draw lines sequence (using gl lines)
+RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color);                            // Draw lines sequence (using gl lines)
 RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color);                   // Draw line segment cubic-bezier in-out interpolation
 RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color);                              // Draw a color-filled circle
 RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color);      // Draw a piece of a circle
 RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
-RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2);       // Draw a gradient-filled circle
+RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color inner, Color outer);         // Draw a gradient-filled circle
 RLAPI void DrawCircleV(Vector2 center, float radius, Color color);                                       // Draw a color-filled circle (Vector version)
 RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color);                         // Draw circle outline
 RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color);                                  // Draw circle outline (Vector version)
@@ -286,27 +305,28 @@
 RLAPI void DrawRectangleV(Vector2 position, Vector2 size, Color color);                                  // Draw a color-filled rectangle (Vector version)
 RLAPI void DrawRectangleRec(Rectangle rec, Color color);                                                 // Draw a color-filled rectangle
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
-RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a vertical-gradient-filled rectangle
-RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2);// Draw a horizontal-gradient-filled rectangle
-RLAPI void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4);       // Draw a gradient-filled rectangle with custom vertex colors
+RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom);   // Draw a vertical-gradient-filled rectangle
+RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right);   // Draw a horizontal-gradient-filled rectangle
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
 RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color);                            // Draw rectangle outline with extended parameters
 RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color);              // Draw rectangle with rounded edges
-RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline
+RLAPI void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color);         // Draw rectangle lines with rounded edges
+RLAPI void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float lineThick, Color color); // Draw rectangle with rounded edges outline
 RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                                // Draw a color-filled triangle (vertex in counter-clockwise order!)
 RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color);                           // Draw triangle outline (vertex in counter-clockwise order!)
-RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color);                                // Draw a triangle fan defined by points (first vertex is the center)
-RLAPI void DrawTriangleStrip(Vector2 *points, int pointCount, Color color);                              // Draw a triangle strip defined by points
+RLAPI void DrawTriangleFan(const Vector2 *points, int pointCount, Color color);                          // Draw a triangle fan defined by points (first vertex is the center)
+RLAPI void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color);                        // Draw a triangle strip defined by points
 RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color);               // Draw a regular polygon (Vector version)
 RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);          // Draw a polygon outline of n sides
 RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters
 
 // Splines drawing functions
-RLAPI void DrawSplineLinear(Vector2 *points, int pointCount, float thick, Color color);                  // Draw spline: Linear, minimum 2 points
-RLAPI void DrawSplineBasis(Vector2 *points, int pointCount, float thick, Color color);                   // Draw spline: B-Spline, minimum 4 points
-RLAPI void DrawSplineCatmullRom(Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull-Rom, minimum 4 points
-RLAPI void DrawSplineBezierQuadratic(Vector2 *points, int pointCount, float thick, Color color);         // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
-RLAPI void DrawSplineBezierCubic(Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color);                  // Draw spline: Linear, minimum 2 points
+RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color);                   // Draw spline: B-Spline, minimum 4 points
+RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull-Rom, minimum 4 points
+RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color);         // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
 RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color);                    // Draw spline segment: Linear, 2 points
 RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points
 RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points
@@ -324,12 +344,13 @@
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles
 RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);                         // Check collision between circle and rectangle
+RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2);               // Check if circle collides with a line created betweeen two points [p1] and [p2]
 RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec);                                         // Check if point is inside rectangle
 RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius);                       // Check if point is inside circle
 RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3);               // Check if point is inside a triangle
-RLAPI bool CheckCollisionPointPoly(Vector2 point, Vector2 *points, int pointCount);                      // Check if point is within a polygon described by array of vertices
-RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference
 RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold);                // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
+RLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount);                // Check if point is within a polygon described by array of vertices
+RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference
 RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);                                         // Get collision rectangle for two rectangles collision
 
 //------------------------------------------------------------------------------------
@@ -340,12 +361,12 @@
 // NOTE: These functions do not require GPU access
 RLAPI Image LoadImage(const char *fileName);                                                             // Load image from file into CPU memory (RAM)
 RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize);       // Load image from RAW file data
-RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height);                           // Load image from SVG file data or string with specified size
 RLAPI Image LoadImageAnim(const char *fileName, int *frames);                                            // Load image sequence from file (frames appended to image.data)
+RLAPI Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames); // Load image sequence from memory buffer
 RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize);      // Load image from memory buffer, fileType refers to extension: i.e. '.png'
 RLAPI Image LoadImageFromTexture(Texture2D texture);                                                     // Load image from GPU texture data
 RLAPI Image LoadImageFromScreen(void);                                                                   // Load image from screen buffer and (screenshot)
-RLAPI bool IsImageReady(Image image);                                                                    // Check if an image is ready
+RLAPI bool IsImageValid(Image image);                                                                    // Check if an image is valid (data and parameters)
 RLAPI void UnloadImage(Image image);                                                                     // Unload image from CPU memory (RAM)
 RLAPI bool ExportImage(Image image, const char *fileName);                                               // Export image data to file, returns true on success
 RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize);              // Export image to memory buffer
@@ -365,6 +386,7 @@
 // Image manipulation functions
 RLAPI Image ImageCopy(Image image);                                                                      // Create an image duplicate (useful for transformations)
 RLAPI Image ImageFromImage(Image image, Rectangle rec);                                                  // Create an image from another image piece
+RLAPI Image ImageFromChannel(Image image, int selectedChannel);                                          // Create an image from a selected channel of another image (GRAYSCALE)
 RLAPI Image ImageText(const char *text, int fontSize, Color color);                                      // Create an image from text (default font)
 RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint);         // Create an image from text (custom sprite font)
 RLAPI void ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format
@@ -375,10 +397,10 @@
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
 RLAPI void ImageAlphaPremultiply(Image *image);                                                          // Premultiply alpha channel
 RLAPI void ImageBlurGaussian(Image *image, int blurSize);                                                // Apply Gaussian blur using a box blur approximation
-RLAPI void ImageKernelConvolution(Image *image, float* kernel, int kernelSize);                         // Apply Custom Square image convolution kernel
+RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize);                    // Apply custom square convolution kernel to image
 RLAPI void ImageResize(Image *image, int newWidth, int newHeight);                                       // Resize image (Bicubic scaling algorithm)
 RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight);                                      // Resize image (Nearest-Neighbor scaling algorithm)
-RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill);  // Resize canvas and fill with color
+RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color
 RLAPI void ImageMipmaps(Image *image);                                                                   // Compute all mipmap levels for a provided image
 RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp);                            // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
 RLAPI void ImageFlipVertical(Image *image);                                                              // Flip image vertically
@@ -406,6 +428,7 @@
 RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color);                                   // Draw pixel within an image (Vector version)
 RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image
 RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color);                          // Draw line within an image (Vector version)
+RLAPI void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color);              // Draw a line defining thickness within an image
 RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color);               // Draw a filled circle within an image
 RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color);                        // Draw a filled circle within an image (Vector version)
 RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color);          // Draw circle outline within an image
@@ -414,6 +437,11 @@
 RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color);                 // Draw rectangle within an image (Vector version)
 RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color);                                // Draw rectangle within an image
 RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color);                   // Draw rectangle lines within an image
+RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);               // Draw triangle within an image
+RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
+RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);          // Draw triangle outline within an image
+RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color);               // Draw a triangle fan defined by points within an image (first vertex is the center)
+RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color);             // Draw a triangle strip defined by points within an image
 RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint);             // Draw a source image within a destination image (tint applied to source)
 RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color);   // Draw text (using default font) within an image (destination)
 RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
@@ -424,9 +452,9 @@
 RLAPI Texture2D LoadTextureFromImage(Image image);                                                       // Load texture from image data
 RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout);                                        // Load cubemap from image, multiple image cubemap layouts supported
 RLAPI RenderTexture2D LoadRenderTexture(int width, int height);                                          // Load texture for rendering (framebuffer)
-RLAPI bool IsTextureReady(Texture2D texture);                                                            // Check if a texture is ready
+RLAPI bool IsTextureValid(Texture2D texture);                                                            // Check if a texture is valid (loaded in GPU)
 RLAPI void UnloadTexture(Texture2D texture);                                                             // Unload texture from GPU memory (VRAM)
-RLAPI bool IsRenderTextureReady(RenderTexture2D target);                                                 // Check if a render texture is ready
+RLAPI bool IsRenderTextureValid(RenderTexture2D target);                                                 // Check if a render texture is valid (loaded in GPU)
 RLAPI void UnloadRenderTexture(RenderTexture2D target);                                                  // Unload render texture from GPU memory (VRAM)
 RLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data
 RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels);                       // Update GPU texture rectangle with new data
@@ -445,8 +473,9 @@
 RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely
 
 // Color/pixel related functions
+RLAPI bool ColorIsEqual(Color col1, Color col2);                            // Check if two colors are equal
 RLAPI Color Fade(Color color, float alpha);                                 // Get color with alpha applied, alpha goes from 0.0f to 1.0f
-RLAPI int ColorToInt(Color color);                                          // Get hexadecimal value for a Color
+RLAPI int ColorToInt(Color color);                                          // Get hexadecimal value for a Color (0xRRGGBBAA)
 RLAPI Vector4 ColorNormalize(Color color);                                  // Get Color normalized as float [0..1]
 RLAPI Color ColorFromNormalized(Vector4 normalized);                        // Get Color from normalized values [0..1]
 RLAPI Vector3 ColorToHSV(Color color);                                      // Get HSV values for a Color, hue [0..360], saturation/value [0..1]
@@ -456,6 +485,7 @@
 RLAPI Color ColorContrast(Color color, float contrast);                     // Get color with contrast correction, contrast values between -1.0f and 1.0f
 RLAPI Color ColorAlpha(Color color, float alpha);                           // Get color with alpha applied, alpha goes from 0.0f to 1.0f
 RLAPI Color ColorAlphaBlend(Color dst, Color src, Color tint);              // Get src alpha-blended into dst color with tint
+RLAPI Color ColorLerp(Color color1, Color color2, float factor);            // Get color lerp interpolation between two colors, factor [0.0f..1.0f]
 RLAPI Color GetColor(unsigned int hexValue);                                // Get Color structure from hexadecimal value
 RLAPI Color GetPixelColor(void *srcPtr, int format);                        // Get Color from a source pixel pointer of certain format
 RLAPI void SetPixelColor(void *dstPtr, Color color, int format);            // Set color formatted into destination pixel pointer
@@ -468,10 +498,10 @@
 // Font loading/unloading functions
 RLAPI Font GetFontDefault(void);                                                            // Get the default Font
 RLAPI Font LoadFont(const char *fileName);                                                  // Load font from file into GPU memory (VRAM)
-RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount);  // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set
+RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height
 RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar);                        // Load font from Image (XNA style)
 RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
-RLAPI bool IsFontReady(Font font);                                                          // Check if a font is ready
+RLAPI bool IsFontValid(Font font);                                                          // Check if a font is valid (font data loaded, WARNING: GPU texture not checked)
 RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use
 RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
 RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount);                               // Unload font chars info data (RAM)
@@ -512,7 +542,7 @@
 RLAPI unsigned int TextLength(const char *text);                                            // Get text length, checks for '\0' ending
 RLAPI const char *TextFormat(const char *text, ...);                                        // Text formatting with variables (sprintf() style)
 RLAPI const char *TextSubtext(const char *text, int position, int length);                  // Get a piece of a text string
-RLAPI char *TextReplace(char *text, const char *replace, const char *by);                   // Replace text string (WARNING: memory must be freed!)
+RLAPI char *TextReplace(const char *text, const char *replace, const char *by);             // Replace text string (WARNING: memory must be freed!)
 RLAPI char *TextInsert(const char *text, const char *insert, int position);                 // Insert text in a position (WARNING: memory must be freed!)
 RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter);        // Join text strings with delimiter
 RLAPI const char **TextSplit(const char *text, char delimiter, int *count);                 // Split text into multiple strings
@@ -521,7 +551,11 @@
 RLAPI const char *TextToUpper(const char *text);                      // Get upper case version of provided string
 RLAPI const char *TextToLower(const char *text);                      // Get lower case version of provided string
 RLAPI const char *TextToPascal(const char *text);                     // Get Pascal case notation version of provided string
+RLAPI const char *TextToSnake(const char *text);                      // Get Snake case notation version of provided string
+RLAPI const char *TextToCamel(const char *text);                      // Get Camel case notation version of provided string
+
 RLAPI int TextToInteger(const char *text);                            // Get integer value from text (negative values not supported)
+RLAPI float TextToFloat(const char *text);                            // Get float value from text (negative values not supported)
 
 //------------------------------------------------------------------------------------
 // Basic 3d Shapes Drawing Functions (Module: models)
@@ -532,7 +566,7 @@
 RLAPI void DrawPoint3D(Vector3 position, Color color);                                                   // Draw a point in 3D space, actually a small line
 RLAPI void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color); // Draw a circle in 3D world space
 RLAPI void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color);                              // Draw a color-filled triangle (vertex in counter-clockwise order!)
-RLAPI void DrawTriangleStrip3D(Vector3 *points, int pointCount, Color color);                            // Draw a triangle strip defined by points
+RLAPI void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color);                      // Draw a triangle strip defined by points
 RLAPI void DrawCube(Vector3 position, float width, float height, float length, Color color);             // Draw cube
 RLAPI void DrawCubeV(Vector3 position, Vector3 size, Color color);                                       // Draw cube (Vector version)
 RLAPI void DrawCubeWires(Vector3 position, float width, float height, float length, Color color);        // Draw cube wires
@@ -557,7 +591,7 @@
 // Model management functions
 RLAPI Model LoadModel(const char *fileName);                                                // Load model from files (meshes and materials)
 RLAPI Model LoadModelFromMesh(Mesh mesh);                                                   // Load model from generated mesh (default material)
-RLAPI bool IsModelReady(Model model);                                                       // Check if a model is ready
+RLAPI bool IsModelValid(Model model);                                                       // Check if a model is valid (loaded in GPU, VAO/VBOs)
 RLAPI void UnloadModel(Model model);                                                        // Unload model (including meshes) from memory (RAM and/or VRAM)
 RLAPI BoundingBox GetModelBoundingBox(Model model);                                         // Compute model bounding box limits (considers all meshes)
 
@@ -566,8 +600,10 @@
 RLAPI void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model with extended parameters
 RLAPI void DrawModelWires(Model model, Vector3 position, float scale, Color tint);          // Draw a model wires (with texture if set)
 RLAPI void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model wires (with texture if set) with extended parameters
+RLAPI void DrawModelPoints(Model model, Vector3 position, float scale, Color tint); // Draw a model as points
+RLAPI void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint); // Draw a model as points with extended parameters
 RLAPI void DrawBoundingBox(BoundingBox box, Color color);                                   // Draw bounding box (wires)
-RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float size, Color tint);   // Draw a billboard texture
+RLAPI void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint);   // Draw a billboard texture
 RLAPI void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint); // Draw a billboard texture defined by source
 RLAPI void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint); // Draw a billboard texture defined by source and rotation
 
@@ -577,9 +613,10 @@
 RLAPI void UnloadMesh(Mesh mesh);                                                           // Unload mesh data from CPU and GPU
 RLAPI void DrawMesh(Mesh mesh, Material material, Matrix transform);                        // Draw a 3d mesh with material and transform
 RLAPI void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances); // Draw multiple mesh instances with material and different transforms
-RLAPI bool ExportMesh(Mesh mesh, const char *fileName);                                     // Export mesh data to file, returns true on success
 RLAPI BoundingBox GetMeshBoundingBox(Mesh mesh);                                            // Compute mesh bounding box limits
 RLAPI void GenMeshTangents(Mesh *mesh);                                                     // Compute mesh tangents
+RLAPI bool ExportMesh(Mesh mesh, const char *fileName);                                     // Export mesh data to file, returns true on success
+RLAPI bool ExportMeshAsCode(Mesh mesh, const char *fileName);                               // Export mesh as code file (.h) defining multiple arrays of vertex attributes
 
 // Mesh generation functions
 RLAPI Mesh GenMeshPoly(int sides, float radius);                                            // Generate polygonal mesh
@@ -597,14 +634,15 @@
 // Material loading/unloading functions
 RLAPI Material *LoadMaterials(const char *fileName, int *materialCount);                    // Load materials from model file
 RLAPI Material LoadMaterialDefault(void);                                                   // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
-RLAPI bool IsMaterialReady(Material material);                                              // Check if a material is ready
+RLAPI bool IsMaterialValid(Material material);                                              // Check if a material is valid (shader assigned, map textures loaded in GPU)
 RLAPI void UnloadMaterial(Material material);                                               // Unload material from GPU memory (VRAM)
 RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture);          // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
 RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId);                  // Set material for a mesh
 
 // Model animations loading/unloading functions
 RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount);            // Load model animations from file
-RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);               // Update model animation pose
+RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);               // Update model animation pose (CPU)
+RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame);          // Update model animation mesh bone matrices (GPU skinning)
 RLAPI void UnloadModelAnimation(ModelAnimation anim);                                       // Unload animation data
 RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount);                // Unload animation array data
 RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim);                         // Check model animation skeleton match
@@ -634,11 +672,11 @@
 // Wave/Sound loading/unloading functions
 RLAPI Wave LoadWave(const char *fileName);                            // Load wave data from file
 RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load wave from memory buffer, fileType refers to extension: i.e. '.wav'
-RLAPI bool IsWaveReady(Wave wave);                                    // Checks if wave data is ready
+RLAPI bool IsWaveValid(Wave wave);                                    // Checks if wave data is valid (data loaded and parameters)
 RLAPI Sound LoadSound(const char *fileName);                          // Load sound from file
 RLAPI Sound LoadSoundFromWave(Wave wave);                             // Load sound from wave data
 RLAPI Sound LoadSoundAlias(Sound source);                             // Create a new sound that shares the same sample data as the source sound, does not own the sound data
-RLAPI bool IsSoundReady(Sound sound);                                 // Checks if a sound is ready
+RLAPI bool IsSoundValid(Sound sound);                                 // Checks if a sound is valid (data loaded and buffers initialized)
 RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
@@ -656,7 +694,7 @@
 RLAPI void SetSoundPitch(Sound sound, float pitch);                   // Set pitch for a sound (1.0 is base level)
 RLAPI void SetSoundPan(Sound sound, float pan);                       // Set pan for a sound (0.5 is center)
 RLAPI Wave WaveCopy(Wave wave);                                       // Copy a wave to a new wave
-RLAPI void WaveCrop(Wave *wave, int initSample, int finalSample);     // Crop a wave to defined samples range
+RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame);       // Crop a wave to defined frames range
 RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
 RLAPI float *LoadWaveSamples(Wave wave);                              // Load samples data from wave as a 32bit float data array
 RLAPI void UnloadWaveSamples(float *samples);                         // Unload samples data loaded with LoadWaveSamples()
@@ -664,7 +702,7 @@
 // Music management functions
 RLAPI Music LoadMusicStream(const char *fileName);                    // Load music stream from file
 RLAPI Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, int dataSize); // Load music stream from data
-RLAPI bool IsMusicReady(Music music);                                 // Checks if a music stream is ready
+RLAPI bool IsMusicValid(Music music);                                 // Checks if a music stream is valid (context and buffers initialized)
 RLAPI void UnloadMusicStream(Music music);                            // Unload music stream
 RLAPI void PlayMusicStream(Music music);                              // Start music playing
 RLAPI bool IsMusicStreamPlaying(Music music);                         // Check if music is playing
@@ -681,7 +719,7 @@
 
 // AudioStream management functions
 RLAPI AudioStream LoadAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); // Load audio stream (to stream raw audio pcm data)
-RLAPI bool IsAudioStreamReady(AudioStream stream);                    // Checks if an audio stream is ready
+RLAPI bool IsAudioStreamValid(AudioStream stream);                    // Checks if an audio stream is valid (buffers initialized)
 RLAPI void UnloadAudioStream(AudioStream stream);                     // Unload audio stream and free memory
 RLAPI void UpdateAudioStream(AudioStream stream, const void *data, int frameCount); // Update audio stream buffers with data
 RLAPI bool IsAudioStreamProcessed(AudioStream stream);                // Check if any audio stream buffers requires refill
@@ -696,9 +734,9 @@
 RLAPI void SetAudioStreamBufferSizeDefault(int size);                 // Default size for new audio streams
 RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
 
-RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as <float>s
+RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo)
 RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
 
-RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as <float>s
+RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo)
 RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
 
diff --git a/raylib/projects/VSCode/main.c b/raylib/projects/VSCode/main.c
--- a/raylib/projects/VSCode/main.c
+++ b/raylib/projects/VSCode/main.c
@@ -15,7 +15,7 @@
 *   This example has been created using raylib 1.0 (www.raylib.com)
 *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 ********************************************************************************************/
 
@@ -26,18 +26,18 @@
 #endif
 
 //----------------------------------------------------------------------------------
-// Local Variables Definition (local to this module)
+// Global Variables Definition (local to this module)
 //----------------------------------------------------------------------------------
 Camera camera = { 0 };
 Vector3 cubePosition = { 0 };
 
 //----------------------------------------------------------------------------------
-// Local Functions Declaration
+// Module Functions Declaration
 //----------------------------------------------------------------------------------
 static void UpdateDrawFrame(void);          // Update and draw one frame
 
 //----------------------------------------------------------------------------------
-// Main entry point
+// Program main entry point
 //----------------------------------------------------------------------------------
 int main()
 {
diff --git a/raylib/src/config.h b/raylib/src/config.h
--- a/raylib/src/config.h
+++ b/raylib/src/config.h
@@ -6,7 +6,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2018-2024 Ahmad Fatoum & Ramon Santamaria (@raysan5)
+*   Copyright (c) 2018-2026 Ahmad Fatoum and Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -30,17 +30,24 @@
 
 //------------------------------------------------------------------------------------
 // Module selection - Some modules could be avoided
-// Mandatory modules: rcore, rlgl, utils
+// Mandatory modules: rcore, rlgl
 //------------------------------------------------------------------------------------
-#define SUPPORT_MODULE_RSHAPES          1
-#define SUPPORT_MODULE_RTEXTURES        1
-#define SUPPORT_MODULE_RTEXT            1       // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
-#define SUPPORT_MODULE_RMODELS          1
-#define SUPPORT_MODULE_RAUDIO           1
+#if !defined(EXTERNAL_CONFIG_FLAGS)
+    #define SUPPORT_MODULE_RSHAPES          1
+    #define SUPPORT_MODULE_RTEXTURES        1
+    #define SUPPORT_MODULE_RTEXT            1       // WARNING: It requires SUPPORT_MODULE_RTEXTURES to load sprite font textures
+    #define SUPPORT_MODULE_RMODELS          1
+    #define SUPPORT_MODULE_RAUDIO           1
+#endif
 
 //------------------------------------------------------------------------------------
 // Module: rcore - Configuration Flags
 //------------------------------------------------------------------------------------
+#if !defined(EXTERNAL_CONFIG_FLAGS)
+// Standard file io library (stdio.h) included
+#define SUPPORT_STANDARD_FILEIO         1
+// Show TRACELOG() output messages
+#define SUPPORT_TRACELOG                1
 // Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital
 #define SUPPORT_CAMERA_SYSTEM           1
 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
@@ -49,19 +56,18 @@
 #define SUPPORT_RPRAND_GENERATOR        1
 // Mouse gestures are directly mapped like touches and processed by gestures system
 #define SUPPORT_MOUSE_GESTURES          1
-// Reconfigure standard input to receive key inputs, works with SSH connection.
+// Reconfigure standard input to receive key inputs, works with SSH connection
 #define SUPPORT_SSH_KEYBOARD_RPI        1
-// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions.
-// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often.
+// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
+// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
 #define SUPPORT_WINMM_HIGHRES_TIMER     1
 // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used
 //#define SUPPORT_BUSY_WAIT_LOOP          1
 // Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy
 #define SUPPORT_PARTIALBUSY_WAIT_LOOP    1
 // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
+// WARNING: It also requires SUPPORT_IMAGE_EXPORT and SUPPORT_FILEFORMAT_PNG flags
 #define SUPPORT_SCREEN_CAPTURE          1
-// Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
-#define SUPPORT_GIF_RECORDING           1
 // Support CompressData() and DecompressData() functions
 #define SUPPORT_COMPRESSION_API         1
 // Support automatic generated events, loading and recording of those events when required
@@ -70,19 +76,50 @@
 // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
 // Enabling this flag allows manual control of the frame processes, use at your own risk
 //#define SUPPORT_CUSTOM_FRAME_CONTROL    1
+// Support for clipboard image loading
+// NOTE: Only working on SDL3, GLFW (Windows) and RGFW (Windows)
+#define SUPPORT_CLIPBOARD_IMAGE         1
+#endif
 
+// NOTE: Clipboard image loading requires support for some image file formats
+// TODO: Those defines should probably be removed from here, letting the user manage them
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+    #ifndef SUPPORT_MODULE_RTEXTURES
+        #define SUPPORT_MODULE_RTEXTURES 1
+    #endif
+    #ifndef STBI_REQUIRED
+        #define STBI_REQUIRED
+    #endif
+    #ifndef SUPPORT_FILEFORMAT_BMP // For clipboard image on Windows
+        #define SUPPORT_FILEFORMAT_BMP 1
+    #endif
+    #ifndef SUPPORT_FILEFORMAT_PNG // Wayland uses png for prints, at least it was on 22 LTS ubuntu
+        #define SUPPORT_FILEFORMAT_PNG 1
+    #endif
+    #ifndef SUPPORT_FILEFORMAT_JPG
+        #define SUPPORT_FILEFORMAT_JPG 1
+    #endif
+#endif
+
+#if defined(SUPPORT_TRACELOG)
+    #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
+#else
+    #define TRACELOG(level, ...) (void)0
+#endif
+
 // rcore: Configuration values
 //------------------------------------------------------------------------------------
+#define MAX_TRACELOG_MSG_LENGTH       256       // Max length of one trace-log message
 #define MAX_FILEPATH_CAPACITY        8192       // Maximum file paths capacity
 #define MAX_FILEPATH_LENGTH          4096       // Maximum length for filepaths (Linux PATH_MAX default value)
 
 #define MAX_KEYBOARD_KEYS             512       // Maximum number of keyboard keys supported
 #define MAX_MOUSE_BUTTONS               8       // Maximum number of mouse buttons supported
 #define MAX_GAMEPADS                    4       // Maximum number of gamepads supported
-#define MAX_GAMEPAD_AXIS                8       // Maximum number of axis supported (per gamepad)
+#define MAX_GAMEPAD_AXES                8       // Maximum number of axes supported (per gamepad)
 #define MAX_GAMEPAD_BUTTONS            32       // Maximum number of buttons supported (per gamepad)
 #define MAX_GAMEPAD_VIBRATION_TIME      2.0f    // Maximum vibration time in seconds
-#define MAX_TOUCH_POINTS                8       // Maximum number of touch points supported
+#define MAX_TOUCH_POINTS               10       // Maximum number of touch points supported
 #define MAX_KEY_PRESSED_QUEUE          16       // Maximum number of keys in the key input queue
 #define MAX_CHAR_PRESSED_QUEUE         16       // Maximum number of characters in the char input queue
 
@@ -93,13 +130,16 @@
 //------------------------------------------------------------------------------------
 // Module: rlgl - Configuration values
 //------------------------------------------------------------------------------------
-
+#if !defined(EXTERNAL_CONFIG_FLAGS)
 // Enable OpenGL Debug Context (only available on OpenGL 4.3)
 //#define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT       1
 
 // Show OpenGL extensions and capabilities detailed logs on init
 //#define RLGL_SHOW_GL_DETAILS_INFO              1
 
+#define RL_SUPPORT_MESH_GPU_SKINNING           1      // GPU skinning, comment if your GPU does not support more than 8 VBOs
+#endif
+
 //#define RL_DEFAULT_BATCH_BUFFER_ELEMENTS    4096    // Default internal render batch elements limits
 #define RL_DEFAULT_BATCH_BUFFERS               1      // Default number of batch buffers (multi-buffering)
 #define RL_DEFAULT_BATCH_DRAWCALLS           256      // Default number of batch draw calls (by state changes: mode, texture)
@@ -109,8 +149,8 @@
 
 #define RL_MAX_SHADER_LOCATIONS               32      // Maximum number of shader locations supported
 
-#define RL_CULL_DISTANCE_NEAR               0.01      // Default projection matrix near cull distance
-#define RL_CULL_DISTANCE_FAR              1000.0      // Default projection matrix far cull distance
+#define RL_CULL_DISTANCE_NEAR              0.05       // Default projection matrix near cull distance
+#define RL_CULL_DISTANCE_FAR             4000.0       // Default projection matrix far cull distance
 
 // Default shader vertex attribute locations
 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION    0
@@ -120,15 +160,11 @@
 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT     4
 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2   5
 #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES     6
-
-
-#define RL_SUPPORT_MESH_GPU_SKINNING                   // Remove this if your GPU does not support more than 8 VBOs
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS     7
-#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
+#if defined(RL_SUPPORT_MESH_GPU_SKINNING)
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS     7
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
 #endif
-
+#define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
 
 // Default shader vertex attribute names to set location points
 // NOTE: When a new shader is loaded, the following locations are tried to be set for convenience
@@ -149,23 +185,24 @@
 #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1  "texture1"          // texture1 (texture slot active 1)
 #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2  "texture2"          // texture2 (texture slot active 2)
 
-
 //------------------------------------------------------------------------------------
 // Module: rshapes - Configuration Flags
 //------------------------------------------------------------------------------------
+#if !defined(EXTERNAL_CONFIG_FLAGS)
 // Use QUADS instead of TRIANGLES for drawing when possible
 // Some lines-based shapes could still use lines
 #define SUPPORT_QUADS_DRAW_MODE         1
+#endif
 
 // rshapes: Configuration values
 //------------------------------------------------------------------------------------
 #define SPLINE_SEGMENT_DIVISIONS       24       // Spline segments subdivisions
 
-
 //------------------------------------------------------------------------------------
 // Module: rtextures - Configuration Flags
 //------------------------------------------------------------------------------------
-// Selecte desired fileformats to be supported for image data loading
+#if !defined(EXTERNAL_CONFIG_FLAGS)
+// Selected desired fileformats to be supported for image data loading
 #define SUPPORT_FILEFORMAT_PNG      1
 //#define SUPPORT_FILEFORMAT_BMP      1
 //#define SUPPORT_FILEFORMAT_TGA      1
@@ -188,11 +225,12 @@
 // Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
 // If not defined, still some functions are supported: ImageFormat(), ImageCrop(), ImageToPOT()
 #define SUPPORT_IMAGE_MANIPULATION      1
-
+#endif
 
 //------------------------------------------------------------------------------------
 // Module: rtext - Configuration Flags
 //------------------------------------------------------------------------------------
+#if !defined(EXTERNAL_CONFIG_FLAGS)
 // Default font is loaded on window initialization to be available for the user to render simple text
 // NOTE: If enabled, uses external module functions to load default raylib font
 #define SUPPORT_DEFAULT_FONT            1
@@ -207,19 +245,23 @@
 
 // On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
 // at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
-// drawing text and shapes with a single draw call [SetShapesTexture()].
+// drawing text and shapes with a single draw call [SetShapesTexture()]
 #define SUPPORT_FONT_ATLAS_WHITE_REC    1
 
+// Support conservative font atlas size estimation
+//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE    1
+#endif
+
 // rtext: Configuration values
 //------------------------------------------------------------------------------------
 #define MAX_TEXT_BUFFER_LENGTH       1024       // Size of internal static buffers used on some functions:
                                                 // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
 #define MAX_TEXTSPLIT_COUNT           128       // Maximum number of substrings to split: TextSplit()
 
-
 //------------------------------------------------------------------------------------
 // Module: rmodels - Configuration Flags
 //------------------------------------------------------------------------------------
+#if !defined(EXTERNAL_CONFIG_FLAGS)
 // Selected desired model fileformats to be supported for loading
 #define SUPPORT_FILEFORMAT_OBJ          1
 #define SUPPORT_FILEFORMAT_MTL          1
@@ -230,6 +272,7 @@
 // Support procedural mesh generation functions, uses external par_shapes.h library
 // NOTE: Some generated meshes DO NOT include generated texture coordinates
 #define SUPPORT_MESH_GENERATION         1
+#endif
 
 // rmodels: Configuration values
 //------------------------------------------------------------------------------------
@@ -244,6 +287,7 @@
 //------------------------------------------------------------------------------------
 // Module: raudio - Configuration Flags
 //------------------------------------------------------------------------------------
+#if !defined(EXTERNAL_CONFIG_FLAGS)
 // Desired audio fileformats to be supported for loading
 #define SUPPORT_FILEFORMAT_WAV          1
 #define SUPPORT_FILEFORMAT_OGG          1
@@ -252,6 +296,7 @@
 //#define SUPPORT_FILEFORMAT_FLAC         1
 #define SUPPORT_FILEFORMAT_XM           1
 #define SUPPORT_FILEFORMAT_MOD          1
+#endif
 
 // raudio: Configuration values
 //------------------------------------------------------------------------------------
@@ -260,19 +305,5 @@
 #define AUDIO_DEVICE_SAMPLE_RATE           0    // Device sample rate (device default)
 
 #define MAX_AUDIO_BUFFER_POOL_CHANNELS    16    // Maximum number of audio pool channels
-
-//------------------------------------------------------------------------------------
-// Module: utils - Configuration Flags
-//------------------------------------------------------------------------------------
-// Standard file io library (stdio.h) included
-#define SUPPORT_STANDARD_FILEIO         1
-// Show TRACELOG() output messages
-// NOTE: By default LOG_DEBUG traces not shown
-#define SUPPORT_TRACELOG                1
-//#define SUPPORT_TRACELOG_DEBUG          1
-
-// utils: Configuration values
-//------------------------------------------------------------------------------------
-#define MAX_TRACELOG_MSG_LENGTH       256       // Max length of one trace-log message
 
 #endif // CONFIG_H
diff --git a/raylib/src/external/RGFW.h b/raylib/src/external/RGFW.h
--- a/raylib/src/external/RGFW.h
+++ b/raylib/src/external/RGFW.h
@@ -1,8993 +1,11074 @@
-/*
-* Copyright (C) 2023-24 ColleagueRiley
-*
-* libpng license
-*
-* This software is provided 'as-is', without any express or implied
-* warranty.  In no event will the authors be held liable for any damages
-* arising from the use of this software.
-*
-* Permission is granted to anyone to use this software for any purpose,
-* including commercial applications, and to alter it and redistribute it
-* freely, subject to the following restrictions:
-*
-* 1. The origin of this software must not be misrepresented; you must not
-*    claim that you wrote the original software. If you use this software
-*    in a product, an acknowledgment in the product documentation would be
-*    appreciated but is not required.
-* 2. Altered source versions must be plainly marked as such, and must not be
-*    misrepresented as being the original software.
-* 3. This notice may not be removed or altered from any source distribution.
-*
-*
-*/
-
-/*
-	(MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION)
-	#define RGFW_IMPLEMENTATION - makes it so source code is included with header
-*/
-
-/*
-	#define RGFW_IMPLEMENTATION - (required) makes it so the source code is included
-	#define RGFW_PRINT_ERRORS - (optional) makes it so RGFW prints errors when they're found
-	#define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl)
-	#define RGFW_BUFFER - (optional) just draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format)
-	#define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api)
-	#define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api)
-									This version doesn't work for desktops (I'm pretty sure)
-	#define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2)
-	#define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3)
-	#define RGFW_DIRECTX - (optional) use directX for the rendering backend (rather than opengl) (windows only, defaults to opengl for unix)
-	#define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX)
-
-	#define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress)
-	#define RGFW_LINK_OSMESA (optional) (windows only) if EGL is being used, if OS Mesa functions should be defined dymanically  (using GetProcAddress)
-
-	#define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS
-	#define RGFW_WGL_LOAD (optional) (windows only) if WGL should be loaded dynamically during runtime
-	#define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor
-	#define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) Use XCursor, but don't link it in code, (you'll have to link it with -lXcursor)
-
-	#define RGFW_NO_DPI - Do not include calculate DPI (no XRM nor libShcore included)
-
-	#define RGFW_ALLOC_DROPFILES (optional) if room should be allocating for drop files (by default it's global data)
-	#define RGFW_MALLOC x - choose what function to use to allocate, by default the standard malloc is used
-	#define RGFW_CALLOC x - choose what function to use to allocate (calloc), by default the standard calloc is used
-	#define RGFW_FREE x - choose what function to use to allocated memory, by default the standard free is used
-
- 	#define RGFW_EXPORT - Use when building RGFW 
-    #define RGFW_IMPORT - Use when linking with RGFW (not as a single-header)
-	
-	#define RGFW_STD_INT - force the use stdint.h (for systems that might not have stdint.h (msvc)) 
-*/
-
-/*
-	Credits :
-		EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing 
-
-		stb - This project is heavily inspired by the stb single header files
-
-		GLFW:
-			certain parts of winapi and X11 are very poorly documented,
-			GLFW's source code was referenced and used throughout the project (used code is marked in some way),
-			this mainly includes, code for drag and drops, code for setting the icon to a bitmap and the code for managing the clipboard for X11 (as these parts are not documented very well)
-
-			GLFW Copyright, https::/github.com/GLFW/GLFW
-
-			Copyright (c) 2002-2006 Marcus Geelnard
-			Copyright (c) 2006-2019 Camilla Löwy
-
-		contributors : (feel free to put yourself here if you contribute)
-		krisvers -> code review
-		EimaMei (SaCode) -> code review
-		Code-Nycticebus -> bug fixes
-		Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs 
-		AICDG (@THISISAGOODNAME) -> vulkan support (example)
-		@Easymode -> support, testing/debugging, bug fixes and reviews
-*/
-
-#if _MSC_VER
-	#pragma comment(lib, "gdi32")
-	#pragma comment(lib, "shell32")
-	#pragma comment(lib, "opengl32")
-	#pragma comment(lib, "winmm")
-	#pragma comment(lib, "user32")
-#endif
-
-#ifndef RGFW_MALLOC
-	#include <stdlib.h>
-
-	#ifndef __USE_POSIX199309
-	#define __USE_POSIX199309
-	#endif
-
-	#include <time.h>
-	#define RGFW_MALLOC malloc
-	#define RGFW_CALLOC calloc
-	#define RGFW_FREE free
-#endif
-
-#if !_MSC_VER
-	#ifndef inline
-		#ifndef __APPLE__
-			#define inline __inline
-		#endif
-	#endif
-#endif
-
-#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
-	#define RGFW_NO_MONITOR
-	#define RGFW_NO_PASSTHROUGH
-#endif
-
-#if defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT)
-	#if defined(_WIN32)
-		#if defined(__TINYC__) && (defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT))
-			#define __declspec(x) __attribute__((x))
-		#endif
-
-		#if defined(RGFW_EXPORT)
-			#define RGFWDEF __declspec(dllexport)
-		#else 
-			#define RGFWDEF __declspec(dllimport)
-		#endif
-	#else
-		#if defined(RGFW_EXPORT)
-			#define RGFWDEF __attribute__((visibility("default")))
-		#endif
-	#endif
-#endif 
-
-#ifndef RGFWDEF
-	#ifdef __clang__
-		#define RGFWDEF static inline
-	#else
-		#define RGFWDEF inline
-	#endif
-#endif
-
-#ifndef RGFW_ENUM
-	#define RGFW_ENUM(type, name) type name; enum
-#endif
-
-#ifndef RGFW_UNUSED
-	#define RGFW_UNUSED(x) (void)(x);
-#endif
-
-#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
-	extern "C" {
-#endif
-
-	/* makes sure the header file part is only defined once by default */
-#ifndef RGFW_HEADER
-
-#define RGFW_HEADER
-
-#if !defined(u8)
-	#if ((defined(_MSC_VER) || defined(__SYMBIAN32__)) && !defined(RGFW_STD_INT)) /* MSVC might not have stdint.h */
-		typedef unsigned char 	u8;
-		typedef signed char		i8;
-		typedef unsigned short  u16;
-		typedef signed short 	i16;
-		typedef unsigned int 	u32;
-		typedef signed int		i32;
-		typedef unsigned long	u64;
-		typedef signed long		i64;
-	#else /* use stdint standard types instead of c ""standard"" types */
-		#include <stdint.h>
-
-		typedef uint8_t     u8;
-		typedef int8_t      i8;
-		typedef uint16_t   u16;
-		typedef int16_t    i16;
-		typedef uint32_t   u32;
-		typedef int32_t    i32;
-		typedef uint64_t   u64;
-		typedef int64_t    i64;
-	#endif
-#endif
-
-#if !defined(b8) /* RGFW bool type */
-	typedef u8 b8;
-	typedef u32 b32;
-#endif
-
-#define RGFW_TRUE (!(0))
-#define RGFW_FALSE 0
-
-/* thse OS macros looks better & are standardized */
-/* plus it helps with cross-compiling */
-
-#ifdef __EMSCRIPTEN__
-	#define RGFW_WEBASM
-
-	#ifndef RGFW_NO_API
-		#define RGFW_OPENGL
-	#endif
-
-	#ifdef RGFW_EGL
-		#undef RGFW_EGL
-	#endif
-
-	#include <emscripten/html5.h>
-	#include <emscripten/key_codes.h>
-#endif
-
-#if defined(RGFW_X11) && defined(__APPLE__)
-	#define RGFW_MACOS_X11
-	#undef __APPLE__
-#endif
-
-#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_WEBASM) /* (if you're using X11 on windows some how) */
-	#define RGFW_WINDOWS
-
-	/* make sure the correct architecture is defined */
-	#if defined(_WIN64)
-		#define _AMD64_
-		#undef _X86_
-	#else
-		#undef _AMD64_
-		#ifndef _X86_
-			#define _X86_
-		#endif
-	#endif
-
-	#ifndef RGFW_NO_XINPUT
-		#ifdef __MINGW32__ /* try to find the right header */
-			#include <xinput.h>
-		#else
-			#include <XInput.h>
-		#endif
-	#endif
-
-	#if defined(RGFW_DIRECTX)
-		#include <d3d11.h>
-		#include <dxgi.h>
-		#include <dxgi.h>
-		#include <d3dcompiler.h>
-
-		#ifndef __cplusplus
-			#define __uuidof(T) IID_##T
-		#endif
-	#endif
-
-#elif defined(RGFW_WAYLAND)
-    #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL))
-		#define RGFW_EGL
-		#define RGFW_OPENGL
-		#include <wayland-egl.h>
-	#endif
-
-	#include <wayland-client.h>
-#elif (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11))  && !defined(RGFW_WEBASM)
-	#define RGFW_MACOS_X11
-	#define RGFW_X11
-	#include <X11/Xlib.h>
-#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11)  && !defined(RGFW_WEBASM)
-	#define RGFW_MACOS
-#endif
-
-#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL)
-	#define RGFW_EGL
-#endif
-
-#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API)
-	#define RGFW_OPENGL
-#endif
-
-#ifdef RGFW_EGL
-	#include <EGL/egl.h>
-#elif defined(RGFW_OSMESA)
-	#ifndef __APPLE__
-		#include <GL/osmesa.h>
-	#else
-		#include <OpenGL/osmesa.h>
-	#endif
-#endif
-
-#if defined(RGFW_OPENGL) && defined(RGFW_X11)
-	#ifndef GLX_MESA_swap_control
-		#define  GLX_MESA_swap_control
-	#endif
-	#include <GL/glx.h> /* GLX defs, xlib.h, gl.h */
-#endif
-
-#ifndef RGFW_ALPHA
-	#define RGFW_ALPHA 128 /* alpha value for RGFW_TRANSPARENT_WINDOW (WINAPI ONLY, macOS + linux don't need this) */
-#endif
-
-/*! Optional arguments for making a windows */
-#define RGFW_TRANSPARENT_WINDOW		(1L<<9) /*!< the window is transparent (only properly works on X11 and MacOS, although it's although for windows) */
-#define RGFW_NO_BORDER		(1L<<3) /*!< the window doesn't have border */
-#define RGFW_NO_RESIZE		(1L<<4) /*!< the window cannot be resized  by the user */
-#define RGFW_ALLOW_DND     (1L<<5) /*!< the window supports drag and drop*/
-#define RGFW_HIDE_MOUSE (1L<<6) /*! the window should hide the mouse or not (can be toggled later on) using `RGFW_window_mouseShow*/
-#define RGFW_FULLSCREEN (1L<<8) /* the window is fullscreen by default or not */
-#define RGFW_CENTER (1L<<10) /*! center the window on the screen */
-#define RGFW_OPENGL_SOFTWARE (1L<<11) /*! use OpenGL software rendering */
-#define RGFW_COCOA_MOVE_TO_RESOURCE_DIR (1L << 12) /* (cocoa only), move to resource folder */
-#define RGFW_SCALE_TO_MONITOR (1L << 13) /* scale the window to the screen */
-#define RGFW_NO_INIT_API (1L << 2) /* DO not init an API (mostly for bindings, you should use `#define RGFW_NO_API` in C */
-
-#define RGFW_NO_GPU_RENDER (1L<<14) /* don't render (using the GPU based API)*/
-#define RGFW_NO_CPU_RENDER (1L<<15) /* don't render (using the CPU based buffer rendering)*/
-#define RGFW_WINDOW_HIDE (1L <<  16)/* the window is hidden */
-
-typedef RGFW_ENUM(u8, RGFW_event_types) {
-	/*! event codes */
- 	RGFW_keyPressed = 1, /* a key has been pressed */
-	RGFW_keyReleased, /*!< a key has been released*/
-	/*! key event note
-		the code of the key pressed is stored in
-		RGFW_Event.keyCode
-		!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!!
-
-		while a string version is stored in
-		RGFW_Event.KeyString
-
-		RGFW_Event.lockState holds the current lockState
-		this means if CapsLock, NumLock are active or not
-	*/
-	RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right)*/
-	RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right)*/
-	RGFW_mousePosChanged, /*!< the position of the mouse has been changed*/
-	/*! mouse event note
-		the x and y of the mouse can be found in the vector, RGFW_Event.point
-
-		RGFW_Event.button holds which mouse button was pressed
-	*/
-	RGFW_jsButtonPressed, /*!< a joystick button was pressed */
-	RGFW_jsButtonReleased, /*!< a joystick button was released */
-	RGFW_jsAxisMove, /*!< an axis of a joystick was moved*/
-	/*! joystick event note
-		RGFW_Event.joystick holds which joystick was altered, if any
-		RGFW_Event.button holds which joystick button was pressed
-
-		RGFW_Event.axis holds the data of all the axis
-		RGFW_Event.axisCount says how many axis there are
-	*/
-	RGFW_windowMoved, /*!< the window was moved (by the user) */
-	RGFW_windowResized, /*!< the window was resized (by the user), [on webASM this means the browser was resized] */
-	RGFW_focusIn, /*!< window is in focus now */
-	RGFW_focusOut, /*!< window is out of focus now */
-	RGFW_mouseEnter, /* mouse entered the window */
-	RGFW_mouseLeave, /* mouse left the window */
-	RGFW_windowRefresh, /* The window content needs to be refreshed */
-
-	/* attribs change event note
-		The event data is sent straight to the window structure
-		with win->r.x, win->r.y, win->r.w and win->r.h
-	*/
-	RGFW_quit, /*!< the user clicked the quit button*/ 
-	RGFW_dnd, /*!< a file has been dropped into the window*/
-	RGFW_dnd_init /*!< the start of a dnd event, when the place where the file drop is known */
-	/* dnd data note
-		The x and y coords of the drop are stored in the vector RGFW_Event.point
-
-		RGFW_Event.droppedFilesCount holds how many files were dropped
-
-		This is also the size of the array which stores all the dropped file string,
-		RGFW_Event.droppedFiles
-	*/
-};
-
-/*! mouse button codes (RGFW_Event.button) */
-#define RGFW_mouseLeft  1 /*!< left mouse button is pressed*/
-#define RGFW_mouseMiddle  2 /*!< mouse-wheel-button is pressed*/
-#define RGFW_mouseRight  3 /*!< right mouse button is pressed*/
-#define RGFW_mouseScrollUp  4 /*!< mouse wheel is scrolling up*/
-#define RGFW_mouseScrollDown  5 /*!< mouse wheel is scrolling down*/
-
-#ifndef RGFW_MAX_PATH
-#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */
-#endif
-#ifndef RGFW_MAX_DROPS
-#define RGFW_MAX_DROPS 260 /* max items you can drop at once */
-#endif
-
-
-/* for RGFW_Event.lockstate */
-#define RGFW_CAPSLOCK (1L << 1)
-#define RGFW_NUMLOCK (1L << 2)
-
-/*! joystick button codes (based on xbox/playstation), you may need to change these values per controller */
-#ifndef RGFW_joystick_codes
-	typedef RGFW_ENUM(u8, RGFW_joystick_codes) {
-		RGFW_JS_A = 0, /*!< or PS X button */
-		RGFW_JS_B = 1, /*!< or PS circle button */
-		RGFW_JS_Y = 2, /*!< or PS triangle button */
-		RGFW_JS_X = 3, /*!< or PS square button */
-		RGFW_JS_START = 9, /*!< start button */
-		RGFW_JS_SELECT = 8, /*!< select button */
-		RGFW_JS_HOME = 10, /*!< home button */
-		RGFW_JS_UP = 13, /*!< dpad up */
-		RGFW_JS_DOWN = 14, /*!< dpad down*/
-		RGFW_JS_LEFT = 15, /*!< dpad left */
-		RGFW_JS_RIGHT = 16, /*!< dpad right */
-		RGFW_JS_L1 = 4, /*!< left bump */
-		RGFW_JS_L2 = 5, /*!< left trigger*/
-		RGFW_JS_R1 = 6, /*!< right bumper */
-		RGFW_JS_R2 = 7, /*!< right trigger */
-	};
-#endif
-
-/*! basic vector type, if there's not already a point/vector type of choice */
-#ifndef RGFW_point
-	typedef struct { i32 x, y; } RGFW_point;
-#endif
-
-/*! basic rect type, if there's not already a rect type of choice */
-#ifndef RGFW_rect
-	typedef struct { i32 x, y, w, h; } RGFW_rect;
-#endif
-
-/*! basic area type, if there's not already a area type of choice */
-#ifndef RGFW_area
-	typedef struct { u32 w, h; } RGFW_area;
-#endif
-
-#ifndef __cplusplus
-#define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)}
-#define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
-#define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)}
-#else
-#define RGFW_POINT(x, y) {(i32)(x), (i32)(y)}
-#define RGFW_RECT(x, y, w, h) {(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
-#define RGFW_AREA(w, h) {(u32)(w), (u32)(h)}
-#endif
-
-#ifndef RGFW_NO_MONITOR
-	/*! structure for monitor data */
-	typedef struct RGFW_monitor {
-		char name[128]; /*!< monitor name */
-		RGFW_rect rect; /*!< monitor Workarea */
-		float scaleX, scaleY; /*!< monitor content scale*/
-		float physW, physH; /*!< monitor physical size */
-	} RGFW_monitor;
-
-	/*
-		NOTE : Monitor functions should be ran only as many times as needed (not in a loop)
-	*/
-
-	/*! get an array of all the monitors (max 6) */
-	RGFWDEF RGFW_monitor* RGFW_getMonitors(void);
-	/*! get the primary monitor */
-	RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void);
-#endif
-
-/* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_Event struct) */
-/*! Event structure for checking/getting events */
-typedef struct RGFW_Event {
-	char keyName[16]; /*!< key name of event*/
-
-	/*! drag and drop data */
-	/* 260 max paths with a max length of 260 */
-#ifdef RGFW_ALLOC_DROPFILES
-	char** droppedFiles;
-#else
-	char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH]; /*!< dropped files*/
-#endif
-	u32 droppedFilesCount; /*!< house many files were dropped */
-
-	u32 type; /*!< which event has been sent?*/
-	RGFW_point point; /*!< mouse x, y of event (or drop point) */
-	
-	u8 keyCode; /*!< keycode of event 	!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */	
-	
-	b8 repeat; /*!< key press event repeated (the key is being held) */
-	b8 inFocus;  /*!< if the window is in focus or not (this is always true for MacOS windows due to the api being weird) */
-
-	u8 lockState;
-	
-	u8 button; /* !< which mouse button was pressed */
-	double scroll; /*!< the raw mouse scroll value */
-
-	u16 joystick; /*! which joystick this event applies to (if applicable to any) */
-	u8 axisesCount; /*!< number of axises */
-	RGFW_point axis[2]; /*!< x, y of axises (-100 to 100) */
-
-	u64 frameTime, frameTime2; /*!< this is used for counting the fps */
-} RGFW_Event;
-
-/*! source data for the window (used by the APIs) */
-typedef struct RGFW_window_src {
-#ifdef RGFW_WINDOWS
-	HWND window; /*!< source window */
-	HDC hdc; /*!< source HDC */
-	u32 hOffset; /*!< height offset for window */
-	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
-		HGLRC ctx; /*!< source graphics context */
-	#elif defined(RGFW_OSMESA)
-		OSMesaContext ctx;
-	#elif defined(RGFW_DIRECTX)
-		IDXGISwapChain* swapchain;
-		ID3D11RenderTargetView* renderTargetView;
-		ID3D11DepthStencilView* pDepthStencilView;
-	#elif defined(RGFW_EGL)
-		EGLSurface EGL_surface;
-		EGLDisplay EGL_display;
-		EGLContext EGL_context;
-	#endif
-
-	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-		HDC hdcMem;
-		HBITMAP bitmap;
-	#endif
-	RGFW_area maxSize, minSize; /*!< for setting max/min resize (RGFW_WINDOWS) */
-#elif defined(RGFW_X11)
-	Display* display; /*!< source display */
-	Window window; /*!< source window */
-	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
-		GLXContext ctx; /*!< source graphics context */
-	#elif defined(RGFW_OSMESA)
-		OSMesaContext ctx;
-	#elif defined(RGFW_EGL)
-		EGLSurface EGL_surface;
-		EGLDisplay EGL_display;
-		EGLContext EGL_context;
-	#endif
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-		XImage* bitmap;
-		GC gc;
-#endif
-#elif defined(RGFW_WAYLAND)
-	struct wl_display* display;
-	struct wl_surface* surface;
-	struct wl_buffer* wl_buffer;
-	struct wl_keyboard* keyboard;
-
-	struct xdg_surface* xdg_surface;
-	struct xdg_toplevel* xdg_toplevel;
-	struct zxdg_toplevel_decoration_v1* decoration;
-	RGFW_Event events[20];
-		i32 eventLen;
-		size_t eventIndex;
-	#if defined(RGFW_EGL)
-			struct wl_egl_window* window;
-			EGLSurface EGL_surface;
-			EGLDisplay EGL_display;
-			EGLContext EGL_context;
-	#elif defined(RGFW_OSMESA)
-		OSMesaContext ctx;
-	#endif
-#elif defined(RGFW_MACOS)
-	u32 display;
-	void* displayLink;
-	void* window;
-	b8 dndPassed;
-#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
-		void* ctx; /*!< source graphics context */
-#elif defined(RGFW_OSMESA)
-		OSMesaContext ctx;
-#elif defined(RGFW_EGL)
-		EGLSurface EGL_surface;
-		EGLDisplay EGL_display;
-		EGLContext EGL_context;
-#endif
-
-	void* view; /*apple viewpoint thingy*/
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-		void* bitmap; /*!< API's bitmap for storing or managing */
-		void* image;
-#endif
-#elif defined(RGFW_WEBASM)
-	EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
-#endif
-} RGFW_window_src;
-
-
-
-typedef struct RGFW_window {
-	RGFW_window_src src; /*!< src window data */
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) 
-	u8* buffer; /*!< buffer for non-GPU systems (OSMesa, basic software rendering) */
-	/* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */
-#endif
-	void* userPtr; /* ptr for usr data */
-	
-	RGFW_Event event; /*!< current event */
-
-	RGFW_rect r; /*!< the x, y, w and h of the struct */
-	
-	RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */
-	
-	u32 _winArgs; /*!< windows args (for RGFW to check) */
-} RGFW_window; /*!< Window structure for managing the window */
-
-#if defined(RGFW_X11) || defined(RGFW_MACOS)
-	typedef u64 RGFW_thread; /*!< thread type unix */
-#else
-	typedef void* RGFW_thread; /*!< thread type for window */
-#endif
-
-/** * @defgroup Window_management
-* @{ */ 
-
-
-/*! 
- * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM
- * by default the class name will == the root window's name
-*/
-RGFWDEF void RGFW_setClassName(char* name);
-
-/*! this has to be set before createWindow is called, else the fulscreen size is used */
-RGFWDEF void RGFW_setBufferSize(RGFW_area size); /*!< the buffer cannot be resized (by RGFW) */
-
-RGFWDEF RGFW_window* RGFW_createWindow(
-	const char* name, /* name of the window */
-	RGFW_rect rect, /* rect of window */
-	u16 args /* extra arguments (NULL / (u16)0 means no args used)*/
-); /*!< function to create a window struct */
-
-/*! get the size of the screen to an area struct */
-RGFWDEF RGFW_area RGFW_getScreenSize(void);
-
-/*!
-	this function checks an *individual* event (and updates window structure attributes)
-	this means, using this function without a while loop may cause event lag
-
-	ex.
-
-	while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one]
-
-	this function is optional if you choose to use event callbacks, 
-	although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents`
-*/
-
-RGFWDEF RGFW_Event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/
-
-/*!
-	for RGFW_window_eventWait and RGFW_window_checkEvents
-	waitMS -> Allows th	e function to keep checking for events even after `RGFW_window_checkEvent == NULL`
-			  if waitMS == 0, the loop will not wait for events
-			  if waitMS == a positive integer, the loop will wait that many miliseconds after there are no more events until it returns
-			  if waitMS == a negative integer, the loop will not return until it gets another event
-*/
-typedef RGFW_ENUM(i32, RGFW_eventWait) {
-	RGFW_NEXT = -1,
-	RGFW_NO_WAIT = 0
-};
-
-/*! sleep until RGFW gets an event or the timer ends (defined by OS) */
-RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS);
-
-/*!
-	check all the events until there are none left, 
-	this should only be used if you're using callbacks only
-*/
-RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS);
-
-/*! 
-	Tell RGFW_window_eventWait to stop waiting, to be ran from another thread
-*/
-RGFWDEF void RGFW_stopCheckEvents(void);
-
-/*! window managment functions*/
-RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */
-
-/*! moves window to a given point */
-RGFWDEF void RGFW_window_move(RGFW_window* win,
-	RGFW_point v/*!< new pos*/
-);
-
-#ifndef RGFW_NO_MONITOR
-	/*! move to a specific monitor */
-	RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */);
-#endif
-
-/*! resize window to a current size/area */
-RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */
-	RGFW_area a/*!< new size*/
-);
-
-/*! set the minimum size a user can shrink a window to a given size/area */
-RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a);
-/*! set the minimum size a user can extend a window to a given size/area */
-RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a);
-
-RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window size */
-RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/
-RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/
-
-/*! if the window should have a border or not (borderless) based on bool value of `border` */
-RGFWDEF void RGFW_window_setBorder(RGFW_window* win, b8 border);
-
-/*! turn on / off dnd (RGFW_ALLOW_DND stil must be passed to the window)*/
-RGFWDEF void RGFW_window_setDND(RGFW_window* win, b8 allow);
-
-#ifndef RGFW_NO_PASSTHROUGH
-	/*!! turn on / off mouse passthrough */
-	RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough);
-#endif 
-
-/*! rename window to a given string */
-RGFWDEF void RGFW_window_setName(RGFW_window* win,
-	char* name
-);
-
-RGFWDEF void RGFW_window_setIcon(RGFW_window* win, /*!< source window */
-	u8* icon /*!< icon bitmap */,
-	RGFW_area a /*!< width and height of the bitmap*/,
-	i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */
-); /*!< image resized by default */
-
-/*!< sets mouse to bitmap (very simular to RGFW_window_setIcon), image NOT resized by default*/
-RGFWDEF void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels);
-
-/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */
-RGFWDEF	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse);
-
-RGFWDEF void RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */
-/*
-	Locks cursor at the center of the window
-	win->event.point become raw mouse movement data 
-
-	this is useful for a 3D camera
-*/
-RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area);
-/*! stop holding the mouse and let it move freely */
-RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win);
-
-/*! hide the window */
-RGFWDEF void RGFW_window_hide(RGFW_window* win);
-/*! show the window */
-RGFWDEF void RGFW_window_show(RGFW_window* win);
-
-/*
-	makes it so `RGFW_window_shouldClose` returns true
-	by setting the window event.type to RGFW_quit
-*/
-RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win);
-
-/*! where the mouse is on the screen */
-RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void);
-
-/*! where the mouse is on the window */
-RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win);
-
-/*! show the mouse or hide the mouse*/
-RGFWDEF void RGFW_window_showMouse(RGFW_window* win, i8 show);
-/*! move the mouse to a set x, y pos*/
-RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v);
-
-/*! if the window should close (RGFW_close was sent or escape was pressed) */
-RGFWDEF b8 RGFW_window_shouldClose(RGFW_window* win);
-/*! if window is fullscreen'd */
-RGFWDEF b8 RGFW_window_isFullscreen(RGFW_window* win);
-/*! if window is hidden */
-RGFWDEF b8 RGFW_window_isHidden(RGFW_window* win);
-/*! if window is minimized */
-RGFWDEF b8 RGFW_window_isMinimized(RGFW_window* win);
-/*! if window is maximized */
-RGFWDEF b8 RGFW_window_isMaximized(RGFW_window* win);
-
-/** @} */ 
-
-/** * @defgroup Monitor
-* @{ */ 
-
-#ifndef RGFW_NO_MONITOR
-/*
-scale the window to the monitor,
-this is run by default if the user uses the arg `RGFW_SCALE_TO_MONITOR` during window creation
-*/
-RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
-/*! get the struct of the window's monitor  */
-RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win);
-#endif
-
-/** @} */ 
-
-/** * @defgroup Input
-* @{ */ 
-
-/*error handling*/
-RGFWDEF b8 RGFW_Error(void); /*!< returns true if an error has occurred (doesn't print errors itself) */
-
-/*! returns true if the key should be shifted */
-RGFWDEF b8 RGFW_shouldShift(u32 keycode, u8 lockState);
-
-/*! get char from RGFW keycode (using a LUT), uses shift'd version if shift = true */
-RGFWDEF char RGFW_keyCodeToChar(u32 keycode, b8 shift);
-/*! get char from RGFW keycode (using a LUT), uses lockState for shouldShift) */
-RGFWDEF char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState);
-
-/*! if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus.*/
-RGFWDEF b8 RGFW_isPressed(RGFW_window* win, u8 key); /*!< if key is pressed (key code)*/
-
-RGFWDEF b8 RGFW_wasPressed(RGFW_window* win, u8 key); /*!< if key was pressed (checks previous state only) (key code)*/
-
-RGFWDEF b8 RGFW_isHeld(RGFW_window* win, u8 key); /*!< if key is held (key code)*/
-RGFWDEF b8 RGFW_isReleased(RGFW_window* win, u8 key); /*!< if key is released (key code)*/
-
-/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */
-RGFWDEF b8 RGFW_isClicked(RGFW_window* win, u8 key /*!< key code*/);
-
-/*! if a mouse button is pressed */
-RGFWDEF b8 RGFW_isMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ );
-/*! if a mouse button is held */
-RGFWDEF b8 RGFW_isMouseHeld(RGFW_window* win, u8 button /*!< mouse button code */ );
-/*! if a mouse button was released */
-RGFWDEF b8 RGFW_isMouseReleased(RGFW_window* win, u8 button /*!< mouse button code */ );
-/*! if a mouse button was pressed (checks previous state only) */
-RGFWDEF b8 RGFW_wasMousePressed(RGFW_window* win, u8 button /*!< mouse button code */ );
-/** @} */ 
-
-/** * @defgroup Clipboard
-* @{ */ 
-RGFWDEF char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */
-RGFWDEF void RGFW_clipboardFree(char* str); /*!< the string returned from RGFW_readClipboard must be freed */
-
-RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */
-/** @} */ 
-
-/**
-	
-	
-	Event callbacks, 
-	these are completely optional, you can use the normal 
-	RGFW_checkEvent() method if you prefer that
-
-* @defgroup Callbacks
-* @{ 
-*/
-
-/*! RGFW_windowMoved, the window and its new rect value  */
-typedef void (* RGFW_windowmovefunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_windowResized, the window and its new rect value  */
-typedef void (* RGFW_windowresizefunc)(RGFW_window* win, RGFW_rect r);
-/*! RGFW_quit, the window that was closed */
-typedef void (* RGFW_windowquitfunc)(RGFW_window* win);
-/*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its inFocus */
-typedef void (* RGFW_focusfunc)(RGFW_window* win, b8 inFocus);
-/*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
-typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, b8 status);
-/*! RGFW_mousePosChanged, the window that the move happened on and the new point of the mouse  */
-typedef void (* RGFW_mouseposfunc)(RGFW_window* win, RGFW_point point);
-/*! RGFW_dnd_init, the window, the point of the drop on the windows */
-typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point);
-/*! RGFW_windowRefresh, the window that needs to be refreshed */
-typedef void (* RGFW_windowrefreshfunc)(RGFW_window* win);
-/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the keycode, the string version, the state of mod keys, if it was a press (else it's a release) */
-typedef void (* RGFW_keyfunc)(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed);
-/*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release)  */
-typedef void (* RGFW_mousebuttonfunc)(RGFW_window* win, u8 button, double scroll, b8 pressed);
-/*! RGFW_jsButtonPressed / RGFW_jsButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
-typedef void (* RGFW_jsButtonfunc)(RGFW_window* win, u16 joystick, u8 button, b8 pressed);
-/*! RGFW_jsAxisMove, the window that got the event, the joystick in question, the axis values and the amount of axises */
-typedef void (* RGFW_jsAxisfunc)(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount);
-
-
-/*!  RGFW_dnd, the window that had the drop, the drop data and the amount files dropped returns previous callback function (if it was set) */
-#ifdef RGFW_ALLOC_DROPFILES
-	typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount);
-#else
-	typedef void (* RGFW_dndfunc)(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount);
-#endif
-/*! set callback for a window move event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func);
-/*! set callback for a window resize event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func);
-/*! set callback for a window quit event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func);
-/*! set callback for a mouse move event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func);
-/*! set callback for a window refresh event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func);
-/*! set callback for a window focus change event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func);
-/*! set callback for a mouse notify event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func);
-/*! set callback for a drop event event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func);
-/*! set callback for a start of a drop event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func);
-/*! set callback for a key (press / release ) event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func);
-/*! set callback for a mouse button (press / release ) event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func);
-/*! set callback for a controller button (press / release ) event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func);
-/*! set callback for a joystick axis mov event returns previous callback function (if it was set)  */
-RGFWDEF RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func);
-
-/** @} */ 
-
-/** * @defgroup Threads
-* @{ */ 
-
-#ifndef RGFW_NO_THREADS
-	/*! threading functions*/
-
-	/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */
-	/*
-		I'd suggest you use sili's threading functions instead
-		if you're going to use sili
-		which is a good idea generally
-	*/
-
-	#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WEBASM) 
-		typedef void* (* RGFW_threadFunc_ptr)(void*);
-	#else
-		typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter);  
-	#endif
-
-	RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread*/
-	RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread*/
-	RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */
-	RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority  */
-#endif
-
-/** @} */ 
-
-/** * @defgroup joystick
-* @{ */ 
-
-/*! joystick count starts at 0*/
-/*!< register joystick to window based on a number (the number is based on when it was connected eg. /dev/js0)*/
-RGFWDEF u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber);
-RGFWDEF u16 RGFW_registerJoystickF(RGFW_window* win, char* file);
-
-RGFWDEF u32 RGFW_isPressedJS(RGFW_window* win, u16 controller, u8 button);
-
-/** @} */ 
-
-/** * @defgroup graphics_API
-* @{ */ 
-
-/*!< make the window the current opengl drawing context
-
-	NOTE:
- 	if you want to switch the graphics context's thread, 
-	you have to run RGFW_window_makeCurrent(NULL); on the old thread
-	then RGFW_window_makeCurrent(valid_window) on the new thread
-*/
-RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win);
-
-/*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */
-RGFWDEF u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap);
-
-/* supports openGL, directX, OSMesa, EGL and software rendering */
-RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */
-RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval);
-
-RGFWDEF void RGFW_window_setGPURender(RGFW_window* win, i8 set);
-RGFWDEF void RGFW_window_setCPURender(RGFW_window* win, i8 set);
-
-/*! native API functions */
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
-	/*! OpenGL init hints */
-	RGFWDEF void RGFW_setGLStencil(i32 stencil); /*!< set stencil buffer bit size (8 by default) */
-	RGFWDEF void RGFW_setGLSamples(i32 samples); /*!< set number of sampiling buffers (4 by default) */
-	RGFWDEF void RGFW_setGLStereo(i32 stereo); /*!< use GL_STEREO (GL_FALSE by default) */
-	RGFWDEF void RGFW_setGLAuxBuffers(i32 auxBuffers); /*!< number of aux buffers (0 by default) */
-
-	/*! which profile to use for the opengl verion */
-	typedef RGFW_ENUM(u8, RGFW_GL_profile)  { RGFW_GL_CORE = 0,  RGFW_GL_COMPATIBILITY  };
-	/*! Set OpenGL version hint (core or compatibility profile)*/
-	RGFWDEF void RGFW_setGLVersion(RGFW_GL_profile profile, i32 major, i32 minor);
-	RGFWDEF void RGFW_setDoubleBuffer(b8 useDoubleBuffer); 
-    RGFWDEF void* RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */
-    RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */
-#elif defined(RGFW_DIRECTX)
-	typedef struct {
-		IDXGIFactory* pFactory;
-		IDXGIAdapter* pAdapter;
-		ID3D11Device* pDevice;
-		ID3D11DeviceContext* pDeviceContext;
-	} RGFW_directXinfo;
-
-	/*
-		RGFW stores a global instance of RGFW_directXinfo,
-		you can use this function to get a pointer the instance
-	*/
-	RGFWDEF RGFW_directXinfo* RGFW_getDirectXInfo(void);
-#endif
-
-/** @} */ 
-
-/** * @defgroup Supporting
-* @{ */ 
-RGFWDEF u64 RGFW_getTime(void); /*!< get time in seconds */
-RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds */
-RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */
-
-/*!
-	key codes and mouse icon enums
-*/
-
-typedef RGFW_ENUM(u8, RGFW_Key) {
-	RGFW_KEY_NULL = 0,
-	RGFW_Escape,
-	RGFW_F1,
-	RGFW_F2,
-	RGFW_F3,
-	RGFW_F4,
-	RGFW_F5,
-	RGFW_F6,
-	RGFW_F7,
-	RGFW_F8,
-	RGFW_F9,
-	RGFW_F10,
-	RGFW_F11,
-	RGFW_F12,
-
-	RGFW_Backtick,
-
-	RGFW_0,
-	RGFW_1,
-	RGFW_2,
-	RGFW_3,
-	RGFW_4,
-	RGFW_5,
-	RGFW_6,
-	RGFW_7,
-	RGFW_8,
-	RGFW_9,
-
-	RGFW_Minus,
-	RGFW_Equals,
-	RGFW_BackSpace,
-	RGFW_Tab,
-	RGFW_CapsLock,
-	RGFW_ShiftL,
-	RGFW_ControlL,
-	RGFW_AltL,
-	RGFW_SuperL,
-	RGFW_ShiftR,
-	RGFW_ControlR,
-	RGFW_AltR,
-	RGFW_SuperR,
-	RGFW_Space,
-
-	RGFW_a,
-	RGFW_b,
-	RGFW_c,
-	RGFW_d,
-	RGFW_e,
-	RGFW_f,
-	RGFW_g,
-	RGFW_h,
-	RGFW_i,
-	RGFW_j,
-	RGFW_k,
-	RGFW_l,
-	RGFW_m,
-	RGFW_n,
-	RGFW_o,
-	RGFW_p,
-	RGFW_q,
-	RGFW_r,
-	RGFW_s,
-	RGFW_t,
-	RGFW_u,
-	RGFW_v,
-	RGFW_w,
-	RGFW_x,
-	RGFW_y,
-	RGFW_z,
-
-	RGFW_Period,
-	RGFW_Comma,
-	RGFW_Slash,
-	RGFW_Bracket,
-	RGFW_CloseBracket,
-	RGFW_Semicolon,
-	RGFW_Return,
-	RGFW_Quote,
-	RGFW_BackSlash,
-
-	RGFW_Up,
-	RGFW_Down,
-	RGFW_Left,
-	RGFW_Right,
-
-	RGFW_Delete,
-	RGFW_Insert,
-	RGFW_End,
-	RGFW_Home,
-	RGFW_PageUp,
-	RGFW_PageDown,
-
-	RGFW_Numlock,
-	RGFW_KP_Slash,
-	RGFW_Multiply,
-	RGFW_KP_Minus,
-	RGFW_KP_1,
-	RGFW_KP_2,
-	RGFW_KP_3,
-	RGFW_KP_4,
-	RGFW_KP_5,
-	RGFW_KP_6,
-	RGFW_KP_7,
-	RGFW_KP_8,
-	RGFW_KP_9,
-	RGFW_KP_0,
-	RGFW_KP_Period,
-	RGFW_KP_Return,
-
-	final_key,
-};
-
-
-typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
-	RGFW_MOUSE_NORMAL = 0,
-	RGFW_MOUSE_ARROW,
-	RGFW_MOUSE_IBEAM,
-	RGFW_MOUSE_CROSSHAIR,
-	RGFW_MOUSE_POINTING_HAND,
-	RGFW_MOUSE_RESIZE_EW,
-	RGFW_MOUSE_RESIZE_NS,
-	RGFW_MOUSE_RESIZE_NWSE,
-	RGFW_MOUSE_RESIZE_NESW,
-	RGFW_MOUSE_RESIZE_ALL,
-	RGFW_MOUSE_NOT_ALLOWED,
-};
-
-/** @} */ 
-
-#endif /* RGFW_HEADER */
-
-/*
-Example to get you started :
-
-linux : gcc main.c -lX11 -lXcursor -lGL
-windows : gcc main.c -lopengl32 -lshell32 -lgdi32
-macos : gcc main.c -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
-
-#define RGFW_IMPLEMENTATION
-#include "RGFW.h"
-
-u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF,    0xFF, 0x00, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF,   0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF};
-
-int main() {
-	RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(500, 500, 500, 500), (u64)0);
-
-	RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4);
-
-	for (;;) {
-		RGFW_window_checkEvent(win); // NOTE: checking events outside of a while loop may cause input lag
-		if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape))
-			break;
-
-		RGFW_window_swapBuffers(win);
-
-		glClearColor(0xFF, 0XFF, 0xFF, 0xFF);
-		glClear(GL_COLOR_BUFFER_BIT);
-	}
-
-	RGFW_window_close(win);
-}
-
-	compiling :
-
-	if you wish to compile the library all you have to do is create a new file with this in it
-
-	rgfw.c
-	#define RGFW_IMPLEMENTATION
-	#include "RGFW.h"
-
-	then you can use gcc (or whatever compile you wish to use) to compile the library into object file
-
-	ex. gcc -c RGFW.c -fPIC
-
-	after you compile the library into an object file, you can also turn the object file into an static or shared library
-
-	(commands ar and gcc can be replaced with whatever equivalent your system uses)
-	static : ar rcs RGFW.a RGFW.o
-	shared :
-		windows:
-			gcc -shared RGFW.o -lopengl32 -lshell32 -lgdi32 -o RGFW.dll
-		linux:
-			gcc -shared RGFW.o -lX11 -lXcursor -lGL -o RGFW.so
-		macos:
-			gcc -shared RGFW.o -framework Foundation -framework AppKit -framework OpenGL -framework CoreVideo
-*/
-
-#ifdef RGFW_X11
-	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) l
-#elif defined(RGFW_WINDOWS)
-	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) w
-#elif defined(RGFW_MACOS)
-	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) m
-#elif defined(RGFW_WEBASM)
-	#define RGFW_OS_BASED_VALUE(l, w, m, h, ww) h
-#elif defined(RGFW_WAYLAND)
-    #define RGFW_OS_BASED_VALUE(l, w, m, h, ww) ww  
-#endif
-
-
-#ifdef RGFW_IMPLEMENTATION
-
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <assert.h>
-
-/*
-RGFW_IMPLEMENTATION starts with generic RGFW defines
-
-This is the start of keycode data
-
-	Why not use macros instead of the numbers itself?
-	Windows -> Not all virtual keys are macros (VK_0 - VK_1, VK_a - VK_z)
-	Linux -> Only symcodes are values, (XK_0 - XK_1, XK_a - XK_z) are larger than 0xFF00, I can't find any way to work with them without making the array an unreasonable size
-	MacOS -> windows and linux already don't have keycodes as macros, so there's no point
-*/
-
-
-
-/* 
-	the c++ compiler doesn't support setting up an array like, 
-	we'll have to do it during runtime using a function & this messy setup
-*/
-#ifndef __cplusplus
-#define RGFW_NEXT ,
-#define RGFW_MAP
-#else 
-#define RGFW_NEXT ;
-#define RGFW_MAP RGFW_keycodes
-#endif
-
-#ifdef RGFW_WAYLAND
-#include <linux/input-event-codes.h>
-#endif
-
-u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(136, 337, 128, DOM_VK_WIN_OEM_CLEAR + 1, 130)] = {
-#ifdef __cplusplus
-	0
-};
-void RGFW_init_keys(void) {
-#endif
-	RGFW_MAP [RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE)] = RGFW_Backtick 		RGFW_NEXT
-
-	RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x30, 29, DOM_VK_0, KEY_0)] = RGFW_0 					RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x31, 18, DOM_VK_1, KEY_1)] = RGFW_1						RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x32, 19, DOM_VK_2, KEY_2)] = RGFW_2						RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x33, 20, DOM_VK_3, KEY_3)] = RGFW_3						RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x34, 21, DOM_VK_4, KEY_4)] = RGFW_4						RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x35, 23, DOM_VK_5, KEY_5)] = RGFW_5                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x36, 22, DOM_VK_6, KEY_6)] = RGFW_6                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x37, 26, DOM_VK_7, KEY_7)] = RGFW_7                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x38, 28, DOM_VK_8, KEY_8)] = RGFW_8                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x39, 25, DOM_VK_9, KEY_9)] = RGFW_9,
-
-	RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x20, 49, DOM_VK_SPACE, KEY_SPACE)] = RGFW_Space,
-
-	RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x41, 0, DOM_VK_A, KEY_A)] = RGFW_a                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x42, 11, DOM_VK_B, KEY_B)] = RGFW_b                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x43, 8, DOM_VK_C, KEY_C)] = RGFW_c                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x44, 2, DOM_VK_D, KEY_D)] = RGFW_d                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x45, 14, DOM_VK_E, KEY_E)] = RGFW_e                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x46, 3, DOM_VK_F, KEY_F)] = RGFW_f                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x47, 5, DOM_VK_G, KEY_G)] = RGFW_g                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x48, 4, DOM_VK_H, KEY_H)] = RGFW_h                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x49, 34, DOM_VK_I, KEY_I)] = RGFW_i                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x4A, 38, DOM_VK_J, KEY_J)] = RGFW_j                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x4B, 40, DOM_VK_K, KEY_K)] = RGFW_k                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x4C, 37, DOM_VK_L, KEY_L)] = RGFW_l                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x4D, 46, DOM_VK_M, KEY_M)] = RGFW_m                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x4E, 45, DOM_VK_N, KEY_N)] = RGFW_n                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x4F, 31, DOM_VK_O, KEY_O)] = RGFW_o                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x50, 35, DOM_VK_P, KEY_P)] = RGFW_p                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x51, 12, DOM_VK_Q, KEY_Q)] = RGFW_q                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x52, 15, DOM_VK_R, KEY_R)] = RGFW_r                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x53, 1, DOM_VK_S, KEY_S)] = RGFW_s                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x54, 17, DOM_VK_T, KEY_T)] = RGFW_t                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x55, 32, DOM_VK_U, KEY_U)] = RGFW_u                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x56, 9, DOM_VK_V, KEY_V)] = RGFW_v                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x57, 13, DOM_VK_W, KEY_W)] = RGFW_w                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x58, 7, DOM_VK_X, KEY_X)] = RGFW_x                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x59, 16, DOM_VK_Y, KEY_Y)] = RGFW_y                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x5A, 6, DOM_VK_Z, KEY_Z)] = RGFW_z,
-
-	RGFW_MAP [RGFW_OS_BASED_VALUE(60, 190, 47, DOM_VK_PERIOD, KEY_DOT)] = RGFW_Period             			RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(59, 188, 43, DOM_VK_COMMA, KEY_COMMA)] = RGFW_Comma               			RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(61, 191, 44, DOM_VK_SLASH, KEY_SLASH)] = RGFW_Slash               			RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(34, 219, 33, DOM_VK_OPEN_BRACKET, KEY_LEFTBRACE)] = RGFW_Bracket      			RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(35, 221, 30, DOM_VK_CLOSE_BRACKET, KEY_RIGHTBRACE)] = RGFW_CloseBracket             RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(47, 186, 41, DOM_VK_SEMICOLON, KEY_SEMICOLON)] = RGFW_Semicolon                 RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(48, 222, 39, DOM_VK_QUOTE, KEY_APOSTROPHE)] = RGFW_Quote                 			RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(51, 322, 42, DOM_VK_BACK_SLASH, KEY_BACKSLASH)] = RGFW_BackSlash,
-	
-	RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x0D, 36, DOM_VK_RETURN, KEY_ENTER)] = RGFW_Return              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x2E, 118, DOM_VK_DELETE, KEY_DELETE)] = RGFW_Delete                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x90, 72, DOM_VK_NUM_LOCK, KEY_NUMLOCK)] = RGFW_Numlock               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x6F, 82, DOM_VK_DIVIDE, KEY_KPSLASH)] = RGFW_KP_Slash               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x6A, 76, DOM_VK_MULTIPLY, KEY_KPASTERISK)] = RGFW_Multiply              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x6D, 67, DOM_VK_SUBTRACT, KEY_KPMINUS)] = RGFW_KP_Minus              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x61, 84, DOM_VK_NUMPAD1, KEY_KP1)] = RGFW_KP_1               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x62, 85, DOM_VK_NUMPAD2, KEY_KP2)] = RGFW_KP_2               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x63, 86, DOM_VK_NUMPAD3, KEY_KP3)] = RGFW_KP_3               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x64, 87, DOM_VK_NUMPAD4, KEY_KP4)] = RGFW_KP_4               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x65, 88, DOM_VK_NUMPAD5, KEY_KP5)] = RGFW_KP_5               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x66, 89, DOM_VK_NUMPAD6, KEY_KP6)] = RGFW_KP_6               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x67, 90, DOM_VK_NUMPAD7, KEY_KP7)] = RGFW_KP_7               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x68, 92, DOM_VK_NUMPAD8, KEY_KP8)] = RGFW_KP_8               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x69, 93, DOM_VK_NUMPAD9, KEY_KP9)] = RGFW_KP_9               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x60, 83, DOM_VK_NUMPAD0, KEY_KP0)] = RGFW_KP_0               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x6E, 65, DOM_VK_DECIMAL, KEY_KPDOT)] = RGFW_KP_Period              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x92, 77, 0, KEY_KPENTER)] = RGFW_KP_Return,
-	
-	RGFW_MAP [RGFW_OS_BASED_VALUE(20, 189, 27, DOM_VK_HYPHEN_MINUS, KEY_MINUS)] = RGFW_Minus              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(21, 187, 24, DOM_VK_EQUALS, KEY_EQUAL)] = RGFW_Equals               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(22, 8, 51, DOM_VK_BACK_SPACE, KEY_BACKSPACE)] = RGFW_BackSpace              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x09, 48, DOM_VK_TAB, KEY_TAB)] = RGFW_Tab                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(66, 20, 57, DOM_VK_CAPS_LOCK, KEY_CAPSLOCK)] = RGFW_CapsLock               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x10, 56, DOM_VK_SHIFT, KEY_LEFTSHIFT)] = RGFW_ShiftL               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x11, 59, DOM_VK_CONTROL, KEY_LEFTCTRL)] = RGFW_ControlL               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(64,0x12, 58, DOM_VK_ALT, KEY_LEFTALT)] = RGFW_AltL                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x5B, 55, DOM_VK_WIN, KEY_LEFTMETA)] = RGFW_SuperL,
-	
-	#if !defined(RGFW_WINDOWS) && !defined(RGFW_MACOS) && !defined(RGFW_WEBASM)
-	RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11, 59, 0, KEY_RIGHTCTRL)] = RGFW_ControlR               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(135, 0xA4, 55, 0, KEY_RIGHTMETA)] = RGFW_SuperR,
-	RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x5C, 56, 0, KEY_RIGHTSHIFT)] = RGFW_ShiftR              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(108, 165, 58, 0, KEY_RIGHTALT)] = RGFW_AltR,
-	#endif
-
-	RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x70, 127, DOM_VK_F1, KEY_F1)] = RGFW_F1                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x71, 121, DOM_VK_F2, KEY_F2)] = RGFW_F2                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x72, 100, DOM_VK_F3, KEY_F3)] = RGFW_F3                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x73, 119, DOM_VK_F4, KEY_F4)] = RGFW_F4                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x74, 97, DOM_VK_F5, KEY_F5)] = RGFW_F5              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x75, 98, DOM_VK_F6, KEY_F6)] = RGFW_F6              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x76, 99, DOM_VK_F7, KEY_F7)] = RGFW_F7              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x77, 101, DOM_VK_F8, KEY_F8)] = RGFW_F8                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x78, 102, DOM_VK_F9, KEY_F9)] = RGFW_F9                 		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x79, 110, DOM_VK_F10, KEY_F10)] = RGFW_F10               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x7A, 104, DOM_VK_F11, KEY_F11)] = RGFW_F11               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x7B, 112, DOM_VK_F12, KEY_F12)] = RGFW_F12               RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x26, 126, DOM_VK_UP, KEY_UP)] = RGFW_Up                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x28, 125, DOM_VK_DOWN, KEY_DOWN)] = RGFW_Down                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x25, 123, DOM_VK_LEFT, KEY_LEFT)] = RGFW_Left                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x27, 124, DOM_VK_RIGHT, KEY_RIGHT)] = RGFW_Right              RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x2D, 115, DOM_VK_INSERT, KEY_INSERT)] = RGFW_Insert                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x23, 120, DOM_VK_END, KEY_END)] = RGFW_End                  		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(112, 336, 117, DOM_VK_PAGE_UP, KEY_PAGEUP)] = RGFW_PageUp                		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(117, 325, 122, DOM_VK_PAGE_DOWN, KEY_PAGEDOWN)] = RGFW_PageDown            RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x1B, 53, DOM_VK_ESCAPE, KEY_ESC)] = RGFW_Escape                   		RGFW_NEXT
-	RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x24, 116, DOM_VK_HOME, KEY_HOME)] = RGFW_Home                    		RGFW_NEXT
-#ifndef __cplusplus
-};
-#else 
-}
-#endif
-
-#undef RGFW_NEXT
-#undef RGFW_MAP
-
-typedef struct {
-	b8 current  : 1;
-	b8 prev  : 1;
-} RGFW_keyState;
-
-RGFW_keyState RGFW_keyboard[final_key] = { {0, 0} };
-
-RGFWDEF u32 RGFW_apiKeyCodeToRGFW(u32 keycode);
-
-u32 RGFW_apiKeyCodeToRGFW(u32 keycode) {
-	#ifdef __cplusplus
-	if (RGFW_OS_BASED_VALUE(49, 192, 50, DOM_VK_BACK_QUOTE, KEY_GRAVE) != RGFW_Backtick) {
-		RGFW_init_keys();
-	}
-	#endif
-
-	/* make sure the key isn't out of bounds */
-	if (keycode > sizeof(RGFW_keycodes) / sizeof(u8))
-		return 0;
-	
-	return RGFW_keycodes[keycode];
-}
-
-RGFWDEF void RGFW_resetKey(void);
-void RGFW_resetKey(void) {
-	size_t len = final_key; /*!< last_key == length */
-	
-	size_t i; /*!< reset each previous state  */
-	for (i = 0; i < len; i++)
-		RGFW_keyboard[i].prev = 0;
-}
-
-b8 RGFW_shouldShift(u32 keycode, u8 lockState) {
-    #define RGFW_xor(x, y) (( (x) && (!(y)) ) ||  ((y) && (!(x)) ))
-    b8 caps4caps = (lockState & RGFW_CAPSLOCK) && ((keycode >= RGFW_a) && (keycode <= RGFW_z));
-    b8 shouldShift = RGFW_xor((RGFW_isPressed(NULL, RGFW_ShiftL) || RGFW_isPressed(NULL, RGFW_ShiftR)), caps4caps);
-    #undef RGFW_xor
-
-	return shouldShift;
-}	
-
-char RGFW_keyCodeToChar(u32 keycode, b8 shift) {
-    static const char map[] = {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '`', '0', '1', '2', '3', '4', '5', '6', '7', '8', 
-        '9', '-', '=', 0, '\t',  0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.', ',', '/', '[', ']',  ';', '\n', '\'', '\\', 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  '/', '*', '-', '1', '2', '3',  '3', '5', '6', '7', '8',  '9', '0', '\n'
-    };
-
-    static const char mapCaps[] = {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '~', ')', '!', '@', '#', '$', '%', '^', '&', '*', 
-        '(', '_', '+', 0, '0',  0, 0, 0, 0, 0, 0, 0, 0, 0, ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
-        'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
-        'X', 'Y', 'Z', '>', '<', '?', '{', '}',  ':', '\n', '"', '|', 
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '?', '*', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-    };
-
-    if (shift == RGFW_FALSE)
-        return map[keycode]; 
-    return mapCaps[keycode];
-}
-
-char RGFW_keyCodeToCharAuto(u32 keycode, u8 lockState) { return RGFW_keyCodeToChar(keycode, RGFW_shouldShift(keycode, lockState)); }
-
-/*
-	this is the end of keycode data
-*/
-
-/* joystick data */
-u8 RGFW_jsPressed[4][16]; /*!< if a key is currently pressed or not (per joystick) */
-
-i32 RGFW_joysticks[4]; /*!< limit of 4 joysticks at a time */
-u16 RGFW_joystickCount; /*!< the actual amount of joysticks */
-
-/* 
-	event callback defines start here
-*/
-
-
-/*
-	These exist to avoid the 
-	if (func == NULL) check 
-	for (allegedly) better performance
-*/
-void RGFW_windowmovefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-void RGFW_windowresizefuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
-void RGFW_windowquitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); }
-void RGFW_focusfuncEMPTY(RGFW_window* win, b8 inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);}
-void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, b8 status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);}
-void RGFW_mouseposfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
-void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
-void RGFW_windowrefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); }
-void RGFW_keyfuncEMPTY(RGFW_window* win, u32 keycode, char keyName[16], u8 lockState, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(keycode); RGFW_UNUSED(keyName); RGFW_UNUSED(lockState); RGFW_UNUSED(pressed);}
-void RGFW_mousebuttonfuncEMPTY(RGFW_window* win, u8 button, double scroll, b8 pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);}
-void RGFW_jsButtonfuncEMPTY(RGFW_window* win, u16 joystick, u8 button, b8 pressed){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(button); RGFW_UNUSED(pressed); }
-void RGFW_jsAxisfuncEMPTY(RGFW_window* win, u16 joystick, RGFW_point axis[2], u8 axisesCount){RGFW_UNUSED(win); RGFW_UNUSED(joystick); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); }
-
-#ifdef RGFW_ALLOC_DROPFILES
-void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
-#else
-void RGFW_dndfuncEMPTY(RGFW_window* win, char droppedFiles[RGFW_MAX_DROPS][RGFW_MAX_PATH], u32 droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
-#endif
-
-RGFW_windowmovefunc RGFW_windowMoveCallback = RGFW_windowmovefuncEMPTY;
-RGFW_windowresizefunc RGFW_windowResizeCallback = RGFW_windowresizefuncEMPTY;
-RGFW_windowquitfunc RGFW_windowQuitCallback = RGFW_windowquitfuncEMPTY;
-RGFW_mouseposfunc RGFW_mousePosCallback = RGFW_mouseposfuncEMPTY;
-RGFW_windowrefreshfunc RGFW_windowRefreshCallback = RGFW_windowrefreshfuncEMPTY;
-RGFW_focusfunc RGFW_focusCallback = RGFW_focusfuncEMPTY;
-RGFW_mouseNotifyfunc RGFW_mouseNotifyCallBack = RGFW_mouseNotifyfuncEMPTY;
-RGFW_dndfunc RGFW_dndCallback = RGFW_dndfuncEMPTY;
-RGFW_dndInitfunc RGFW_dndInitCallback = RGFW_dndInitfuncEMPTY;
-RGFW_keyfunc RGFW_keyCallback = RGFW_keyfuncEMPTY;
-RGFW_mousebuttonfunc RGFW_mouseButtonCallback = RGFW_mousebuttonfuncEMPTY;
-RGFW_jsButtonfunc RGFW_jsButtonCallback = RGFW_jsButtonfuncEMPTY;
-RGFW_jsAxisfunc RGFW_jsAxisCallback = RGFW_jsAxisfuncEMPTY;
-
-void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) { 
-	RGFW_window_eventWait(win, waitMS);
-
-	while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) { 
-		if (win->event.type == RGFW_quit) return; 
-	}
-	
-	#ifdef RGFW_WEBASM /* webasm needs to run the sleep function for asyncify */
-		RGFW_sleep(0);
-	#endif
-}
-
-RGFW_windowmovefunc RGFW_setWindowMoveCallback(RGFW_windowmovefunc func) { 
-	RGFW_windowmovefunc	prev =  (RGFW_windowMoveCallback == RGFW_windowmovefuncEMPTY) ? NULL : RGFW_windowMoveCallback;
-	RGFW_windowMoveCallback = func;
-	return prev;
-}
-RGFW_windowresizefunc RGFW_setWindowResizeCallback(RGFW_windowresizefunc func) {
-    RGFW_windowresizefunc prev = (RGFW_windowResizeCallback == RGFW_windowresizefuncEMPTY) ? NULL : RGFW_windowResizeCallback;
-    RGFW_windowResizeCallback = func;
-    return prev;
-}
-RGFW_windowquitfunc RGFW_setWindowQuitCallback(RGFW_windowquitfunc func) {
-    RGFW_windowquitfunc prev = (RGFW_windowQuitCallback == RGFW_windowquitfuncEMPTY) ? NULL : RGFW_windowQuitCallback;
-    RGFW_windowQuitCallback = func;
-    return prev;
-}
-
-RGFW_mouseposfunc RGFW_setMousePosCallback(RGFW_mouseposfunc func) {
-    RGFW_mouseposfunc prev = (RGFW_mousePosCallback == RGFW_mouseposfuncEMPTY) ? NULL : RGFW_mousePosCallback;
-    RGFW_mousePosCallback = func;
-    return prev;
-}
-RGFW_windowrefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowrefreshfunc func) {
-    RGFW_windowrefreshfunc prev = (RGFW_windowRefreshCallback == RGFW_windowrefreshfuncEMPTY) ? NULL : RGFW_windowRefreshCallback;
-    RGFW_windowRefreshCallback = func;
-    return prev;
-}
-RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func) {
-    RGFW_focusfunc prev = (RGFW_focusCallback == RGFW_focusfuncEMPTY) ? NULL : RGFW_focusCallback;
-    RGFW_focusCallback = func;
-    return prev;
-}
-
-RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallBack(RGFW_mouseNotifyfunc func) {
-    RGFW_mouseNotifyfunc prev = (RGFW_mouseNotifyCallBack == RGFW_mouseNotifyfuncEMPTY) ? NULL : RGFW_mouseNotifyCallBack;
-    RGFW_mouseNotifyCallBack = func;
-    return prev;
-}
-RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func) {
-    RGFW_dndfunc prev = (RGFW_dndCallback == RGFW_dndfuncEMPTY) ? NULL : RGFW_dndCallback;
-    RGFW_dndCallback = func;
-    return prev;
-}
-RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func) {
-    RGFW_dndInitfunc prev = (RGFW_dndInitCallback == RGFW_dndInitfuncEMPTY) ? NULL : RGFW_dndInitCallback;
-    RGFW_dndInitCallback = func;
-    return prev;
-}
-RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func) {
-    RGFW_keyfunc prev = (RGFW_keyCallback == RGFW_keyfuncEMPTY) ? NULL : RGFW_keyCallback;
-    RGFW_keyCallback = func;
-    return prev;
-}
-RGFW_mousebuttonfunc RGFW_setMouseButtonCallback(RGFW_mousebuttonfunc func) {
-    RGFW_mousebuttonfunc prev = (RGFW_mouseButtonCallback == RGFW_mousebuttonfuncEMPTY) ? NULL : RGFW_mouseButtonCallback;
-    RGFW_mouseButtonCallback = func;
-    return prev;
-}
-RGFW_jsButtonfunc RGFW_setjsButtonCallback(RGFW_jsButtonfunc func) {
-    RGFW_jsButtonfunc prev = (RGFW_jsButtonCallback == RGFW_jsButtonfuncEMPTY) ? NULL : RGFW_jsButtonCallback;
-    RGFW_jsButtonCallback = func;
-    return prev;
-}
-RGFW_jsAxisfunc RGFW_setjsAxisCallback(RGFW_jsAxisfunc func) {
-    RGFW_jsAxisfunc prev = (RGFW_jsAxisCallback == RGFW_jsAxisfuncEMPTY) ? NULL : RGFW_jsAxisCallback;
-    RGFW_jsAxisCallback = func;
-    return prev;
-}
-/* 
-no more event call back defines
-*/
-
-#define RGFW_ASSERT(check, str) {\
-	if (!(check)) { \
-		printf(str); \
-		assert(check); \
-	} \
-}
-
-b8 RGFW_error = 0;
-b8 RGFW_Error(void) { return RGFW_error; }
-
-#define SET_ATTRIB(a, v) { \
-    assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
-    attribs[index++] = a; \
-    attribs[index++] = v; \
-}
-	
-RGFW_area RGFW_bufferSize = {0, 0};
-void RGFW_setBufferSize(RGFW_area size) {
-	RGFW_bufferSize = size;
-}
-
-
-RGFWDEF RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args);
-
-/* do a basic initialization for RGFW_window, this is to standard it for each OS */
-RGFW_window* RGFW_window_basic_init(RGFW_rect rect, u16 args) {
-	RGFW_window* win = (RGFW_window*) RGFW_MALLOC(sizeof(RGFW_window)); /*!< make a new RGFW struct */
-
-	/* clear out dnd info */
-#ifdef RGFW_ALLOC_DROPFILES
-	win->event.droppedFiles = (char**) RGFW_MALLOC(sizeof(char*) * RGFW_MAX_DROPS);
-	u32 i;
-	for (i = 0; i < RGFW_MAX_DROPS; i++)
-		win->event.droppedFiles[i] = (char*) RGFW_CALLOC(RGFW_MAX_PATH, sizeof(char));
-#endif
-
-	/* X11 requires us to have a display to get the screen size */
-	#ifndef RGFW_X11 
-	RGFW_area screenR = RGFW_getScreenSize();
-	#else
-	win->src.display = XOpenDisplay(NULL);
-	assert(win->src.display != NULL);
-
-	Screen* scrn = DefaultScreenOfDisplay((Display*)win->src.display);
-	RGFW_area screenR = RGFW_AREA((u32)scrn->width, (u32)scrn->height);
-	#endif
-	
-	/* rect based the requested args */
-	if (args & RGFW_FULLSCREEN)
-		rect = RGFW_RECT(0, 0, screenR.w, screenR.h);
-
-	/* set and init the new window's data */
-	win->r = rect;
-	win->event.inFocus = 1;
-	win->event.droppedFilesCount = 0;
-	RGFW_joystickCount = 0;
-	win->_winArgs = 0;
-	win->event.lockState = 0;
-
-	return win;
-}
-
-#ifndef RGFW_NO_MONITOR
-void RGFW_window_scaleToMonitor(RGFW_window* win) {
-	RGFW_monitor monitor = RGFW_window_getMonitor(win);
-	
-	RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleX * (float)win->r.h)));
-}
-#endif
-
-RGFW_window* RGFW_root = NULL;
-
-
-#define RGFW_HOLD_MOUSE			(1L<<2) /*!< hold the moues still */
-#define RGFW_MOUSE_LEFT 		(1L<<3) /* if mouse left the window */
-
-#ifdef RGFW_MACOS
-RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer);
-RGFWDEF void* RGFW_cocoaGetLayer(void);
-#endif
-
-char* RGFW_className = NULL;
-void RGFW_setClassName(char* name) {
-	RGFW_className = name;
-}
-
-void RGFW_clipboardFree(char* str) { RGFW_FREE(str); }
-
-RGFW_keyState RGFW_mouseButtons[5] = { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} };
-
-b8 RGFW_isMousePressed(RGFW_window* win, u8 button) {
-	assert(win != NULL);
-	return RGFW_mouseButtons[button].current && (win != NULL) && win->event.inFocus; 
-}
-b8 RGFW_wasMousePressed(RGFW_window* win, u8 button) {
-	assert(win != NULL); 
-	return RGFW_mouseButtons[button].prev && (win != NULL) && win->event.inFocus; 
-}
-b8 RGFW_isMouseHeld(RGFW_window* win, u8 button) {
-	return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
-}
-b8 RGFW_isMouseReleased(RGFW_window* win, u8 button) {
-	return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));	
-}
-
-b8 RGFW_isPressed(RGFW_window* win, u8 key) {
-	return RGFW_keyboard[key].current && (win == NULL || win->event.inFocus);
-}
-
-b8 RGFW_wasPressed(RGFW_window* win, u8 key) {
-	return RGFW_keyboard[key].prev && (win == NULL || win->event.inFocus);
-}
-
-b8 RGFW_isHeld(RGFW_window* win, u8 key) {
-	return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
-}
-
-b8 RGFW_isClicked(RGFW_window* win, u8 key) {
-	return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key));
-}
-
-b8 RGFW_isReleased(RGFW_window* win, u8 key) {
-	return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));	
-}
-
-#if defined(RGFW_WINDOWS)  && defined(RGFW_DIRECTX) /* defines for directX context*/
-	RGFW_directXinfo RGFW_dxInfo;
-	RGFW_directXinfo* RGFW_getDirectXInfo(void) { return &RGFW_dxInfo; }
-#endif
-
-void RGFW_window_makeCurrent(RGFW_window* win) {
-#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
-	if (win == NULL)
-		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, NULL, NULL);
-	else
-		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, NULL);
-#elif defined(RGFW_OPENGL)
-	RGFW_window_makeCurrent_OpenGL(win);
-#else
-	RGFW_UNUSED(win)
-#endif
-}
-
-void RGFW_window_setGPURender(RGFW_window* win, i8 set) {
-	if (!set && !(win->_winArgs & RGFW_NO_GPU_RENDER))
-		win->_winArgs |= RGFW_NO_GPU_RENDER;
-		
-	else if (set && win->_winArgs & RGFW_NO_GPU_RENDER)
-		win->_winArgs ^= RGFW_NO_GPU_RENDER;
-}
-
-void RGFW_window_setCPURender(RGFW_window* win, i8 set) {
-	if (!set && !(win->_winArgs & RGFW_NO_CPU_RENDER))
-		win->_winArgs |= RGFW_NO_CPU_RENDER;
-
-	else if (set && win->_winArgs & RGFW_NO_CPU_RENDER)
-		win->_winArgs ^= RGFW_NO_CPU_RENDER;
-}
-
-void RGFW_window_maximize(RGFW_window* win) {
-	assert(win != NULL);
-
-	RGFW_area screen = RGFW_getScreenSize();
-
-	RGFW_window_move(win, RGFW_POINT(0, 0));
-	RGFW_window_resize(win, screen);
-}
-
-b8 RGFW_window_shouldClose(RGFW_window* win) {
-	assert(win != NULL);
-	return (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_Escape));
-}
-
-void RGFW_window_setShouldClose(RGFW_window* win) { win->event.type = RGFW_quit; RGFW_windowQuitCallback(win); }
-
-#ifndef RGFW_NO_MONITOR
-	void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) {
-		RGFW_window_move(win, RGFW_POINT(m.rect.x + win->r.x, m.rect.y + win->r.y));
-	}
-#endif
-
-RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect);
-RGFWDEF void RGFW_releaseCursor(RGFW_window* win);
-
-void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) {
-	if ((win->_winArgs & RGFW_HOLD_MOUSE))
-		return;
-	
-
-	if (!area.w && !area.h)
-		area = RGFW_AREA(win->r.w / 2, win->r.h / 2);
-		
-	win->_winArgs |= RGFW_HOLD_MOUSE;
-	RGFW_captureCursor(win, win->r);
-	RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
-}
-
-void RGFW_window_mouseUnhold(RGFW_window* win) {
-	if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
-		win->_winArgs ^= RGFW_HOLD_MOUSE;
-
-		RGFW_releaseCursor(win);
-	}
-}
-
-u32 RGFW_window_checkFPS(RGFW_window* win, u32 fpsCap) {
-	u64 deltaTime = RGFW_getTimeNS() - win->event.frameTime;
-
-	u32 output_fps = 0;
-	u64 fps = round(1e+9 / deltaTime);
-	output_fps= fps;
-
-	if (fpsCap && fps > fpsCap) {
-		u64 frameTimeNS = 1e+9 / fpsCap;
-		u64 sleepTimeMS = (frameTimeNS - deltaTime) / 1e6;
-
-		if (sleepTimeMS > 0) {
-			RGFW_sleep(sleepTimeMS);
-			win->event.frameTime = 0;
-		}
-	}
-
-	win->event.frameTime = RGFW_getTimeNS();
-	
-	if (fpsCap == 0) 
-		return (u32) output_fps;
-	
-	deltaTime = RGFW_getTimeNS() - win->event.frameTime2;
-	output_fps = round(1e+9 / deltaTime);
-	win->event.frameTime2 = RGFW_getTimeNS();
-
-	return output_fps;
-}
-
-u32 RGFW_isPressedJS(RGFW_window* win, u16 c, u8 button) { 
-	RGFW_UNUSED(win);
-	return RGFW_jsPressed[c][button]; 
-}
-
-#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-		static u8 RGFW_blk[] = { 0, 0, 0, 0 };
-		if (show == 0)
-			RGFW_window_setMouse(win, RGFW_blk, RGFW_AREA(1, 1), 4);
-		else
-			RGFW_window_setMouseDefault(win);
-	}
-#endif
-
-RGFWDEF void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock);	
-void RGFW_updateLockState(RGFW_window* win, b8 capital, b8 numlock) {
-	if (capital && !(win->event.lockState & RGFW_CAPSLOCK))
-		win->event.lockState |= RGFW_CAPSLOCK;
-	else if (!capital && (win->event.lockState & RGFW_CAPSLOCK))			
-		win->event.lockState ^= RGFW_CAPSLOCK;
-	
-	if (numlock && !(win->event.lockState & RGFW_NUMLOCK))
-		win->event.lockState |= RGFW_NUMLOCK;
-	else if (!numlock && (win->event.lockState & RGFW_NUMLOCK))
-		win->event.lockState ^= RGFW_NUMLOCK;
-}
-
-#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM) || defined(RGFW_WAYLAND)
-	struct timespec;
-
-	int nanosleep(const struct timespec* duration, struct timespec* rem);
-	int clock_gettime(clockid_t clk_id, struct timespec* tp);
-	int setenv(const char *name, const char *value, int overwrite);
-
-	void RGFW_window_setDND(RGFW_window* win, b8 allow) {
-		if (allow && !(win->_winArgs & RGFW_ALLOW_DND))
-			win->_winArgs |= RGFW_ALLOW_DND;
-
-		else if (!allow && (win->_winArgs & RGFW_ALLOW_DND))
-			win->_winArgs ^= RGFW_ALLOW_DND;
-	}
-#endif
-
-/*
-	graphics API specific code (end of generic code)
-	starts here 
-*/
-
-
-/* 
-	OpenGL defines start here   (Normal, EGL, OSMesa)
-*/
-
-#if defined(RGFW_OPENGL) || defined(RGFW_EGL) || defined(RGFW_OSMESA)
-	#ifdef RGFW_WINDOWS
-		#define WIN32_LEAN_AND_MEAN
-		#define OEMRESOURCE
-		#include <windows.h>
-	#endif
-
-	#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER)
-		#include <GL/gl.h>
-	#elif defined(__APPLE__)
-		#ifndef GL_SILENCE_DEPRECATION
-			#define GL_SILENCE_DEPRECATION
-		#endif
-		#include <OpenGL/gl.h>
-		#include <OpenGL/OpenGL.h>
-	#endif
-
-/* EGL, normal OpenGL only */
-#if !defined(RGFW_OSMESA) 
-	i32 RGFW_majorVersion = 0, RGFW_minorVersion = 0;
-	b8 RGFW_profile = RGFW_GL_CORE;
-	
-	#ifndef RGFW_EGL
-	i32 RGFW_STENCIL = 8, RGFW_SAMPLES = 4, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
-	#else
-	i32 RGFW_STENCIL = 0, RGFW_SAMPLES = 0, RGFW_STEREO = 0, RGFW_AUX_BUFFERS = 0, RGFW_DOUBLE_BUFFER = 1;
-	#endif
-
-
-	void RGFW_setGLStencil(i32 stencil) { RGFW_STENCIL = stencil; }
-	void RGFW_setGLSamples(i32 samples) { RGFW_SAMPLES = samples; }
-	void RGFW_setGLStereo(i32 stereo) { RGFW_STEREO = stereo; }
-	void RGFW_setGLAuxBuffers(i32 auxBuffers) { RGFW_AUX_BUFFERS = auxBuffers; }
-	void RGFW_setDoubleBuffer(b8 useDoubleBuffer) { RGFW_DOUBLE_BUFFER = useDoubleBuffer; }
-
-	void RGFW_setGLVersion(b8 profile, i32 major, i32 minor) {
-        RGFW_profile = profile;
-		RGFW_majorVersion = major;
-		RGFW_minorVersion = minor;
-	}
-
-/* OPENGL normal only (no EGL / OSMesa) */
-#ifndef RGFW_EGL
-
-#define RGFW_GL_RENDER_TYPE 		RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE,    	0x2003,		73, 0, 0)
-	#define RGFW_GL_ALPHA_SIZE 		RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE,       	0x201b,		11,     0, 0)
-	#define RGFW_GL_DEPTH_SIZE 		RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE,       	0x2022,		12,     0, 0)
-	#define RGFW_GL_DOUBLEBUFFER 		RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER,     	0x2011, 	5,  0, 0)   
-	#define RGFW_GL_STENCIL_SIZE 		RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE,	 	0x2023,	13,     0, 0)
-	#define RGFW_GL_SAMPLES			RGFW_OS_BASED_VALUE(GLX_SAMPLES, 		 	0x2042,	    55,     0, 0)
-	#define RGFW_GL_STEREO 			RGFW_OS_BASED_VALUE(GLX_STEREO,	 		 	0x2012,			6,  0, 0)
-	#define RGFW_GL_AUX_BUFFERS		RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS,	    0x2024,	7, 		    0, 0)
-
-#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-	#define RGFW_GL_DRAW 			RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE,	 	0x2001,					0, 0, 0)
-	#define RGFW_GL_DRAW_TYPE 		RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE,     	0x2013,						0, 0, 0)
-	#define RGFW_GL_FULL_FORMAT		RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR,   	 	0x2027,						0, 0, 0)
-	#define RGFW_GL_RED_SIZE		RGFW_OS_BASED_VALUE(GLX_RED_SIZE,         	0x2015,						0, 0, 0)
-	#define RGFW_GL_GREEN_SIZE		RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE,       	0x2017,						0, 0, 0)
-	#define RGFW_GL_BLUE_SIZE		RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 	 		0x2019,						0, 0, 0)
-	#define RGFW_GL_USE_RGBA		RGFW_OS_BASED_VALUE(GLX_RGBA_BIT,   	 	0x202B,						0, 0, 0)
-#endif
-
-#ifdef RGFW_WINDOWS
-	#define WGL_SUPPORT_OPENGL_ARB                    0x2010
-	#define WGL_COLOR_BITS_ARB                        0x2014
-	#define WGL_NUMBER_PIXEL_FORMATS_ARB 			0x2000
-	#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
-	#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
-	#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
-	#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
-	#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-	#define WGL_SAMPLE_BUFFERS_ARB               0x2041
-	#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
-	#define WGL_PIXEL_TYPE_ARB                        0x2013
-	#define WGL_TYPE_RGBA_ARB                         0x202B
-
-	#define WGL_TRANSPARENT_ARB   					  0x200A
-#endif
-	
-/*  The window'ing api needs to know how to render the data we (or opengl) give it 
-	MacOS and Windows do this using a structure called a "pixel format" 
-	X11 calls it a "Visual"
-	This function returns the attributes for the format we want */
-	static u32* RGFW_initFormatAttribs(u32 useSoftware) {
-		RGFW_UNUSED(useSoftware);
-		static u32 attribs[] = {
-								#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-								RGFW_GL_RENDER_TYPE,
-								RGFW_GL_FULL_FORMAT,
-								#endif
-								RGFW_GL_ALPHA_SIZE      , 8,
-								RGFW_GL_DEPTH_SIZE      , 24,
-								#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
-								RGFW_GL_DRAW, 1,
-								RGFW_GL_RED_SIZE        , 8,
-								RGFW_GL_GREEN_SIZE      , 8,
-								RGFW_GL_BLUE_SIZE       , 8,
-								RGFW_GL_DRAW_TYPE     , RGFW_GL_USE_RGBA,
-								#endif 
-
-								#ifdef RGFW_X11
-								GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
-								#endif	
-
-								#ifdef RGFW_MACOS
-								72,
-								8, 24,
-								#endif
-
-								#ifdef RGFW_WINDOWS
-								WGL_SUPPORT_OPENGL_ARB,		1,
-								WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
-								WGL_COLOR_BITS_ARB,	 32,
-								#endif
-
-								0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-		};
-
-		size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 13;
-
-#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
-		if (attVal) { \
-			attribs[index] = attrib;\
-			attribs[index + 1] = attVal;\
-			index += 2;\
-		}
-         
-        RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1);
-        
-        RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_STENCIL);
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_STEREO);
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_AUX_BUFFERS);
-
-#ifndef RGFW_X11
-		RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_SAMPLES);
-#endif 
-
-#ifdef RGFW_MACOS
-		if (useSoftware) {
-			RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
-		} else {
-			attribs[index] = RGFW_GL_RENDER_TYPE;
-			index += 1;
-		}
-#endif
-
-#ifdef RGFW_MACOS
-		/* macOS has the surface attribs and the opengl attribs connected for some reason
-			maybe this is to give macOS more control to limit openGL/the opengl version? */
-
-		attribs[index] = 99;
-		attribs[index + 1] = 0x1000;
-
-		if (RGFW_majorVersion >= 4 || RGFW_majorVersion >= 3) {
-			attribs[index + 1] = (u32) ((RGFW_majorVersion >= 4) ? 0x4100 : 0x3200);
-		}
-#endif
-
-		RGFW_GL_ADD_ATTRIB(0, 0);
-
-		return attribs;
-	}
-
-/* EGL only (no OSMesa nor normal OPENGL) */
-#elif defined(RGFW_EGL)
-
-#include <EGL/egl.h>
-
-#if defined(RGFW_LINK_EGL)
-	typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
-
-	PFNEGLINITIALIZEPROC eglInitializeSource;
-	PFNEGLGETCONFIGSPROC eglGetConfigsSource;
-	PFNEGLCHOOSECONFIGPROC eglChooseConfigSource;
-	PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource;
-	PFNEGLCREATECONTEXTPROC eglCreateContextSource;
-	PFNEGLMAKECURRENTPROC eglMakeCurrentSource;
-	PFNEGLGETDISPLAYPROC eglGetDisplaySource;
-	PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource;
-	PFNEGLSWAPINTERVALPROC eglSwapIntervalSource;
-	PFNEGLBINDAPIPROC eglBindAPISource;
-	PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource;
-	PFNEGLTERMINATEPROC eglTerminateSource;
-	PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource;
-
-#define eglInitialize eglInitializeSource
-#define eglGetConfigs eglGetConfigsSource
-#define eglChooseConfig eglChooseConfigSource
-#define eglCreateWindowSurface eglCreateWindowSurfaceSource
-#define eglCreateContext eglCreateContextSource
-#define eglMakeCurrent eglMakeCurrentSource
-#define eglGetDisplay eglGetDisplaySource
-#define eglSwapBuffers eglSwapBuffersSource
-#define eglSwapInterval eglSwapIntervalSource
-#define eglBindAPI eglBindAPISource
-#define eglDestroyContext eglDestroyContextSource
-#define eglTerminate eglTerminateSource
-#define eglDestroySurface eglDestroySurfaceSource;
-#endif
-
-
-#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
-#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
-
-#ifndef RGFW_GL_ADD_ATTRIB
-#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
-	if (attVal) { \
-		attribs[index] = attrib;\
-		attribs[index + 1] = attVal;\
-		index += 2;\
-	}
-#endif
-
-
-	void RGFW_createOpenGLContext(RGFW_window* win) {
-#if defined(RGFW_LINK_EGL)
-		eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize");
-		eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs");
-		eglChooseConfigSource = (PFNEGLCHOOSECONFIGPROC) eglGetProcAddress("eglChooseConfig");
-		eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface");
-		eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext");
-		eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent");
-		eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay");
-		eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers");
-		eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval");
-		eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI");
-		eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext");
-		eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate");
-		eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface");
-#endif /* RGFW_LINK_EGL */
-
-		#ifdef RGFW_WINDOWS
-		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc);
-		#elif defined(RGFW_MACOS)
-		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0);
-		#else
-		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
-		#endif
-
-		EGLint major, minor;
-
-		eglInitialize(win->src.EGL_display, &major, &minor);
-
-		#ifndef EGL_OPENGL_ES1_BIT
-		#define EGL_OPENGL_ES1_BIT 0x1
-		#endif
-
-		EGLint egl_config[] = {
-			EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
-			EGL_RENDERABLE_TYPE,
-			#ifdef RGFW_OPENGL_ES1
-			EGL_OPENGL_ES1_BIT,
-			#elif defined(RGFW_OPENGL_ES3)
-			EGL_OPENGL_ES3_BIT,
-			#elif defined(RGFW_OPENGL_ES2)
-			EGL_OPENGL_ES2_BIT,
-			#else
-			EGL_OPENGL_BIT,
-			#endif
-			EGL_NONE, EGL_NONE
-		};
-
-		EGLConfig config;
-		EGLint numConfigs;
-		eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs);
-
-		#if defined(RGFW_MACOS)
-		    void* layer = RGFW_cocoaGetLayer(); 
-		
-			RGFW_window_cocoaSetLayer(win, layer);
-			
-			win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL);
-		#else
-			win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
-		#endif
-
-		EGLint attribs[] = {
-			EGL_CONTEXT_CLIENT_VERSION,
-			#ifdef RGFW_OPENGL_ES1
-			1,
-			#else
-			2,
-			#endif
-			EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE, EGL_NONE
-		};
-
-		size_t index = 4;
-		RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_STENCIL);
-		RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_SAMPLES);
-
-        if (RGFW_DOUBLE_BUFFER)
-            RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_BACK_BUFFER);
-
-		if (RGFW_majorVersion) {
-			attribs[1] = RGFW_majorVersion;
-	
-			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_majorVersion);
-			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_minorVersion);
-
-			if (RGFW_profile == RGFW_GL_CORE) {
-				RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
-			}
-			else {
-				RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
-			}
-
-		}
-
-		#if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)
-		eglBindAPI(EGL_OPENGL_ES_API);
-		#else
-		eglBindAPI(EGL_OPENGL_API);		
-		#endif
-      		
-		win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs);
-		
-		if (win->src.EGL_context == NULL)
-			fprintf(stderr, "failed to create an EGL opengl context\n");
-
-		eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
-		eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-	}
-
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
-	}
-
-	#ifdef RGFW_APPLE
-	void* RGFWnsglFramework = NULL;
-	#elif defined(RGFW_WINDOWS)
-	static HMODULE wglinstance = NULL;
-	#endif
-
-	void* RGFW_getProcAddress(const char* procname) { 
-		#if defined(RGFW_WINDOWS)
-			void* proc = (void*) GetProcAddress(wglinstance, procname); 
-
-			if (proc)
-				return proc;
-		#endif
-
-		return (void*) eglGetProcAddress(procname); 
-	}
-
-	void RGFW_closeEGL(RGFW_window* win) {
-		eglDestroySurface(win->src.EGL_display, win->src.EGL_surface);
-		eglDestroyContext(win->src.EGL_display, win->src.EGL_context);
-
-		eglTerminate(win->src.EGL_display);
-	}
-	
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		
-		eglSwapInterval(win->src.EGL_display, swapInterval);
-
-	}
-#endif /* RGFW_EGL */
-
-/* 
-	end of RGFW_EGL defines
-*/
-
-/* OPENGL Normal / EGL defines only (no OS MESA)  Ends here */
-
-#elif defined(RGFW_OSMESA) /* OSmesa only */
-RGFWDEF void RGFW_OSMesa_reorganize(void);
-
-/* reorganize buffer for osmesa */
-void RGFW_OSMesa_reorganize(void) {
-	u8* row = (u8*) RGFW_MALLOC(win->r.w * 3);
-
-	i32 half_height = win->r.h / 2;
-	i32 stride = win->r.w * 3;
-
-	i32 y;
-	for (y = 0; y < half_height; ++y) {
-		i32 top_offset = y * stride;
-		i32 bottom_offset = (win->r.h - y - 1) * stride;
-		memcpy(row, win->buffer + top_offset, stride);
-		memcpy(win->buffer + top_offset, win->buffer + bottom_offset, stride);
-		memcpy(win->buffer + bottom_offset, row, stride);
-	}
-
-	RGFW_FREE(row);
-}
-#endif /* RGFW_OSMesa */
-
-#endif /* RGFW_GL (OpenGL, EGL, OSMesa )*/
-
-/*
-This is where OS specific stuff starts
-*/
-
-
-#if defined(RGFW_WAYLAND) || defined(RGFW_X11)
-	int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */
-
-	#ifdef __linux__
-		#include <linux/joystick.h>
-		#include <fcntl.h>
-		#include <unistd.h>
-		
-		RGFW_Event* RGFW_linux_updateJoystick(RGFW_window* win) {
-			static int xAxis = 0, yAxis = 0;
-			u8 i;
-			for (i = 0; i < RGFW_joystickCount; i++) {
-				struct js_event e;
-
-
-				if (RGFW_joysticks[i] == 0)
-					continue;
-
-				i32 flags = fcntl(RGFW_joysticks[i], F_GETFL, 0);
-				fcntl(RGFW_joysticks[i], F_SETFL, flags | O_NONBLOCK);
-
-				ssize_t bytes;
-				while ((bytes = read(RGFW_joysticks[i], &e, sizeof(e))) > 0) {
-					switch (e.type) {
-					case JS_EVENT_BUTTON:
-						win->event.type = e.value ? RGFW_jsButtonPressed : RGFW_jsButtonReleased;
-						win->event.button = e.number;
-						RGFW_jsPressed[i][e.number] = e.value;
-						RGFW_jsButtonCallback(win, i, e.number, e.value);
-						return &win->event;
-					case JS_EVENT_AXIS:
-						ioctl(RGFW_joysticks[i], JSIOCGAXES, &win->event.axisesCount);
-
-						if ((e.number == 0 || e.number % 2) && e.number != 1)
-							xAxis = e.value;
-						else
-							yAxis = e.value;
-
-						win->event.axis[e.number / 2].x = xAxis;
-						win->event.axis[e.number / 2].y = yAxis;
-						win->event.type = RGFW_jsAxisMove;
-						win->event.joystick = i;
-						RGFW_jsAxisCallback(win, i, win->event.axis, win->event.axisesCount);
-						return &win->event;
-
-						default: break;
-					}
-				}
-			}
-
-			return NULL;
-		}
-
-	#endif
-#endif
-
-/*
-
-
-Start of Linux / Unix defines
-
-
-*/
-
-#ifdef RGFW_X11
-#ifndef RGFW_NO_X11_CURSOR
-#include <X11/Xcursor/Xcursor.h>
-#endif
-#include <dlfcn.h>
-
-#ifndef RGFW_NO_DPI
-#include <X11/extensions/Xrandr.h>
-#include <X11/Xresource.h>
-#endif
-
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/keysymdef.h>
-#include <unistd.h>
-
-#include <X11/XKBlib.h> /* for converting keycode to string */
-#include <X11/cursorfont.h> /* for hiding */
-#include <X11/extensions/shapeconst.h>
-#include <X11/extensions/shape.h>
-#include <X11/extensions/XInput2.h>
-
-#include <limits.h> /* for data limits (mainly used in drag and drop functions) */
-#include <poll.h>
-
-
-#ifdef __linux__
-#include <linux/joystick.h>
-#endif
-
-	u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor};  
-	/*atoms needed for drag and drop*/
-	Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList;
-
-	Atom wm_delete_window = 0;
-
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-	typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
-	typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
-	typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
-#endif
-#ifdef RGFW_OPENGL
-	typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
-#endif
-
-#if !defined(RGFW_NO_X11_XI_PRELOAD)
-	typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
-	PFN_XISelectEvents XISelectEventsSrc = NULL;
-	#define XISelectEvents XISelectEventsSrc
-
-	void* X11Xihandle = NULL;
-#endif
-
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-	PFN_XcursorImageLoadCursor XcursorImageLoadCursorSrc = NULL;
-	PFN_XcursorImageCreate XcursorImageCreateSrc = NULL;
-	PFN_XcursorImageDestroy XcursorImageDestroySrc = NULL;
-
-#define XcursorImageLoadCursor XcursorImageLoadCursorSrc
-#define XcursorImageCreate XcursorImageCreateSrc
-#define XcursorImageDestroy XcursorImageDestroySrc
-
-	void* X11Cursorhandle = NULL;
-#endif
-
-	u32 RGFW_windowsOpen = 0;
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); }
-#endif
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi);
-	void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-		if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-			RGFW_bufferSize = RGFW_getScreenSize();
-		
-		win->buffer = (u8*)RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
-
-		#ifdef RGFW_OSMESA
-				win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
-				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-		#endif
-
-		win->src.bitmap = XCreateImage(
-			win->src.display, XDefaultVisual(win->src.display, vi->screen),
-			vi->depth,
-			ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h,
-			32, 0
-		);
-
-		win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL);
-
-		#else
-		RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
-		RGFW_UNUSED(vi)
-		#endif
-	}
-
-
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		static Atom _MOTIF_WM_HINTS = 0;
-		if (_MOTIF_WM_HINTS == 0 )
-			_MOTIF_WM_HINTS = XInternAtom(win->src.display, "_MOTIF_WM_HINTS", False);
-		
-		struct __x11WindowHints {
-			unsigned long flags, functions, decorations, status;
-			long input_mode;
-		} hints;
-		hints.flags = (1L << 1);
-		hints.decorations = border;
-
-		XChangeProperty(
-			win->src.display, win->src.window,
-			_MOTIF_WM_HINTS, _MOTIF_WM_HINTS,
-			32, PropModeReplace, (u8*)&hints, 5
-		);
-	}
-	
-	void RGFW_releaseCursor(RGFW_window* win) {
-		XUngrabPointer(win->src.display, CurrentTime);
-
-		/* disable raw input */
-		unsigned char mask[] = { 0 };
-		XIEventMask em;
-		em.deviceid = XIAllMasterDevices;
-		em.mask_len = sizeof(mask);
-		em.mask = mask;
-
-		XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
-	}
-	
-	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
-		/* enable raw input */
-		unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
-		XISetMask(mask, XI_RawMotion);
-
-		XIEventMask em;
-		em.deviceid = XIAllMasterDevices;
-		em.mask_len = sizeof(mask);
-		em.mask = mask;
-		
-		XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
-
-		XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
-
-		RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2)));
-	}
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
-		if (X11Cursorhandle == NULL) {
-#if defined(__CYGWIN__)
-			X11Cursorhandle = dlopen("libXcursor-1.so", RTLD_LAZY | RTLD_LOCAL);
-#elif defined(__OpenBSD__) || defined(__NetBSD__)
-			X11Cursorhandle = dlopen("libXcursor.so", RTLD_LAZY | RTLD_LOCAL);
-#else
-			X11Cursorhandle = dlopen("libXcursor.so.1", RTLD_LAZY | RTLD_LOCAL);
-#endif
-
-			XcursorImageCreateSrc = (PFN_XcursorImageCreate) dlsym(X11Cursorhandle, "XcursorImageCreate");
-			XcursorImageDestroySrc = (PFN_XcursorImageDestroy) dlsym(X11Cursorhandle, "XcursorImageDestroy");
-			XcursorImageLoadCursorSrc = (PFN_XcursorImageLoadCursor) dlsym(X11Cursorhandle, "XcursorImageLoadCursor");
-		}
-#endif
-
-#if !defined(RGFW_NO_X11_XI_PRELOAD)
-		if (X11Xihandle == NULL) {
-#if defined(__CYGWIN__)
-			X11Xihandle = dlopen("libXi-6.so", RTLD_LAZY | RTLD_LOCAL);
-#elif defined(__OpenBSD__) || defined(__NetBSD__)
-			X11Xihandle = dlopen("libXi.so", RTLD_LAZY | RTLD_LOCAL);
-#else
-			X11Xihandle = dlopen("libXi.so.6", RTLD_LAZY | RTLD_LOCAL);
-#endif
-
-			XISelectEventsSrc = (PFN_XISelectEvents) dlsym(X11Xihandle, "XISelectEvents");
-		}
-#endif
-
-		XInitThreads(); /*!< init X11 threading*/
-
-		if (args & RGFW_OPENGL_SOFTWARE)
-			setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-
-		u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/
-
-#ifdef RGFW_OPENGL
-		u32* visual_attribs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
-		i32 fbcount;
-		GLXFBConfig* fbc = glXChooseFBConfig((Display*) win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount);
-
-		i32 best_fbc = -1;
-
-		if (fbcount == 0) {
-			printf("Failed to find any valid GLX visual configs\n");
-			return NULL;
-		}
-
-		u32 i;
-		for (i = 0; i < (u32)fbcount; i++) {
-			XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, fbc[i]);
-                        if (vi == NULL)
-				continue;
-                        
-			XFree(vi);
-
-			i32 samp_buf, samples;
-			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
-			glXGetFBConfigAttrib((Display*) win->src.display, fbc[i], GLX_SAMPLES, &samples);
-			
-			if ((best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) {
-				best_fbc = i;
-			}
-		}
-
-		if (best_fbc == -1) {
-			printf("Failed to get a valid GLX visual\n");
-			return NULL;
-		}
-
-		GLXFBConfig bestFbc = fbc[best_fbc];
-
-		/* Get a visual */
-		XVisualInfo* vi = glXGetVisualFromFBConfig((Display*) win->src.display, bestFbc);
-		
-		XFree(fbc);
-		
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-			XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/
-		}
-		
-#else
-		XVisualInfo viNorm;
-
-		viNorm.visual = DefaultVisual((Display*) win->src.display, DefaultScreen((Display*) win->src.display));
-		
-		viNorm.depth = 0;
-		XVisualInfo* vi = &viNorm;
-		
-		XMatchVisualInfo((Display*) win->src.display, DefaultScreen((Display*) win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/
-#endif
-		/* make X window attrubutes*/
-		XSetWindowAttributes swa;
-		Colormap cmap;
-
-		swa.colormap = cmap = XCreateColormap((Display*) win->src.display,
-			DefaultRootWindow(win->src.display),
-			vi->visual, AllocNone);
-
-		swa.background_pixmap = None;
-		swa.border_pixel = 0;
-		swa.event_mask = event_mask;
-		
-		swa.background_pixel = 0;
-
-		/* create the window*/
-		win->src.window = XCreateWindow((Display*) win->src.display, DefaultRootWindow((Display*) win->src.display), win->r.x, win->r.y, win->r.w, win->r.h,
-			0, vi->depth, InputOutput, vi->visual,
-			CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa);
-
-		XFreeColors((Display*) win->src.display, cmap, NULL, 0, 0);
-
-		#ifdef RGFW_OPENGL
-		XFree(vi);
-		#endif
-
-		// In your .desktop app, if you set the property
-		// StartupWMClass=RGFW that will assoicate the launcher icon
-		// with your application - robrohan 
-		
-		if (RGFW_className == NULL)
-			RGFW_className = (char*)name;
-
-		XClassHint *hint = XAllocClassHint();
-		assert(hint != NULL);
-		hint->res_class = (char*)RGFW_className;
-		hint->res_name = (char*)name; // just use the window name as the app name
-		XSetClassHint((Display*) win->src.display, win->src.window, hint);
-		XFree(hint);
-
-		if ((args & RGFW_NO_INIT_API) == 0) {
-#ifdef RGFW_OPENGL /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ 
-		i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
-		context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
-		if (RGFW_profile == RGFW_GL_CORE) 
-			context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
-		else 
-			context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
-		
-		if (RGFW_majorVersion || RGFW_minorVersion) {
-			context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
-			context_attribs[3] = RGFW_majorVersion;
-			context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
-			context_attribs[5] = RGFW_minorVersion;
-		}
-
-		glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
-		glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
-			glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
-
-		GLXContext ctx = NULL;
-
-		if (RGFW_root != NULL)
-			ctx = RGFW_root->src.ctx;
-
-		win->src.ctx = glXCreateContextAttribsARB((Display*) win->src.display, bestFbc, ctx, True, context_attribs);
-#endif
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-
-		RGFW_init_buffer(win, vi);
-		}
-		
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-
-		if (args & RGFW_CENTER) {
-			RGFW_area screenR = RGFW_getScreenSize();
-			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
-		}
-
-		if (args & RGFW_NO_RESIZE) { /* make it so the user can't resize the window*/
-			XSizeHints* sh = XAllocSizeHints();
-			sh->flags = (1L << 4) | (1L << 5);
-			sh->min_width = sh->max_width = win->r.w;
-			sh->min_height = sh->max_height = win->r.h;
-
-			XSetWMSizeHints((Display*) win->src.display, (Drawable) win->src.window, sh, XA_WM_NORMAL_HINTS);
-			XFree(sh);
-		}
-
-		if (args & RGFW_NO_BORDER) {
-			RGFW_window_setBorder(win, 0);
-		}
-
-		XSelectInput((Display*) win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/
-
-		/* make it so the user can't close the window until the program does*/
-		if (wm_delete_window == 0)
-			wm_delete_window = XInternAtom((Display*) win->src.display, "WM_DELETE_WINDOW", False);
-
-		XSetWMProtocols((Display*) win->src.display, (Drawable) win->src.window, &wm_delete_window, 1);
-
-		/* connect the context to the window*/
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
-#endif
-
-		/* set the background*/
-		XStoreName((Display*) win->src.display, (Drawable) win->src.window, name); /*!< set the name*/
-
-		XMapWindow((Display*) win->src.display, (Drawable) win->src.window);						  /* draw the window*/
-		XMoveWindow((Display*) win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/
-
-		if (args & RGFW_ALLOW_DND) { /* init drag and drop atoms and turn on drag and drop for this window */
-			win->_winArgs |= RGFW_ALLOW_DND;
-
-			XdndTypeList = XInternAtom((Display*) win->src.display, "XdndTypeList", False);
-			XdndSelection = XInternAtom((Display*) win->src.display, "XdndSelection", False);
-
-			/* client messages */
-			XdndEnter = XInternAtom((Display*) win->src.display, "XdndEnter", False);
-			XdndPosition = XInternAtom((Display*) win->src.display, "XdndPosition", False);
-			XdndStatus = XInternAtom((Display*) win->src.display, "XdndStatus", False);
-			XdndLeave = XInternAtom((Display*) win->src.display, "XdndLeave", False);
-			XdndDrop = XInternAtom((Display*) win->src.display, "XdndDrop", False);
-			XdndFinished = XInternAtom((Display*) win->src.display, "XdndFinished", False);
-
-			/* actions */
-			XdndActionCopy = XInternAtom((Display*) win->src.display, "XdndActionCopy", False);
-
-			XtextUriList = XInternAtom((Display*) win->src.display, "text/uri-list", False); 
-			XtextPlain = XInternAtom((Display*) win->src.display, "text/plain", False);
-
-			XdndAware = XInternAtom((Display*) win->src.display, "XdndAware", False);
-			const u8 version = 5;
-
-			XChangeProperty((Display*) win->src.display, (Window) win->src.window,
-				XdndAware, 4, 32,
-				PropModeReplace, &version, 1); /*!< turns on drag and drop */
-		}
-
-		#ifdef RGFW_EGL
-			if ((args & RGFW_NO_INIT_API) == 0)
-				RGFW_createOpenGLContext(win);
-		#endif
-
-		RGFW_window_setMouseDefault(win);
-
-		RGFW_windowsOpen++;
-
-		return win; /*return newly created window*/
-	}
-
-	RGFW_area RGFW_getScreenSize(void) {
-		assert(RGFW_root != NULL);
-
-		Screen* scrn = DefaultScreenOfDisplay((Display*) RGFW_root->src.display);
-		return RGFW_AREA(scrn->width, scrn->height);
-	}
-
-	RGFW_point RGFW_getGlobalMousePoint(void) {
-		assert(RGFW_root != NULL);
-
-		RGFW_point RGFWMouse;
-
-		i32 x, y;
-		u32 z;
-		Window window1, window2;
-		XQueryPointer((Display*) RGFW_root->src.display, XDefaultRootWindow((Display*) RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z);
- 
-		return RGFWMouse;
-	}
-
-	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
-		assert(win != NULL);
-
-		RGFW_point RGFWMouse;
-
-		i32 x, y;
-		u32 z;
-		Window window1, window2;
-		XQueryPointer((Display*) win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z);
-
-		return RGFWMouse;
-	}
-
-	int xAxis = 0, yAxis = 0;
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-
-		static struct {
-			long source, version;
-			i32 format;
-		} xdnd;
-
-		if (win->event.type == 0) 
-			RGFW_resetKey();
-
-		if (win->event.type == RGFW_quit) {
-			return NULL;
-		}
-
-		win->event.type = 0;
-
-#ifdef __linux__
-	RGFW_Event* event = RGFW_linux_updateJoystick(win);
-	if (event != NULL)
-		return event;
-#endif
-
-		XPending(win->src.display);
-
-		XEvent E; /*!< raw X11 event */
-
-		/* if there is no unread qued events, get a new one */
-		if ((QLength(win->src.display) || XEventsQueued((Display*) win->src.display, QueuedAlready) + XEventsQueued((Display*) win->src.display, QueuedAfterReading)) 
-			&& win->event.type != RGFW_quit
-		)
-			XNextEvent((Display*) win->src.display, &E);
-		else {
-			return NULL;
-		}
-
-		u32 i;
-		win->event.type = 0;
-
-
-		switch (E.type) {
-		case KeyPress:
-		case KeyRelease: {
-			win->event.repeat = RGFW_FALSE;
-			/* check if it's a real key release */
-			if (E.type == KeyRelease && XEventsQueued((Display*) win->src.display, QueuedAfterReading)) { /* get next event if there is one*/
-				XEvent NE;
-				XPeekEvent((Display*) win->src.display, &NE);
-
-				if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/
-					win->event.repeat = RGFW_TRUE;
-			}
-
-			/* set event key data */
-			KeySym sym = (KeySym)XkbKeycodeToKeysym((Display*) win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0);
-			win->event.keyCode = RGFW_apiKeyCodeToRGFW(E.xkey.keycode);
-			
-			char* str = (char*)XKeysymToString(sym);
-			if (str != NULL)
-				strncpy(win->event.keyName, str, 16);
-
-			win->event.keyName[15] = '\0';		
-
-			RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-			
-			/* get keystate data */
-			win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased;
-
-			XKeyboardState keystate;
-			XGetKeyboardControl((Display*) win->src.display, &keystate);
-
-			RGFW_updateLockState(win, (keystate.led_mask & 1), (keystate.led_mask & 2));
-			RGFW_keyboard[win->event.keyCode].current = (E.type == KeyPress);
-			RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, (E.type == KeyPress));
-			break;
-		}
-		case ButtonPress:
-		case ButtonRelease:
-			win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match 
-			
-			switch(win->event.button) {
-				case RGFW_mouseScrollUp:
-					win->event.scroll = 1;
-					break;
-				case RGFW_mouseScrollDown:
-					win->event.scroll = -1;
-					break;
-				default: break;
-			}
-
-			win->event.button = E.xbutton.button;
-			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-
-			if (win->event.repeat == RGFW_FALSE)
-				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
-
-			RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress);
-			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress));
-			break;
-
-		case MotionNotify:	
-			win->event.point.x = E.xmotion.x;
-			win->event.point.y = E.xmotion.y;
-			
-			if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
-				win->event.point.y = E.xmotion.y;
-
-				win->event.point.x = win->_lastMousePoint.x - abs(win->event.point.x);
-				win->event.point.y = win->_lastMousePoint.y - abs(win->event.point.y);
-			}
-
-			win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y);
-
-			win->event.type = RGFW_mousePosChanged;
-			RGFW_mousePosCallback(win, win->event.point);
-			break;
-
-		case GenericEvent: {
-			/* MotionNotify is used for mouse events if the mouse isn't held */                
-			if (!(win->_winArgs & RGFW_HOLD_MOUSE)) {
-            	XFreeEventData(win->src.display, &E.xcookie);
-				break;
-			}
-			
-            XGetEventData(win->src.display, &E.xcookie);
-            if (E.xcookie.evtype == XI_RawMotion) {
-				XIRawEvent *raw = (XIRawEvent *)E.xcookie.data;
-				if (raw->valuators.mask_len == 0) {
-					XFreeEventData(win->src.display, &E.xcookie);
-					break;
-				}
-
-                double deltaX = 0.0f; 
-				double deltaY = 0.0f;
-
-                /* check if relative motion data exists where we think it does */
-				if (XIMaskIsSet(raw->valuators.mask, 0) != 0)
-					deltaX += raw->raw_values[0];
-				if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
-					deltaY += raw->raw_values[1];
-
-				win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY);
-				
-				RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
-
-				win->event.type = RGFW_mousePosChanged;
-				RGFW_mousePosCallback(win, win->event.point);
-            }
-
-            XFreeEventData(win->src.display, &E.xcookie);
-			break;
-		}
-		
-		case Expose:
-			win->event.type = RGFW_windowRefresh;
-			RGFW_windowRefreshCallback(win);
-			break;
-
-		case ClientMessage:
-			/* if the client closed the window*/
-			if (E.xclient.data.l[0] == (i64) wm_delete_window) {
-				win->event.type = RGFW_quit;
-				RGFW_windowQuitCallback(win);
-				break;
-			}
-			
-			/* reset DND values */
-			if (win->event.droppedFilesCount) {
-				for (i = 0; i < win->event.droppedFilesCount; i++)
-					win->event.droppedFiles[i][0] = '\0';
-			}
-
-			win->event.droppedFilesCount = 0;
-
-			if ((win->_winArgs & RGFW_ALLOW_DND) == 0)
-				break;
-
-			XEvent reply = { ClientMessage };
-			reply.xclient.window = xdnd.source;
-			reply.xclient.format = 32;
-			reply.xclient.data.l[0] = (long) win->src.window;
-			reply.xclient.data.l[1] = 0;
-			reply.xclient.data.l[2] = None;
-
-			if (E.xclient.message_type == XdndEnter) {
-				unsigned long count;
-				Atom* formats;
-				Atom real_formats[6];
-
-				Bool list = E.xclient.data.l[1] & 1;
-
-				xdnd.source = E.xclient.data.l[0];
-				xdnd.version = E.xclient.data.l[1] >> 24;
-				xdnd.format = None;
-
-				if (xdnd.version > 5)
-					break;
-
-				if (list) {
-					Atom actualType;
-					i32 actualFormat;
-					unsigned long bytesAfter;
-
-					XGetWindowProperty((Display*) win->src.display,
-						xdnd.source,
-						XdndTypeList,
-						0,
-						LONG_MAX,
-						False,
-						4,
-						&actualType,
-						&actualFormat,
-						&count,
-						&bytesAfter,
-						(u8**) &formats);
-				} else {
-					count = 0;
-
-					if (E.xclient.data.l[2] != None)
-						real_formats[count++] = E.xclient.data.l[2];
-					if (E.xclient.data.l[3] != None)
-						real_formats[count++] = E.xclient.data.l[3];
-					if (E.xclient.data.l[4] != None)
-						real_formats[count++] = E.xclient.data.l[4];
-					
-					formats = real_formats;
-				}
-
-				unsigned long i;
-				for (i = 0; i < count; i++) {
-				    if (formats[i] == XtextUriList || formats[i] == XtextPlain) {
-						xdnd.format = formats[i];
-						break;
-					}
-				}
-
-				if (list) {
-					XFree(formats);
-				}
-
-				break;
-			}
-			if (E.xclient.message_type == XdndPosition) {
-				const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff;
-				const i32 yabs = (E.xclient.data.l[2]) & 0xffff;
-				Window dummy;
-				i32 xpos, ypos;
-
-				if (xdnd.version > 5)
-					break;
-
-				XTranslateCoordinates((Display*) win->src.display,
-					XDefaultRootWindow((Display*) win->src.display),
-					(Window) win->src.window,
-					xabs, yabs,
-					&xpos, &ypos,
-					&dummy);
-
-				win->event.point.x = xpos;
-				win->event.point.y = ypos;
-
-				reply.xclient.window = xdnd.source;
-				reply.xclient.message_type = XdndStatus;
-
-				if (xdnd.format) {
-					reply.xclient.data.l[1] = 1;
-					if (xdnd.version >= 2)
-						reply.xclient.data.l[4] = XdndActionCopy;
-				}
-
-				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-				break;
-			}
-
-			if (E.xclient.message_type != XdndDrop)
-				break;
-
-			if (xdnd.version > 5)
-				break;
-
-			win->event.type = RGFW_dnd_init;
-
-			if (xdnd.format) {
-				Time time = CurrentTime;
-
-				if (xdnd.version >= 1)
-					time = E.xclient.data.l[2];
-
-				XConvertSelection((Display*) win->src.display,
-					XdndSelection,
-					xdnd.format,
-					XdndSelection,
-					(Window) win->src.window,
-					time);
-			} else if (xdnd.version >= 2) {
-				XEvent reply = { ClientMessage };
-
-				XSendEvent((Display*) win->src.display, xdnd.source,
-					False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-			}
-
-			RGFW_dndInitCallback(win, win->event.point);
-			break;
-		case SelectionNotify: {
-			/* this is only for checking for xdnd drops */
-			if (E.xselection.property != XdndSelection || !(win->_winArgs | RGFW_ALLOW_DND))
-				break;
-
-			char* data;
-			unsigned long result;
-
-			Atom actualType;
-			i32 actualFormat;
-			unsigned long bytesAfter;
-
-			XGetWindowProperty((Display*) win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
-
-			if (result == 0)
-				break;
-
-			/*
-			SOURCED FROM GLFW _glfwParseUriList
-			Copyright (c) 2002-2006 Marcus Geelnard
-			Copyright (c) 2006-2019 Camilla Löwy
-			*/
-
-			const char* prefix = (const char*)"file://";
-
-			char* line;
-
-			win->event.droppedFilesCount = 0;
-
-			win->event.type = RGFW_dnd;
-
-			while ((line = strtok(data, "\r\n"))) {
-				char path[RGFW_MAX_PATH];
-
-				data = NULL;
-
-				if (line[0] == '#')
-					continue;
-
-				char* l;
-				for (l = line; 1; l++) {
-					if ((l - line) > 7)
-						break;
-					else if (*l != prefix[(l - line)])
-						break;
-					else if (*l == '\0' && prefix[(l - line)] == '\0') {
-						line += 7;
-						while (*line != '/')
-							line++;
-						break;
-					} else if (*l == '\0')
-						break;
-				}
-
-				win->event.droppedFilesCount++;
-
-				size_t index = 0;
-				while (*line) {
-					if (line[0] == '%' && line[1] && line[2]) {
-						const char digits[3] = { line[1], line[2], '\0' };
-						path[index] = (char) strtol(digits, NULL, 16);
-						line += 2;
-					} else
-						path[index] = *line;
-
-					index++;
-					line++;
-				}
-				path[index] = '\0';
-				strncpy(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1);
-			}
-
-			if (data)
-				XFree(data);
-
-			if (xdnd.version >= 2) {
-				reply.xclient.message_type = XdndFinished;
-				reply.xclient.data.l[1] = result;
-				reply.xclient.data.l[2] = XdndActionCopy;
-
-				XSendEvent((Display*) win->src.display, xdnd.source, False, NoEventMask, &reply);
-				XFlush((Display*) win->src.display);
-			}
-
-			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-			break;
-		}
-		case FocusIn:
-			win->event.inFocus = 1;
-			win->event.type = RGFW_focusIn;
-			RGFW_focusCallback(win, 1);
-			break;
-
-			break;
-		case FocusOut:
-			win->event.inFocus = 0;
-			win->event.type = RGFW_focusOut;
-			RGFW_focusCallback(win, 0);
-			break;
-		
-		case EnterNotify: {
-			win->event.type = RGFW_mouseEnter;
-			win->event.point.x = E.xcrossing.x;
-			win->event.point.y = E.xcrossing.y;
-			RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-			break;
-		}
-
-		case LeaveNotify: {
-			win->event.type = RGFW_mouseLeave;
-			RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-			break;
-		}
-
-		case ConfigureNotify: {
-				/* detect resize */
-      			if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) {
-					win->event.type = RGFW_windowResized;
-					win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height);
-					RGFW_windowResizeCallback(win, win->r);
-					break;
-      			}  
-      
-      			/* detect move */
-      			if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) {
-					win->event.type = RGFW_windowMoved;
-					win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h);
-					RGFW_windowMoveCallback(win, win->r);
-					break;
-				} 
-
-				break;
-		}
-		default: {
-			break;
-		}
-		}
-
-		XFlush((Display*) win->src.display);
-
-		if (win->event.type)
-			return &win->event;
-		else
-			return NULL;
-	}
-
-	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
-		assert(win != NULL);
-		win->r.x = v.x;
-		win->r.y = v.y;
-
-		XMoveWindow((Display*) win->src.display, (Window) win->src.window, v.x, v.y);
-	}
-
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->r.w = a.w;
-		win->r.h = a.h;
-
-		XResizeWindow((Display*) win->src.display, (Window) win->src.window, a.w, a.h);
-	}
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		XSizeHints hints;
-		long flags;
-
-		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
-
-		hints.flags |= PMinSize;
-		
-		hints.min_width = a.w;
-		hints.min_height = a.h;
-
-		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		XSizeHints hints;
-		long flags;
-
-		XGetWMNormalHints(win->src.display, (Window) win->src.window, &hints, &flags);
-
-		hints.flags |= PMaxSize;
-
-		hints.max_width = a.w;
-		hints.max_height = a.h;
-
-		XSetWMNormalHints(win->src.display, (Window) win->src.window, &hints);
-	}
-
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		XIconifyWindow(win->src.display, (Window) win->src.window, DefaultScreen(win->src.display));
-		XFlush(win->src.display);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		XMapWindow(win->src.display, (Window) win->src.window);
-		XFlush(win->src.display);
-	}	
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		XStoreName((Display*) win->src.display, (Window) win->src.window, name);
-	}
-	
-	void* RGFW_libxshape = NULL;
-
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		assert(win != NULL);
-		
-		#if defined(__CYGWIN__)
-			RGFW_libxshape = dlopen("libXext-6.so", RTLD_LAZY | RTLD_LOCAL);
-		#elif defined(__OpenBSD__) || defined(__NetBSD__)
-			RGFW_libxshape = dlopen("libXext.so", RTLD_LAZY | RTLD_LOCAL);
-		#else
-    		RGFW_libxshape = dlopen("libXext.so.6", RTLD_LAZY | RTLD_LOCAL);
-		#endif
-		
-		typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
-		static PFN_XShapeCombineMask XShapeCombineMask;
-		
-		typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
-		static PFN_XShapeCombineRegion XShapeCombineRegion;
-		
-		if (XShapeCombineMask != NULL)
-			XShapeCombineMask = (PFN_XShapeCombineMask) dlsym(RGFW_libxshape, "XShapeCombineMask");
-
-		if (XShapeCombineRegion != NULL)
-			XShapeCombineRegion = (PFN_XShapeCombineRegion) dlsym(RGFW_libxshape, "XShapeCombineMask");
-
-		if (passthrough) {
-			Region region = XCreateRegion();
-			XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
-			XDestroyRegion(region);
-
-			return;
-		}
-
-		XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
-	}
-	#endif
-
-	/*
-		the majority function is sourced from GLFW
-	*/
-
-	void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-		i32 longCount = 2 + a.w * a.h;
-
-		u64* X11Icon = (u64*) RGFW_MALLOC(longCount * sizeof(u64));
-		u64* target = X11Icon;
-
-		*target++ = a.w;
-		*target++ = a.h;
-
-		u32 i;
-
-		for (i = 0; i < a.w * a.h; i++) {
-			if (channels == 3)
-				*target++ = ((icon[i * 3 + 0]) << 16) |
-				((icon[i * 3 + 1]) << 8) |
-				((icon[i * 3 + 2]) << 0) |
-				(0xFF << 24);
-
-			else if (channels == 4)
-				*target++ = ((icon[i * 4 + 0]) << 16) |
-				((icon[i * 4 + 1]) << 8) |
-				((icon[i * 4 + 2]) << 0) |
-				((icon[i * 4 + 3]) << 24);
-		}
-
-		static Atom NET_WM_ICON = 0;
-		if (NET_WM_ICON == 0)
-			NET_WM_ICON = XInternAtom((Display*) win->src.display, "_NET_WM_ICON", False);
-
-		XChangeProperty((Display*) win->src.display, (Window) win->src.window,
-			NET_WM_ICON,
-			6, 32,
-			PropModeReplace,
-			(u8*) X11Icon,
-			longCount);
-
-		RGFW_FREE(X11Icon);
-
-		XFlush((Display*) win->src.display);
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-#ifndef RGFW_NO_X11_CURSOR
-		XcursorImage* native = XcursorImageCreate(a.w, a.h);
-		native->xhot = 0;
-		native->yhot = 0;
-
-		u8* source = (u8*) image;
-		XcursorPixel* target = native->pixels;
-
-		u32 i;
-		for (i = 0; i < a.w * a.h; i++, target++, source += 4) {
-			u8 alpha = 0xFF;
-			if (channels == 4)
-				alpha = source[3];
-
-			*target = (alpha << 24) | (((source[0] * alpha) / 255) << 16) | (((source[1] * alpha) / 255) << 8) | (((source[2] * alpha) / 255) << 0);
-		}
-
-		Cursor cursor = XcursorImageLoadCursor((Display*) win->src.display, native);
-		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
-
-		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
-		XcursorImageDestroy(native);
-#else
-	RGFW_UNUSED(image) RGFW_UNUSED(a.w) RGFW_UNUSED(channels)
-#endif
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
-		assert(win != NULL);
-
-		XEvent event;
-		XQueryPointer(win->src.display, DefaultRootWindow(win->src.display),
-			&event.xbutton.root, &event.xbutton.window,
-			&event.xbutton.x_root, &event.xbutton.y_root,
-			&event.xbutton.x, &event.xbutton.y,
-			&event.xbutton.state);
-
-		if (event.xbutton.x == v.x && event.xbutton.y == v.y)
-			return;
-
-		XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) v.x - win->r.x, (int) v.y - win->r.y);
-	}
-
-	RGFWDEF void RGFW_window_disableMouse(RGFW_window* win) {
-		RGFW_UNUSED(win);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-		assert(win != NULL);
-		 
-		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8)))
-			return;
-		
-		mouse = RGFW_mouseIconSrc[mouse];
-
-		Cursor cursor = XCreateFontCursor((Display*) win->src.display, mouse);
-		XDefineCursor((Display*) win->src.display, (Window) win->src.window, (Cursor) cursor);
-
-		XFreeCursor((Display*) win->src.display, (Cursor) cursor);
-	}
-
-	void RGFW_window_hide(RGFW_window* win) {
-		XMapWindow(win->src.display, win->src.window);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		XUnmapWindow(win->src.display, win->src.window);
-	}
-
-	/*
-		the majority function is sourced from GLFW
-	*/
-	char* RGFW_readClipboard(size_t* size) {
-		static Atom UTF8 = 0;
-		if (UTF8 == 0)
-			UTF8 = XInternAtom(RGFW_root->src.display, "UTF8_STRING", True);
-
-		XEvent event;
-		int format;
-		unsigned long N, sizeN;
-		char* data, * s = NULL;
-		Atom target;
-		Atom CLIPBOARD = 0, XSEL_DATA = 0;
-
-		if (CLIPBOARD == 0) {
-			CLIPBOARD = XInternAtom(RGFW_root->src.display, "CLIPBOARD", 0);
-			XSEL_DATA = XInternAtom(RGFW_root->src.display, "XSEL_DATA", 0);
-		}
-
-		XConvertSelection(RGFW_root->src.display, CLIPBOARD, UTF8, XSEL_DATA, RGFW_root->src.window, CurrentTime);
-		XSync(RGFW_root->src.display, 0);
-		XNextEvent(RGFW_root->src.display, &event);
-
-		if (event.type != SelectionNotify || event.xselection.selection != CLIPBOARD || event.xselection.property == 0)
-			return NULL;
-
-		XGetWindowProperty(event.xselection.display, event.xselection.requestor,
-			event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
-			&format, &sizeN, &N, (unsigned char**) &data);
-
-		if (target == UTF8 || target == XA_STRING) {
-			s = (char*)RGFW_MALLOC(sizeof(char) * sizeN);
-			strncpy(s, data, sizeN);
-			s[sizeN] = '\0';
-			XFree(data);
-		}
-
-		XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
-
-		if (s != NULL && size != NULL)
-			*size = sizeN;
-
-		return s;
-	}
-
-	/*
-		almost all of this function is sourced from GLFW
-	*/
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		static Atom CLIPBOARD = 0,
-			UTF8_STRING = 0,
-			SAVE_TARGETS = 0,
-			TARGETS = 0,
-			MULTIPLE = 0,
-			ATOM_PAIR = 0,
-			CLIPBOARD_MANAGER = 0;
-
-		if (CLIPBOARD == 0) {
-			CLIPBOARD = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD", False);
-			UTF8_STRING = XInternAtom((Display*) RGFW_root->src.display, "UTF8_STRING", False);
-			SAVE_TARGETS = XInternAtom((Display*) RGFW_root->src.display, "SAVE_TARGETS", False);
-			TARGETS = XInternAtom((Display*) RGFW_root->src.display, "TARGETS", False);
-			MULTIPLE = XInternAtom((Display*) RGFW_root->src.display, "MULTIPLE", False);
-			ATOM_PAIR = XInternAtom((Display*) RGFW_root->src.display, "ATOM_PAIR", False);
-			CLIPBOARD_MANAGER = XInternAtom((Display*) RGFW_root->src.display, "CLIPBOARD_MANAGER", False);
-		}
-		
-		XSetSelectionOwner((Display*) RGFW_root->src.display, CLIPBOARD, (Window) RGFW_root->src.window, CurrentTime);
-
-		XConvertSelection((Display*) RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, (Window) RGFW_root->src.window, CurrentTime);
-		for (;;) {
-			XEvent event;
-
-			XNextEvent((Display*) RGFW_root->src.display, &event);
-			if (event.type != SelectionRequest) {
-				break;
-			}
-
-			const XSelectionRequestEvent* request = &event.xselectionrequest;
-
-			XEvent reply = { SelectionNotify };
-			reply.xselection.property = 0;
-
-			if (request->target == TARGETS) {
-				const Atom targets[] = { TARGETS,
-										MULTIPLE,
-										UTF8_STRING,
-										XA_STRING };
-
-				XChangeProperty((Display*) RGFW_root->src.display,
-					request->requestor,
-					request->property,
-					4,
-					32,
-					PropModeReplace,
-					(u8*) targets,
-					sizeof(targets) / sizeof(targets[0]));
-
-				reply.xselection.property = request->property;
-			}
-
-			if (request->target == MULTIPLE) {
-				Atom* targets = NULL;
-
-				Atom actualType = 0;
-				int actualFormat = 0;
-				unsigned long count = 0, bytesAfter = 0;
-
-				XGetWindowProperty((Display*) RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
-
-				unsigned long i;
-				for (i = 0; i < (u32)count; i += 2) {
-					if (targets[i] == UTF8_STRING || targets[i] == XA_STRING) {
-						XChangeProperty((Display*) RGFW_root->src.display,
-							request->requestor,
-							targets[i + 1],
-							targets[i],
-							8,
-							PropModeReplace,
-							(u8*) text,
-							textLen);
-						XFlush(RGFW_root->src.display);
-					} else {
-						targets[i + 1] = None;
-					}
-				}
-
-				XChangeProperty((Display*) RGFW_root->src.display,
-					request->requestor,
-					request->property,
-					ATOM_PAIR,
-					32,
-					PropModeReplace,
-					(u8*) targets,
-					count);
-
-				XFlush(RGFW_root->src.display);
-				XFree(targets);
-
-				reply.xselection.property = request->property;
-			}
-
-			reply.xselection.display = request->display;
-			reply.xselection.requestor = request->requestor;
-			reply.xselection.selection = request->selection;
-			reply.xselection.target = request->target;
-			reply.xselection.time = request->time;
-
-			XSendEvent((Display*) RGFW_root->src.display, request->requestor, False, 0, &reply);
-			XFlush(RGFW_root->src.display);
-		}
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		XWindowAttributes windowAttributes;
-		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
-
-		/* check if the window is visable */
-		if (windowAttributes.map_state != IsViewable)
-			return 0;
-
-		/* check if the window covers the full screen */
-		return (windowAttributes.x == 0 && windowAttributes.y == 0 &&
-			windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) &&
-			windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display)));
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		XWindowAttributes windowAttributes;
-		XGetWindowAttributes(win->src.display, (Window) win->src.window, &windowAttributes);
-
-		return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win));
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		static Atom prop = 0;
-		if (prop == 0)
-			prop = XInternAtom(win->src.display, "WM_STATE", False);
-
-		Atom actual_type;
-		i32 actual_format;
-		unsigned long nitems, bytes_after;
-		unsigned char* prop_data;
-
-		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, prop, 0, 2, False,
-			AnyPropertyType, &actual_type, &actual_format,
-			&nitems, &bytes_after, &prop_data);
-
-		if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) {
-			XFree(prop_data);
-			return 1;
-		}
-
-		if (prop_data != NULL)
-			XFree(prop_data);
-
-		return 0;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		static Atom net_wm_state = 0;
-		static Atom net_wm_state_maximized_horz = 0;
-		static Atom net_wm_state_maximized_vert = 0;
-
-		if (net_wm_state == 0) {
-			net_wm_state = XInternAtom(win->src.display, "_NET_WM_STATE", False);
-			net_wm_state_maximized_vert = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
-			net_wm_state_maximized_horz = XInternAtom(win->src.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
-		}
-
-		Atom actual_type;
-		i32 actual_format;
-		unsigned long nitems, bytes_after;
-		unsigned char* prop_data;
-
-		i16 status = XGetWindowProperty(win->src.display, (Window) win->src.window, net_wm_state, 0, 1024, False,
-			XA_ATOM, &actual_type, &actual_format,
-			&nitems, &bytes_after, &prop_data);
-
-		if (status != Success) {
-			if (prop_data != NULL)
-				XFree(prop_data);
-
-			return 0;
-		}
-
-		Atom* atoms = (Atom*) prop_data;
-		u64 i;
-		for (i = 0; i < nitems; ++i) {
-			if (atoms[i] == net_wm_state_maximized_horz ||
-				atoms[i] == net_wm_state_maximized_vert) {
-				XFree(prop_data);
-				return 1;
-			}
-		}
-
-		return 0;
-	}
-
-	static void XGetSystemContentScale(Display* display, float* xscale, float* yscale) {
-		float xdpi = 96.f, ydpi = 96.f;
-
-#ifndef RGFW_NO_DPI
-		char* rms = XResourceManagerString(display);
-		XrmDatabase db = NULL;
-
-		if (rms && db)
-			db = XrmGetStringDatabase(rms);
-
-		if (db == 0) {
-			*xscale = xdpi / 96.f;
-			*yscale = ydpi / 96.f;
-			return;
-		}
-
-		XrmValue value;
-		char* type = NULL;
-
-		if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && strncmp(type, "String", 7) == 0)
-			xdpi = ydpi = atof(value.addr);
-		XrmDestroyDatabase(db);
-#endif
-
-		* xscale = xdpi / 96.f;
-		*yscale = ydpi / 96.f;
-	}
-
-	RGFW_monitor RGFW_XCreateMonitor(i32 screen) {
-		RGFW_monitor monitor;
-
-		Display* display = XOpenDisplay(NULL);
-		
-		RGFW_area size = RGFW_getScreenSize();
-
-		monitor.rect = RGFW_RECT(0, 0, size.w, size.h);
-		monitor.physW = DisplayWidthMM(display, screen);
-		monitor.physH = DisplayHeightMM(display, screen);
-		
-		XGetSystemContentScale(display, &monitor.scaleX, &monitor.scaleY);
-		XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen));
-
-		XRRCrtcInfo* ci = NULL;
-		int crtc = screen;
-
-		if (sr->ncrtc > crtc) {
-			ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]);
-		}
-		
-		if (ci == NULL) {
-			float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4));
-			float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4));
-		
-			monitor.scaleX = (float) (dpi_width) / (float) 96;
-			monitor.scaleY = (float) (dpi_height) / (float) 96;
-			XRRFreeScreenResources(sr);
-			XCloseDisplay(display);
-			return monitor;
-		}
-	    
-		XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]);
-		monitor.physW = info->mm_width;
-		monitor.physH = info->mm_height;
-
-		monitor.rect.x = ci->x;
-		monitor.rect.y = ci->y;
-		monitor.rect.w = ci->width;
-		monitor.rect.h = ci->height;
-		
-		float dpi_width = round((double)monitor.rect.w/(((double)monitor.physW)/25.4));
-		float dpi_height = round((double)monitor.rect.h/(((double)monitor.physH)/25.4));
-
-		monitor.scaleX = (float) (dpi_width) / (float) 96;
-		monitor.scaleY = (float) (dpi_height) / (float) 96;		
-
-		if (monitor.scaleX > 1 && monitor.scaleX < 1.1)
-			monitor.scaleX = 1;
-
-		if (monitor.scaleY > 1 && monitor.scaleY < 1.1)
-			monitor.scaleY = 1;
-
-		XRRFreeCrtcInfo(ci);
-		XRRFreeScreenResources(sr);
-
-		XCloseDisplay(display);
-
-		return monitor;
-	}
-
-	RGFW_monitor RGFW_monitors[6];
-	RGFW_monitor* RGFW_getMonitors(void) {
-		size_t i;
-		for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++)
-			RGFW_monitors[i] = RGFW_XCreateMonitor(i);
-
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		assert(RGFW_root != NULL);
-
-		i32 primary = -1;
-		Window root = DefaultRootWindow(RGFW_root->src.display);
-		XRRScreenResources* res = XRRGetScreenResources(RGFW_root->src.display, root);
-
-		for (int i = 0; i < res->noutput; i++) {
-			XRROutputInfo* output_info = XRRGetOutputInfo(RGFW_root->src.display, res, res->outputs[i]);
-			if (output_info->connection == RR_Connected && output_info->crtc) {
-				XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(RGFW_root->src.display, res, output_info->crtc);
-				if (crtc_info->mode != None && crtc_info->x == 0 && crtc_info->y == 0) {
-					primary = i;
-					XRRFreeCrtcInfo(crtc_info);
-					XRRFreeOutputInfo(output_info);
-					break;
-				}
-				XRRFreeCrtcInfo(crtc_info);
-			}
-			XRRFreeOutputInfo(output_info);
-		}
-
-		XRRFreeScreenResources(res);
-
-		return RGFW_XCreateMonitor(primary);
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		return RGFW_XCreateMonitor(DefaultScreen(win->src.display));
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		if (win == NULL)
-			glXMakeCurrent((Display*) NULL, (Drawable)NULL, (GLXContext) NULL);
-		else
-			glXMakeCurrent((Display*) win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
-	}
-	#endif
-
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-
-		/* clear the window*/
-		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-			RGFW_area area = RGFW_bufferSize;
-
-#ifndef RGFW_X11_DONT_CONVERT_BGR
-			win->src.bitmap->data = (char*) win->buffer;
-			u32 x, y;
-			for (y = 0; y < (u32)win->r.h; y++) {
-				for (x = 0; x < (u32)win->r.w; x++) {
-					u32 index = (y * 4 * area.w) + x * 4;
-
-					u8 red = win->src.bitmap->data[index];
-					win->src.bitmap->data[index] = win->buffer[index + 2];
-					win->src.bitmap->data[index + 2] = red;
-
-				}
-			}
-#endif	
-			XPutImage(win->src.display, (Window) win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h);
-#endif
-		}
-
-		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					glXSwapBuffers((Display*) win->src.display, (Window) win->src.window);
-			#endif
-		}
-	}
-
-	#if !defined(RGFW_EGL)	
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-
-		#if defined(RGFW_OPENGL)	
-		((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))((Display*) win->src.display, (Window) win->src.window, swapInterval);
-		#else
-		RGFW_UNUSED(swapInterval);
-		#endif
-	}
-	#endif
-
-
-	void RGFW_window_close(RGFW_window* win) {
-		/* ungrab pointer if it was grabbed */
-		if (win->_winArgs & RGFW_HOLD_MOUSE) 
-			XUngrabPointer(win->src.display, CurrentTime);
-			
-		assert(win != NULL);
-#ifdef RGFW_EGL
-		RGFW_closeEGL(win);
-#endif
-
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-		if (win->buffer != NULL) {
-			XDestroyImage((XImage*) win->src.bitmap);
-			XFreeGC(win->src.display, win->src.gc);
-		}
-#endif
-
-		if ((Display*) win->src.display) {
-#ifdef RGFW_OPENGL
-			glXDestroyContext((Display*) win->src.display, win->src.ctx);
-#endif
-
-			if (win == RGFW_root)
-				RGFW_root = NULL;
-
-			if ((Drawable) win->src.window)
-				XDestroyWindow((Display*) win->src.display, (Drawable) win->src.window); /*!< close the window*/
-			
-			XCloseDisplay((Display*) win->src.display); /*!< kill the display*/
-		}
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-
-		RGFW_windowsOpen--;
-#if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
-		if (X11Cursorhandle != NULL && RGFW_windowsOpen <= 0) {
-			dlclose(X11Cursorhandle);
-
-			X11Cursorhandle = NULL;
-		}
-#endif
-#if !defined(RGFW_NO_X11_XI_PRELOAD)
-		if (X11Xihandle != NULL && RGFW_windowsOpen <= 0) {
-			dlclose(X11Xihandle);
-
-			X11Xihandle = NULL;
-		}
-#endif
-
-		if (RGFW_libxshape != NULL && RGFW_windowsOpen <= 0) {
-			dlclose(RGFW_libxshape);
-			RGFW_libxshape = NULL;
-		}
-
-		if (RGFW_windowsOpen <= 0) {
-			if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){
-				close(RGFW_eventWait_forceStop[0]);
-				close(RGFW_eventWait_forceStop[1]);
-			}
-
-			u8 i;
-			for (i = 0; i < RGFW_joystickCount; i++)
-				close(RGFW_joysticks[i]);
-		}
-
-		/* set cleared display / window to NULL for error checking */
-		win->src.display = (Display*) 0;
-		win->src.window = (Window) 0;
-
-		RGFW_FREE(win); /*!< free collected window data */
-	}
-	
-
-/* 
-	End of X11 linux / unix defines
-*/
-
-#endif /* RGFW_X11 */
-
-
-/* wayland or X11 defines*/
-#if defined(RGFW_WAYLAND) || defined(RGFW_X11)
-#include <fcntl.h>
-#include <poll.h>
-#include <unistd.h>
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		assert(win != NULL);
-
-#ifdef __linux__
-
-		i32 js = open(file, O_RDONLY);
-
-		if (js && RGFW_joystickCount < 4) {
-			RGFW_joystickCount++;
-
-			RGFW_joysticks[RGFW_joystickCount - 1] = open(file, O_RDONLY);
-
-			u8 i;
-			for (i = 0; i < 16; i++)
-				RGFW_jsPressed[RGFW_joystickCount - 1][i] = 0;
-
-		}
-
-		else {
-#ifdef RGFW_PRINT_ERRORS
-			RGFW_error = 1;
-			fprintf(stderr, "Error RGFW_registerJoystickF : Cannot open file %s\n", file);
-#endif
-		}
-
-		return RGFW_joystickCount - 1;
-#endif
-	}
-	
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		assert(win != NULL);
-
-#ifdef __linux__
-		char file[15];
-		sprintf(file, "/dev/input/js%i", jsNumber);
-
-		return RGFW_registerJoystickF(win, file);
-#endif
-	}
-	
-	void RGFW_stopCheckEvents(void) { 
-		RGFW_eventWait_forceStop[2] = 1;
-		while (1) {
-			const char byte = 0;
-			const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1);
-			if (result == 1 || result == -1)
-				break;
-		}
-	}
-
-	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
-		if (waitMS == 0)
-			return;
-		
-		u8 i;
-
-		if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) {
-			if (pipe(RGFW_eventWait_forceStop) != -1) {
-				fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0);
-				fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0);
-				fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0);
-				fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0);
-			}
-		}
-
-		struct pollfd fds[] = {
-			#ifdef RGFW_WAYLAND
-			{ wl_display_get_fd(win->src.display), POLLIN, 0 },
-			#else
-			{ ConnectionNumber(win->src.display), POLLIN, 0 },
-			#endif
-			{ RGFW_eventWait_forceStop[0], POLLIN, 0 },
-			#ifdef __linux__ /* blank space for 4 joystick files*/
-			{ -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 },  {-1, POLLIN, 0} 
-			#endif
-		};
-
-		u8 index = 2;
-		
-		#if defined(__linux__)
-			for (i = 0; i < RGFW_joystickCount; i++) {
-				if (RGFW_joysticks[i] == 0)
-					continue;
-
-				fds[index].fd = RGFW_joysticks[i];
-				index++;
-			}
-		#endif
-
-
-		u64 start = RGFW_getTimeNS();
-
-		#ifdef RGFW_WAYLAND
-		while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) {
-		#else
-		while (XPending(win->src.display) == 0 && waitMS >= -1) {
-		#endif
-			if (poll(fds, index, waitMS) <= 0)
-				break;
-
-			if (waitMS > 0) {
-				waitMS -= (RGFW_getTimeNS() - start) / 1e+6;
-			}
-		}
-
-		/* drain any data in the stop request */
-		if (RGFW_eventWait_forceStop[2]) {	
-			char data[64];
-			(void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data));
-			
-			RGFW_eventWait_forceStop[2] = 0;
-		}
-	}
-
-	u64 RGFW_getTimeNS(void) { 
-		struct timespec ts = { 0 };
-		clock_gettime(1, &ts);
-		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
-		return nanoSeconds;
-	}
-
-	u64 RGFW_getTime(void) {
-		struct timespec ts = { 0 };
-		clock_gettime(1, &ts);
-		unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
-		return (double)(nanoSeconds) * 1e-9;
-	}
-#endif /* end of wayland or X11 time defines*/
-
-
-/*
-
-	Start of Wayland defines
-
-
-*/
-
-#ifdef RGFW_WAYLAND
-/*
-Wayland TODO:
-- fix RGFW_keyPressed lock state
-
-	RGFW_windowMoved, 		the window was moved (by the user)
-	RGFW_windowResized  	the window was resized (by the user), [on webASM this means the browser was resized]
-	RGFW_windowRefresh	 	The window content needs to be refreshed
-
-	RGFW_dnd 				a file has been dropped into the window
-	RGFW_dnd_init
-
-- window args:
-	#define RGFW_NO_RESIZE	 			the window cannot be resized  by the user
-	#define RGFW_ALLOW_DND     			the window supports drag and drop
-	#define RGFW_SCALE_TO_MONITOR 			scale the window to the screen 
-
-- other missing functions functions ("TODO wayland") (~30 functions)
-- fix buffer rendering weird behavior
-*/
-	#include <errno.h>
-	#include <unistd.h>
-	#include <sys/mman.h>
-	#include <xkbcommon/xkbcommon.h>
-	#include <xkbcommon/xkbcommon-keysyms.h>
-	#include <dirent.h>
-	#include <linux/kd.h> 
-	#include <wayland-cursor.h>
-
-RGFW_window* RGFW_key_win = NULL;
-
-void RGFW_eventPipe_push(RGFW_window* win, RGFW_Event event) {
-	if (win == NULL) {
-		win = RGFW_key_win;
-
-		if (win == NULL) return;
-	}
-	
-	if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0])))
-		return;
-
-	win->src.events[win->src.eventLen] = event;
-	win->src.eventLen += 1;
-}
-
-RGFW_Event RGFW_eventPipe_pop(RGFW_window* win) {
-	RGFW_Event ev;
-	ev.type = 0;
-	
-	if (win->src.eventLen > -1)
-		win->src.eventLen -= 1;
-	
-	if (win->src.eventLen >= 0)  
-		ev = win->src.events[win->src.eventLen];
-
-	return ev;	
-}
-
-/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
-#include "xdg-shell.h"
-#include "xdg-decoration-unstable-v1.h"
-
-struct xdg_wm_base *xdg_wm_base;
-struct wl_compositor* RGFW_compositor = NULL;
-struct wl_shm* shm = NULL;
-struct wl_shell* RGFW_shell = NULL;
-static struct wl_seat *seat = NULL;
-static struct xkb_context *xkb_context;
-static struct xkb_keymap *keymap = NULL;
-static struct xkb_state *xkb_state = NULL;
-enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode;
-static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
-
-struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL;
-struct wl_surface* RGFW_cursor_surface = NULL;
-struct wl_cursor_image* RGFW_cursor_image = NULL;
-
-static void xdg_wm_base_ping_handler(void *data,
-        struct xdg_wm_base *wm_base, uint32_t serial)
-{
-	RGFW_UNUSED(data);
-    xdg_wm_base_pong(wm_base, serial);
-}
-
-static const struct xdg_wm_base_listener xdg_wm_base_listener = {
-    .ping = xdg_wm_base_ping_handler,
-};
-
-b8 RGFW_wl_configured = 0;
-
-static void xdg_surface_configure_handler(void *data,
-        struct xdg_surface *xdg_surface, uint32_t serial)
-{	
-	RGFW_UNUSED(data);
-    xdg_surface_ack_configure(xdg_surface, serial);
-	#ifdef RGFW_DEBUG
-	printf("Surface configured\n");
-	#endif
-	RGFW_wl_configured = 1;
-}
-
-static const struct xdg_surface_listener xdg_surface_listener = {
-    .configure = xdg_surface_configure_handler,
-};
-
-static void xdg_toplevel_configure_handler(void *data,
-        struct xdg_toplevel *toplevel, int32_t width, int32_t height,
-        struct wl_array *states)
-{
-	RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states)
-    fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height);
-}
-
-static void xdg_toplevel_close_handler(void *data,
-        struct xdg_toplevel *toplevel)
-{
-	RGFW_UNUSED(data);
-	RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel);
-	if (win == NULL)
-		win = RGFW_key_win;
-	
-	RGFW_Event ev;
-	ev.type = RGFW_quit;
-
-	RGFW_eventPipe_push(win, ev); 	
-
-	RGFW_windowQuitCallback(win);
-}
-
-static void shm_format_handler(void *data,
-        struct wl_shm *shm, uint32_t format)
-{
-	RGFW_UNUSED(data); RGFW_UNUSED(shm);
-    fprintf(stderr, "Format %d\n", format);
-}
-
-static const struct wl_shm_listener shm_listener = {
-    .format = shm_format_handler,
-};
-
-static const struct xdg_toplevel_listener xdg_toplevel_listener = {
-    .configure = xdg_toplevel_configure_handler,
-    .close = xdg_toplevel_close_handler,
-};
-
-RGFW_window* RGFW_mouse_win = NULL;
-
-static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
-	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y);
-	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
-	RGFW_mouse_win = win;
-
-	RGFW_Event ev;
-	ev.type = RGFW_mouseEnter;
-	ev.point = win->event.point;
-
-	RGFW_eventPipe_push(win, ev); 	
-
-	RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE);
-}
-static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {
-	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface);
-	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
-	if (RGFW_mouse_win == win)
-		RGFW_mouse_win = NULL;
-	
-	RGFW_Event ev;
-	ev.type = RGFW_mouseLeave;
-	ev.point = win->event.point;
-	RGFW_eventPipe_push(win, ev);
-
-	RGFW_mouseNotifyCallBack(win,  win->event.point, RGFW_FALSE);
-}
-static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
-	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y);
-
-	assert(RGFW_mouse_win != NULL);
-	
-	RGFW_Event ev;
-	ev.type = RGFW_mousePosChanged;
-	ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y));
-	RGFW_eventPipe_push(RGFW_mouse_win, ev);
-	
-	RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)));
-}
-static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
-	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
-	assert(RGFW_mouse_win != NULL);
-
-	u32 b = (button - 0x110) + 1;
-
-	/* flip right and middle button codes */
-	if (b == 2) b = 3;
-	else if (b == 3) b = 2;
-	
-	RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current;
-	RGFW_mouseButtons[b].current = state;
-
-	RGFW_Event ev;
-	ev.type = RGFW_mouseButtonPressed + state;
-	ev.button = b;
-	RGFW_eventPipe_push(RGFW_mouse_win, ev);
-
-	RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state);
-}
-static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
-	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time);  RGFW_UNUSED(axis);
-	assert(RGFW_mouse_win != NULL); 
-
-	double scroll = wl_fixed_to_double(value);
-
-	RGFW_Event ev;
-	ev.type = RGFW_mouseButtonPressed;
-	ev.button = RGFW_mouseScrollUp + (scroll < 0);
-	RGFW_eventPipe_push(RGFW_mouse_win, ev);
-
-	RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1);
-}
-
-void RGFW_doNothing(void) { }
-static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing};
-
-static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
-	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
-
-	char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
-	xkb_keymap_unref (keymap);
-	keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
-	
-	munmap (keymap_string, size);
-	close (fd);
-	xkb_state_unref (xkb_state);
-	xkb_state = xkb_state_new (keymap);
-}
-static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { 
-	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys);
-
-	RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface);
-
-	RGFW_Event ev;
-	ev.type = RGFW_focusIn;
-	ev.inFocus = RGFW_TRUE;
-	RGFW_key_win->event.inFocus = RGFW_TRUE;
-
-	RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev);
-
-	RGFW_focusCallback(RGFW_key_win, RGFW_TRUE);
-}
-static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { 
-	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
-
-	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
-	if (RGFW_key_win == win)
-		RGFW_key_win = NULL;	
-
-	RGFW_Event ev;
-	ev.type = RGFW_focusOut;
-	ev.inFocus = RGFW_FALSE;
-	win->event.inFocus = RGFW_FALSE;
-	RGFW_eventPipe_push(win, ev);
-
-	RGFW_focusCallback(win, RGFW_FALSE);
-}
-static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
-	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); 
-
-	assert(RGFW_key_win != NULL);
-
-	xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8);
-	char name[16];
-	xkb_keysym_get_name(keysym, name, 16);
-
-	u32 RGFW_key = RGFW_apiKeyCodeToRGFW(key);
-	RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current;
-	RGFW_keyboard[RGFW_key].current = state;
-	RGFW_Event ev;
-	ev.type = RGFW_keyPressed + state;
-	ev.keyCode = RGFW_key;
-	strcpy(ev.keyName, name);
-	ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key);
-	RGFW_eventPipe_push(RGFW_key_win, ev);
-	
-	RGFW_updateLockState(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2"));
-
-	RGFW_keyCallback(RGFW_key_win, RGFW_key, name, RGFW_key_win->event.lockState, state);
-}
-static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
-	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); 
-	xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
-}
-static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing};
-
-static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) {
-	RGFW_UNUSED(data);
-
-	if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
-		struct wl_pointer *pointer = wl_seat_get_pointer (seat);
-		wl_pointer_add_listener (pointer, &pointer_listener, NULL);
-	}
-	if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
-		struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat);
-		wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL);
-	}
-}
-static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing};
-
-static void wl_global_registry_handler(void *data,
-		struct wl_registry *registry, uint32_t id, const char *interface,
-		uint32_t version)
-{
-	RGFW_UNUSED(data); RGFW_UNUSED(version);
-
-    if (strcmp(interface, "wl_compositor") == 0) {
-		RGFW_compositor = wl_registry_bind(registry,
-			id, &wl_compositor_interface, 4);
-	} else if (strcmp(interface, "xdg_wm_base") == 0) {
-	xdg_wm_base = wl_registry_bind(registry,
-		id, &xdg_wm_base_interface, 1);
-	} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
-		decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
-    } else if (strcmp(interface, "wl_shm") == 0) {
-        shm = wl_registry_bind(registry,
-            id, &wl_shm_interface, 1);
-        wl_shm_add_listener(shm, &shm_listener, NULL);
-	} else if (strcmp(interface,"wl_seat") == 0) {
-		seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
-		wl_seat_add_listener(seat, &seat_listener, NULL);
-	}
-
-	else {
-		#ifdef RGFW_DEBUG
-		printf("did not register %s\n", interface);
-		return;
-		#endif
-	}
-
-		#ifdef RGFW_DEBUG
-		printf("registered %s\n", interface);
-		#endif
-}
-
-static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); }
-static const struct wl_registry_listener registry_listener = {
-	.global = wl_global_registry_handler,
-	.global_remove = wl_global_registry_remove,
-};
-
-static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) {
-	switch (mode) {
-	case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE:
-		return "client-side decorations";
-	case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE:
-		return "server-side decorations";
-	}
-	abort();
-}
-
-
-static void decoration_handle_configure(void *data,
-		struct zxdg_toplevel_decoration_v1 *decoration,
-		enum zxdg_toplevel_decoration_v1_mode mode) {
-	RGFW_UNUSED(data); RGFW_UNUSED(decoration);
-	printf("Using %s\n", get_mode_name(mode));
-	RGFW_current_mode = mode;
-}
-
-static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
-	.configure = decoration_handle_configure,
-};
-
-static void randname(char *buf) {
-	struct timespec ts;
-	clock_gettime(CLOCK_REALTIME, &ts);
-	long r = ts.tv_nsec;
-	for (int i = 0; i < 6; ++i) {
-		buf[i] = 'A'+(r&15)+(r&16)*2;
-		r >>= 5;
-	}
-}
-
-static int anonymous_shm_open(void) {
-	char name[] = "/RGFW-wayland-XXXXXX";
-	int retries = 100;
-
-	do {
-		randname(name + strlen(name) - 6);
-
-		--retries;
-		// shm_open guarantees that O_CLOEXEC is set
-		int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
-		if (fd >= 0) {
-			shm_unlink(name);
-			return fd;
-		}
-	} while (retries > 0 && errno == EEXIST);
-
-	return -1;
-}
-
-int create_shm_file(off_t size) {
-	int fd = anonymous_shm_open();
-	if (fd < 0) {
-		return fd;
-	}
-
-	if (ftruncate(fd, size) < 0) {
-		close(fd);
-		return -1;
-	}
-
-	return fd;
-}
-
-static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
-	#ifdef RGFW_BUFFER
-		RGFW_window* win = (RGFW_window*)data;
-		if ((win->_winArgs & RGFW_NO_CPU_RENDER))
-			return;	
-		
-		#ifndef RGFW_X11_DONT_CONVERT_BGR
-			u32 x, y;
-			for (y = 0; y < (u32)win->r.h; y++) {
-				for (x = 0; x < (u32)win->r.w; x++) {
-					u32 index = (y * 4 * win->r.w) + x * 4;
-
-					u8 red = win->buffer[index];
-					win->buffer[index] = win->buffer[index + 2];
-					win->buffer[index + 2] = red;
-
-				}
-			}
-		#endif	
-	
-		wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
-		wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h);
-		wl_surface_commit(win->src.surface);
-	#endif
-}
-
-static const struct wl_callback_listener wl_surface_frame_listener = {
-	.done = wl_surface_frame_done,
-};
-
-
-	/* normal wayland RGFW stuff */
-	
-	RGFW_area RGFW_getScreenSize(void) {
-		RGFW_area area = {};
-
-		if (RGFW_root != NULL)
-			/* this isn't right but it's here for buffers */
-			area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h);
-		
-		/* TODO wayland */
-		return area;
-	}
-	
-	void RGFW_releaseCursor(RGFW_window* win) {
-		RGFW_UNUSED(win);
-	}
-
-	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
-		RGFW_UNUSED(win); RGFW_UNUSED(r);
-
-		/* TODO wayland */
-	}
-
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-	void RGFW_init_buffer(RGFW_window* win) {
-		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)	
-			size_t size = win->r.w * win->r.h * 4;
-			int fd = create_shm_file(size);
-			if (fd < 0) {
-				fprintf(stderr, "Failed to create a buffer. size: %ld\n", size);
-				exit(1);
-			}
-
-			win->buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-			if (win->buffer == MAP_FAILED) {
-				fprintf(stderr, "mmap failed!\n");
-				close(fd);
-				exit(1);
-			}
-
-			struct wl_shm_pool* pool = wl_shm_create_pool(shm, fd, size);
-			win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4,
-				WL_SHM_FORMAT_ARGB8888);
-			wl_shm_pool_destroy(pool);
-
-			close(fd);
-			
-			wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
-			wl_surface_commit(win->src.surface);
-
-			u8 color[] = {0x00, 0x00, 0x00, 0xFF};
-
-			size_t i;
-			for (i = 0; i < size; i += 4) {
-				memcpy(&win->buffer[i], color, 4);
-			}
-	
-			#if defined(RGFW_OSMESA)
-					win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
-					OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-			#endif
-		#else
-		RGFW_UNUSED(win);
-		#endif
-	}
-   
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-		
-		fprintf(stderr, "Warning: RGFW Wayland support is experimental\n");
-		
-		win->src.display = wl_display_connect(NULL);
-		if (win->src.display == NULL) {
-			#ifdef RGFW_DEBUG
-				fprintf(stderr, "Failed to load Wayland display\n");
-			#endif
-			return NULL;
-		}
-		
-		struct wl_registry *registry = wl_display_get_registry(win->src.display);
-		wl_registry_add_listener(registry, &registry_listener, NULL);
-			
-		wl_display_dispatch(win->src.display);
-		wl_display_roundtrip(win->src.display);
-
-		if (RGFW_compositor == NULL) {
-			#ifdef RGFW_DEBUG
-				fprintf(stderr, "Can't find compositor.\n");
-			#endif
-			
-			return NULL;
-		}
-		
-		if (RGFW_wl_cursor_theme == NULL) {
-			RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, shm);
-			RGFW_cursor_surface = wl_compositor_create_surface(RGFW_compositor); 
-			
-			struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr");
-			RGFW_cursor_image = cursor->images[0];
-			struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
-
-			wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
-			wl_surface_commit(RGFW_cursor_surface); 
-		}
-
-		if (RGFW_root == NULL)
-			xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
-		
-		xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
-
-		win->src.surface = wl_compositor_create_surface(RGFW_compositor);
-		wl_surface_set_user_data(win->src.surface, win);
-
-		win->src.xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, win->src.surface);
-		xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL);
-	
-		xdg_wm_base_set_user_data(xdg_wm_base, win);
-
-		win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
-		xdg_toplevel_set_user_data(win->src.xdg_toplevel, win);
-		xdg_toplevel_set_title(win->src.xdg_toplevel, name);
-		xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL);
-
-		xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
-		
-		if (!(args & RGFW_NO_BORDER)) {
-			win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
-						decoration_manager, win->src.xdg_toplevel);
-		}
-
-		if (args & RGFW_CENTER) {
-			RGFW_area screenR = RGFW_getScreenSize();
-			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
-		}
-
-		if (args & RGFW_OPENGL_SOFTWARE)
-			setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
-
-		wl_display_roundtrip(win->src.display);
-
-		wl_surface_commit(win->src.surface);
-		
-		/* wait for the surface to be configured */
-		while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { }
-		
-		
-		#ifdef RGFW_OPENGL
-			if ((args & RGFW_NO_INIT_API) == 0) {
-				win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h);
-				RGFW_createOpenGLContext(win);
-			}
-		#endif	
-
-		RGFW_init_buffer(win);
-
-		struct wl_callback* callback = wl_surface_frame(win->src.surface);
-   		wl_callback_add_listener(callback, &wl_surface_frame_listener, win);	
-		wl_surface_commit(win->src.surface);
-
-		if (args & RGFW_HIDE_MOUSE) {
-			RGFW_window_showMouse(win, 0);
-		}
-		
-		if (RGFW_root == NULL) {
-			RGFW_root = win;
-		}
-		
-		win->src.eventIndex = 0;
-		win->src.eventLen = 0;
-		
-		return win;
-	}
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		if (win->_winArgs & RGFW_WINDOW_HIDE)
-			return NULL;
-
-		if (win->src.eventIndex == 0) {
-			if (wl_display_roundtrip(win->src.display) == -1) {
-				return NULL;
-			}
-			RGFW_resetKey();
-		}
-
-		#ifdef __linux__
-			RGFW_Event* event = RGFW_linux_updateJoystick(win);
-			if (event != NULL)
-				return event;
-		#endif
-		
-		if (win->src.eventLen == 0) {
-				return NULL;
-		}
-
-		RGFW_Event ev = RGFW_eventPipe_pop(win);
-		
-		if (ev.type ==  0 || win->event.type == RGFW_quit) {
-			return NULL;
-		}
-        
-		ev.frameTime = win->event.frameTime;
-        ev.frameTime2 = win->event.frameTime2;
-        ev.inFocus = win->event.inFocus;
-        win->event = ev;
-		
-		return &win->event;
-	}
-
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		RGFW_UNUSED(win); RGFW_UNUSED(a);
-
-		/* TODO wayland */
-	}
-
-	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
-		RGFW_UNUSED(win); RGFW_UNUSED(v);
-
-		/* TODO wayland */
-	}
-
-	void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) {
-		RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels)
-		/* TODO wayland */
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
-		RGFW_UNUSED(win); RGFW_UNUSED(v);
-
-		/* TODO wayland */
-	}
-
-	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-		RGFW_UNUSED(win);
-
-		if (show) {
-
-		}
-		else {
-			
-		}
-
-		/* TODO wayland */
-	}
-
-	b8 RGFW_window_isMaximized(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		/* TODO wayland */
-		return 0;
-	}
-
-	b8 RGFW_window_isMinimized(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		/* TODO wayland */
-		return 0;
-	}
-
-	b8 RGFW_window_isHidden(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		/* TODO wayland */
-		return 0;
-	}
-
-	b8 RGFW_window_isFullscreen(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		/* TODO wayland */
-		return 0;
-	}
-
-	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		/* TODO wayland */
-		return RGFW_POINT(0, 0);
-	}
-			
-	RGFW_point RGFW_getGlobalMousePoint(void) {
-		/* TODO wayland */
-		return RGFW_POINT(0, 0);
-	}
-			
-	void RGFW_window_show(RGFW_window* win) {
-		//wl_surface_attach(win->src.surface, win->rc., 0, 0);
-        wl_surface_commit(win->src.surface);
-		
-		if (win->_winArgs & RGFW_WINDOW_HIDE)
-			win->_winArgs ^= RGFW_WINDOW_HIDE;
-	}
-		
-	void RGFW_window_hide(RGFW_window* win) {
-		wl_surface_attach(win->src.surface, NULL, 0, 0);
-        wl_surface_commit(win->src.surface);
-		win->_winArgs |= RGFW_WINDOW_HIDE;
-	}
-		
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_UNUSED(win);
-	
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL);
-	}
-		
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-		RGFW_UNUSED(win);
-		
-		static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" };
-
-		struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
-		RGFW_cursor_image = cursor->images[0];
-		struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
-
-		wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
-		wl_surface_commit(RGFW_cursor_surface); 
-	}
-		
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		RGFW_UNUSED(win); RGFW_UNUSED(image); RGFW_UNUSED(a); RGFW_UNUSED(channels)
-		//struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
-		//RGFW_cursor_image = image;
-		struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
-
-		wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
-		wl_surface_commit(RGFW_cursor_surface); 
-	}
-		
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		xdg_toplevel_set_title(win->src.xdg_toplevel, name);
-	}
-		
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		RGFW_UNUSED(win); RGFW_UNUSED(passthrough);
-
-	/* TODO wayland */
-	}
-		
-	void RGFW_window_setBorder(RGFW_window* win, b8 border) {
-		RGFW_UNUSED(win); RGFW_UNUSED(border);
-
-	/* TODO wayland */
-	}
-		
-	void RGFW_window_restore(RGFW_window* win) {
-		RGFW_UNUSED(win);
-
-	/* TODO wayland */
-	}
-		
-	void RGFW_window_minimize(RGFW_window* win) {
-		RGFW_UNUSED(win);
-
-	/* TODO wayland */
-	}
-		
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		RGFW_UNUSED(win); RGFW_UNUSED(a);
-
-	/* TODO wayland */
-	}
-		
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		RGFW_UNUSED(win); RGFW_UNUSED(a);
-
-	/* TODO wayland */
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		RGFW_monitor m = {};
-		RGFW_UNUSED(win);
-		RGFW_UNUSED(m);
-		/* TODO wayland */
-
-		return m;
-	}
-
-
-	#ifndef RGFW_EGL
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
-	#endif
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-
-		/* clear the window*/
-		#ifdef RGFW_BUFFER	
-			wl_surface_frame_done(win, NULL, 0);
-			if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) 
-		#endif
-		{
-		#ifdef RGFW_OPENGL
-			eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-		#endif
-		}
-		
-		wl_display_flush(win->src.display);
-	}
-
-	void RGFW_window_close(RGFW_window* win) {
-		#ifdef RGFW_EGL
-			RGFW_closeEGL(win);
-		#endif
-
-		if (RGFW_root == win) {
-			RGFW_root = NULL;
-		}
-
-		xdg_toplevel_destroy(win->src.xdg_toplevel);
-		xdg_surface_destroy(win->src.xdg_surface);
-		wl_surface_destroy(win->src.surface);
-
-		#ifdef RGFW_BUFFER
-		wl_buffer_destroy(win->src.wl_buffer);
-		#endif
-		
-		wl_display_disconnect(win->src.display);
-		RGFW_FREE(win);
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		/* TODO wayland */
-
-		return (RGFW_monitor){};
-	}
-						
-	RGFW_monitor* RGFW_getMonitors(void) {
-		/* TODO wayland */
-
-		return NULL;
-	}
-
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		RGFW_UNUSED(text); RGFW_UNUSED(textLen);
-
-		/* TODO wayland */
-	}
-
-	char* RGFW_readClipboard(size_t* size) {
-		RGFW_UNUSED(size);
-
-		/* TODO wayland */
-
-		return NULL;
-	}
-#endif /* RGFW_WAYLAND */
-
-/* 
-	End of Wayland defines
-*/
-
-
-/*
-
-	Start of Windows defines
-
-
-*/
-
-#ifdef RGFW_WINDOWS
-	#define WIN32_LEAN_AND_MEAN
-	#define OEMRESOURCE
-	#include <windows.h>
-	
-	#include <processthreadsapi.h>
-	#include <wchar.h>
-	#include <locale.h>
-	#include <windowsx.h>
-	#include <shellapi.h>
-	#include <shellscalingapi.h>
-
-	#include <winuser.h>
-
-	__declspec(dllimport) int __stdcall WideCharToMultiByte( UINT CodePage, DWORD dwFlags, const WCHAR* lpWideCharStr, int cchWideChar,  LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
-	
-	#ifndef RGFW_NO_XINPUT
-	typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
-	PFN_XInputGetState XInputGetStateSRC = NULL;
-	#define XInputGetState XInputGetStateSRC
-
-	typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
-	PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL;
-	#define XInputGetKeystroke XInputGetKeystrokeSRC
-
-	static HMODULE RGFW_XInput_dll = NULL;
-	#endif
-
-	u32 RGFW_mouseIconSrc[] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO};
-
-	char* createUTF8FromWideStringWin32(const WCHAR* source);
-
-#define GL_FRONT				0x0404
-#define GL_BACK					0x0405
-#define GL_LEFT					0x0406
-#define GL_RIGHT				0x0407
-
-#if defined(RGFW_OSMESA) && defined(RGFW_LINK_OSMESA)
-
-	typedef void (GLAPIENTRY* PFN_OSMesaDestroyContext)(OSMesaContext);
-	typedef i32(GLAPIENTRY* PFN_OSMesaMakeCurrent)(OSMesaContext, void*, int, int, int);
-	typedef OSMesaContext(GLAPIENTRY* PFN_OSMesaCreateContext)(GLenum, OSMesaContext);
-
-	PFN_OSMesaMakeCurrent OSMesaMakeCurrentSource;
-	PFN_OSMesaCreateContext OSMesaCreateContextSource;
-	PFN_OSMesaDestroyContext OSMesaDestroyContextSource;
-
-#define OSMesaCreateContext OSMesaCreateContextSource
-#define OSMesaMakeCurrent OSMesaMakeCurrentSource
-#define OSMesaDestroyContext OSMesaDestroyContextSource
-#endif
-
-	typedef int (*PFN_wglGetSwapIntervalEXT)(void);
-	PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
-#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
-
-
-	void* RGFWjoystickApi = NULL;
-
-	/* these two wgl functions need to be preloaded */
-	typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
-	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
-
-	/* defines for creating ARB attributes */
-#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
-#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
-#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
-#define WGL_DRAW_TO_WINDOW_ARB                    0x2001
-#define WGL_ACCELERATION_ARB                      0x2003
-#define WGL_NO_ACCELERATION_ARB 0x2025
-#define WGL_DOUBLE_BUFFER_ARB                     0x2011
-#define WGL_COLOR_BITS_ARB                        0x2014
-#define WGL_RED_BITS_ARB 0x2015
-#define WGL_RED_SHIFT_ARB 0x2016
-#define WGL_GREEN_BITS_ARB 0x2017
-#define WGL_GREEN_SHIFT_ARB 0x2018
-#define WGL_BLUE_BITS_ARB 0x2019
-#define WGL_BLUE_SHIFT_ARB 0x201a
-#define WGL_ALPHA_BITS_ARB 0x201b
-#define WGL_ALPHA_SHIFT_ARB 0x201c
-#define WGL_ACCUM_BITS_ARB 0x201d
-#define WGL_ACCUM_RED_BITS_ARB 0x201e
-#define WGL_ACCUM_GREEN_BITS_ARB 0x201f
-#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
-#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
-#define WGL_DEPTH_BITS_ARB 0x2022
-#define WGL_AUX_BUFFERS_ARB 0x2024
-#define WGL_STEREO_ARB 0x2012
-#define WGL_DEPTH_BITS_ARB                        0x2022
-#define WGL_STENCIL_BITS_ARB 					  0x2023
-#define WGL_FULL_ACCELERATION_ARB                 0x2027
-#define WGL_CONTEXT_FLAGS_ARB                     0x2094
-#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
-#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
-#define WGL_SAMPLE_BUFFERS_ARB               0x2041
-#define WGL_SAMPLES_ARB 0x2042
-#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
-
-#ifndef RGFW_EGL
-static HMODULE wglinstance = NULL;
-#endif
-
-#ifdef RGFW_WGL_LOAD
-	typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
-	typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
-	typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
-	typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
-	typedef HDC(WINAPI* PFN_wglGetCurrentDC)();
-	typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)();
-
-	PFN_wglCreateContext wglCreateContextSRC;
-	PFN_wglDeleteContext wglDeleteContextSRC;
-	PFN_wglGetProcAddress wglGetProcAddressSRC;
-	PFN_wglMakeCurrent wglMakeCurrentSRC;
-	PFN_wglGetCurrentDC wglGetCurrentDCSRC;
-	PFN_wglGetCurrentContext wglGetCurrentContextSRC;
-
-	#define wglCreateContext wglCreateContextSRC
-	#define wglDeleteContext wglDeleteContextSRC
-	#define wglGetProcAddress wglGetProcAddressSRC
-	#define wglMakeCurrent wglMakeCurrentSRC
-
-	#define wglGetCurrentDC wglGetCurrentDCSRC
-	#define wglGetCurrentContext wglGetCurrentContextSRC
-#endif
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) { 
-		void* proc = (void*) wglGetProcAddress(procname);
-		if (proc)
-			return proc;
-
-		return (void*) GetProcAddress(wglinstance, procname); 
-	}
-
-	typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
-	static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
-#endif
-
-	RGFW_window RGFW_eventWindow;
-
-	LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
-		switch (message) {
-		case WM_MOVE:
-			RGFW_eventWindow.r.x = LOWORD(lParam);
-			RGFW_eventWindow.r.y = HIWORD(lParam);
-			RGFW_eventWindow.src.window = hWnd;
-			return DefWindowProcA(hWnd, message, wParam, lParam);
-		case WM_SIZE:
-			RGFW_eventWindow.r.w = LOWORD(lParam);
-			RGFW_eventWindow.r.h = HIWORD(lParam);
-			RGFW_eventWindow.src.window = hWnd;
-			return DefWindowProcA(hWnd, message, wParam, lParam); // Call DefWindowProc after handling
-		default:
-			return DefWindowProcA(hWnd, message, wParam, lParam);
-		}
-	}
-	
-	#ifndef RGFW_NO_DPI
-	static HMODULE RGFW_Shcore_dll = NULL;
-	typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
-	PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
-	#define GetDpiForMonitor GetDpiForMonitorSRC
-	#endif
-
-	__declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
-	
-	#ifndef RGFW_NO_XINPUT
-	void RGFW_loadXInput(void) {
-		u32 i;
-		static const char* names[] = { 
-			"xinput1_4.dll",
-			"xinput1_3.dll",
-			"xinput9_1_0.dll",
-			"xinput1_2.dll",
-			"xinput1_1.dll"
-		};
-
-		for (i = 0; i < sizeof(names) / sizeof(const char*);  i++) {
-			RGFW_XInput_dll = LoadLibraryA(names[i]);
-
-			if (RGFW_XInput_dll) {
-				XInputGetStateSRC = (PFN_XInputGetState)(void*)GetProcAddress(RGFW_XInput_dll, "XInputGetState");
-			
-				if (XInputGetStateSRC == NULL)
-					printf("Failed to load XInputGetState");
-			}
-		}
-	}
-	#endif
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-	void RGFW_init_buffer(RGFW_window* win) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-	if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-		RGFW_bufferSize = RGFW_getScreenSize();
-	
-	BITMAPV5HEADER bi = { 0 };
-	ZeroMemory(&bi, sizeof(bi));
-	bi.bV5Size = sizeof(bi);
-	bi.bV5Width = RGFW_bufferSize.w;
-	bi.bV5Height = -((LONG) RGFW_bufferSize.h);
-	bi.bV5Planes = 1;
-	bi.bV5BitCount = 32;
-	bi.bV5Compression = BI_BITFIELDS;
-	bi.bV5BlueMask = 0x00ff0000;
-	bi.bV5GreenMask = 0x0000ff00;
-	bi.bV5RedMask = 0x000000ff;
-	bi.bV5AlphaMask = 0xff000000;
-
-	win->src.bitmap = CreateDIBSection(win->src.hdc,
-		(BITMAPINFO*) &bi,
-		DIB_RGB_COLORS,
-		(void**) &win->buffer,
-		NULL,
-		(DWORD) 0);
-	
-	win->src.hdcMem = CreateCompatibleDC(win->src.hdc);
-
-	#if defined(RGFW_OSMESA)
-	win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
-	OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-	#endif
-#else
-RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
-#endif
-	}
-
-	void RGFW_window_setDND(RGFW_window* win, b8 allow) {
-		DragAcceptFiles(win->src.window, allow);
-	}
-
-	void RGFW_releaseCursor(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		ClipCursor(NULL);
-    	const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL };
-    	RegisterRawInputDevices(&id, 1, sizeof(id));	
-	}
-
-	void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) {
-		RGFW_UNUSED(win); RGFW_UNUSED(rect);
-
-		RECT clipRect;
-		GetClientRect(win->src.window, &clipRect);
-		ClientToScreen(win->src.window, (POINT*) &clipRect.left);
-		ClientToScreen(win->src.window, (POINT*) &clipRect.right);
-		ClipCursor(&clipRect);
-
-	    const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
-		RegisterRawInputDevices(&id, 1, sizeof(id));
-	}
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-		#ifndef RGFW_NO_XINPUT
-		if (RGFW_XInput_dll == NULL)
-			RGFW_loadXInput();
-		#endif
-
-		#ifndef RGFW_NO_DPI
-		if (RGFW_Shcore_dll == NULL) {
-			RGFW_Shcore_dll = LoadLibraryA("shcore.dll");
-			GetDpiForMonitorSRC = (PFN_GetDpiForMonitor)(void*)GetProcAddress(RGFW_Shcore_dll, "GetDpiForMonitor");
-			SetProcessDPIAware();
-		}
-		#endif
-
-		if (wglinstance == NULL) {
-			wglinstance = LoadLibraryA("opengl32.dll");
-#ifdef RGFW_WGL_LOAD
-			wglCreateContextSRC = (PFN_wglCreateContext) GetProcAddress(wglinstance, "wglCreateContext");
-			wglDeleteContextSRC = (PFN_wglDeleteContext) GetProcAddress(wglinstance, "wglDeleteContext");
-			wglGetProcAddressSRC = (PFN_wglGetProcAddress) GetProcAddress(wglinstance, "wglGetProcAddress");
-			wglMakeCurrentSRC = (PFN_wglMakeCurrent) GetProcAddress(wglinstance, "wglMakeCurrent");
-			wglGetCurrentDCSRC = (PFN_wglGetCurrentDC) GetProcAddress(wglinstance, "wglGetCurrentDC");
-			wglGetCurrentContextSRC = (PFN_wglGetCurrentContext) GetProcAddress(wglinstance, "wglGetCurrentContext");
-#endif
-		}
-	
-		if (name[0] == 0) name = (char*) " ";
-
-		RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
-		RGFW_eventWindow.src.window = NULL;
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-
-		win->src.maxSize = RGFW_AREA(0, 0);
-		win->src.minSize = RGFW_AREA(0, 0);
-
-
-		HINSTANCE inh = GetModuleHandleA(NULL);
-
-		#ifndef __cplusplus
-		WNDCLASSA Class = { 0 }; /*!< Setup the Window class. */
-		#else
-		WNDCLASSA Class = { };
-		#endif
-
-		if (RGFW_className == NULL)
-			RGFW_className = (char*)name;
-
-		Class.lpszClassName = RGFW_className;
-		Class.hInstance = inh;
-		Class.hCursor = LoadCursor(NULL, IDC_ARROW);
-		Class.lpfnWndProc = WndProc;
-
-		RegisterClassA(&Class);
-
-		DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
-
-		RECT windowRect, clientRect;
-
-		if (!(args & RGFW_NO_BORDER)) {
-			window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX;
-
-			if (!(args & RGFW_NO_RESIZE))
-				window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME;
-		} else
-			window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX;
-
-		HWND dummyWin = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0);
-
-		GetWindowRect(dummyWin, &windowRect);
-		GetClientRect(dummyWin, &clientRect);
-
-		win->src.hOffset = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
-		win->src.window = CreateWindowA(Class.lpszClassName, name, window_style, win->r.x, win->r.y, win->r.w, win->r.h + win->src.hOffset, 0, 0, inh, 0);
-
-		if (args & RGFW_ALLOW_DND) {
-			win->_winArgs |= RGFW_ALLOW_DND;
-			RGFW_window_setDND(win, 1);
-		}
-		win->src.hdc = GetDC(win->src.window);
-
-		if ((args & RGFW_NO_INIT_API) == 0) {
-#ifdef RGFW_DIRECTX
-		assert(FAILED(CreateDXGIFactory(&__uuidof(IDXGIFactory), (void**) &RGFW_dxInfo.pFactory)) == 0);
-
-		if (FAILED(RGFW_dxInfo.pFactory->lpVtbl->EnumAdapters(RGFW_dxInfo.pFactory, 0, &RGFW_dxInfo.pAdapter))) {
-			fprintf(stderr, "Failed to enumerate DXGI adapters\n");
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-			return NULL;
-		}
-
-		D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 };
-
-		if (FAILED(D3D11CreateDevice(RGFW_dxInfo.pAdapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, featureLevels, 1, D3D11_SDK_VERSION, &RGFW_dxInfo.pDevice, NULL, &RGFW_dxInfo.pDeviceContext))) {
-			fprintf(stderr, "Failed to create Direct3D device\n");
-			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-			return NULL;
-		}
-
-		DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
-		swapChainDesc.BufferCount = 1;
-		swapChainDesc.BufferDesc.Width = win->r.w;
-		swapChainDesc.BufferDesc.Height = win->r.h;
-		swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
-		swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
-		swapChainDesc.OutputWindow = win->src.window;
-		swapChainDesc.SampleDesc.Count = 1;
-		swapChainDesc.SampleDesc.Quality = 0;
-		swapChainDesc.Windowed = TRUE;
-		RGFW_dxInfo.pFactory->lpVtbl->CreateSwapChain(RGFW_dxInfo.pFactory, (IUnknown*) RGFW_dxInfo.pDevice, &swapChainDesc, &win->src.swapchain);
-
-		ID3D11Texture2D* pBackBuffer;
-		win->src.swapchain->lpVtbl->GetBuffer(win->src.swapchain, 0, &__uuidof(ID3D11Texture2D), (LPVOID*) &pBackBuffer);
-		RGFW_dxInfo.pDevice->lpVtbl->CreateRenderTargetView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pBackBuffer, NULL, &win->src.renderTargetView);
-		pBackBuffer->lpVtbl->Release(pBackBuffer);
-
-		D3D11_TEXTURE2D_DESC depthStencilDesc = { 0 };
-		depthStencilDesc.Width = win->r.w;
-		depthStencilDesc.Height = win->r.h;
-		depthStencilDesc.MipLevels = 1;
-		depthStencilDesc.ArraySize = 1;
-		depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
-		depthStencilDesc.SampleDesc.Count = 1;
-		depthStencilDesc.SampleDesc.Quality = 0;
-		depthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
-		depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
-
-		ID3D11Texture2D* pDepthStencilTexture = NULL;
-		RGFW_dxInfo.pDevice->lpVtbl->CreateTexture2D(RGFW_dxInfo.pDevice, &depthStencilDesc, NULL, &pDepthStencilTexture);
-
-		D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = { 0 };
-		depthStencilViewDesc.Format = depthStencilDesc.Format;
-		depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
-		depthStencilViewDesc.Texture2D.MipSlice = 0;
-
-		RGFW_dxInfo.pDevice->lpVtbl->CreateDepthStencilView(RGFW_dxInfo.pDevice, (ID3D11Resource*) pDepthStencilTexture, &depthStencilViewDesc, &win->src.pDepthStencilView);
-
-		pDepthStencilTexture->lpVtbl->Release(pDepthStencilTexture);
-
-		RGFW_dxInfo.pDeviceContext->lpVtbl->OMSetRenderTargets(RGFW_dxInfo.pDeviceContext, 1, &win->src.renderTargetView, win->src.pDepthStencilView);
-#endif
-
-#ifdef RGFW_OPENGL 
-		HDC dummy_dc = GetDC(dummyWin);
-        
-        u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL; 
-        
-        //if (RGFW_DOUBLE_BUFFER)    
-             pfd_flags |= PFD_DOUBLEBUFFER;
-		
-        PIXELFORMATDESCRIPTOR pfd = {
-			sizeof(pfd),
-			1, /* version */
-			pfd_flags,
-		    PFD_TYPE_RGBA, /* ipixel type */
-			24, /* color bits */
-			0, 0, 0, 0, 0, 0,
-			8, /* alpha bits */
-			0, 0, 0, 0, 0, 0,
-			32, /* depth bits */
-			8, /* stencil bits */ 
-			0,
-			PFD_MAIN_PLANE, /* Layer type */
-			0, 0, 0, 0
-		};
-
-		int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
-		SetPixelFormat(dummy_dc, pixel_format, &pfd);
-
-		HGLRC dummy_context = wglCreateContext(dummy_dc);
-		wglMakeCurrent(dummy_dc, dummy_context);
-
-		if (wglChoosePixelFormatARB == NULL) {
-			wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) (void*) wglGetProcAddress("wglCreateContextAttribsARB");
-			wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) (void*)wglGetProcAddress("wglChoosePixelFormatARB");
-		}
-
-		wglMakeCurrent(dummy_dc, 0);
-		wglDeleteContext(dummy_context);
-		ReleaseDC(dummyWin, dummy_dc);
-		
-		/* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */ 
-		if (wglCreateContextAttribsARB != NULL) {
-			PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 24, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-			if (args & RGFW_OPENGL_SOFTWARE)
-				pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
-
-			if (wglChoosePixelFormatARB != NULL) {
-				i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
-
-				int pixel_format;
-				UINT num_formats;
-				wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
-				if (!num_formats) {
-					printf("Failed to create a pixel format for WGL.\n");
-				}
-
-				DescribePixelFormat(win->src.hdc, pixel_format, sizeof(pfd), &pfd);
-				if (!SetPixelFormat(win->src.hdc, pixel_format, &pfd)) {
-					printf("Failed to set the WGL pixel format.\n");
-				}
-			}
-			
-			/* create opengl/WGL context for the specified version */ 
-			u32 index = 0;
-			i32 attribs[40];
-
-			if (RGFW_profile == RGFW_GL_CORE) {
-				SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
-			}
-			else {
-				SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
-			}
-			
-			if (RGFW_majorVersion || RGFW_minorVersion) {
-				SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_majorVersion);
-				SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_minorVersion);
-			}
-
-			SET_ATTRIB(0, 0);
-
-			win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
-		} else { /* fall back to a default context (probably opengl 2 or something) */
-			fprintf(stderr, "Failed to create an accelerated OpenGL Context\n");
-
-			int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
-			SetPixelFormat(win->src.hdc, pixel_format, &pfd);
-
-			win->src.ctx = wglCreateContext(win->src.hdc);
-		}
-		
-		wglMakeCurrent(win->src.hdc, win->src.ctx);
-#endif
-	}
-
-#ifdef RGFW_OSMESA
-#ifdef RGFW_LINK_OSM ESA
-		OSMesaMakeCurrentSource = (PFN_OSMesaMakeCurrent) GetProcAddress(win->src.hdc, "OSMesaMakeCurrent");
-		OSMesaCreateContextSource = (PFN_OSMesaCreateContext) GetProcAddress(win->src.hdc, "OSMesaCreateContext");
-		OSMesaDestroyContextSource = (PFN_OSMesaDestroyContext) GetProcAddress(win->src.hdc, "OSMesaDestroyContext");
-#endif
-#endif
-
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0) {
-			ReleaseDC(win->src.window, win->src.hdc);
-			win->src.hdc = GetDC(win->src.window);
-			wglMakeCurrent(win->src.hdc, win->src.ctx);
-		}
-#endif
-
-		DestroyWindow(dummyWin);
-		RGFW_init_buffer(win);
-
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-	
-		if (args & RGFW_CENTER) {
-			RGFW_area screenR = RGFW_getScreenSize();
-			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
-		}
-
-#ifdef RGFW_EGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			RGFW_createOpenGLContext(win);
-#endif
-
-		if (args & RGFW_HIDE_MOUSE)
-			RGFW_window_showMouse(win, 0);
-
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-			SetWindowLong(win->src.window, GWL_EXSTYLE, GetWindowLong(win->src.window, GWL_EXSTYLE) | WS_EX_LAYERED);
-			SetLayeredWindowAttributes(win->src.window, RGB(255, 255, 255), RGFW_ALPHA, LWA_ALPHA);
-		}
-
-		ShowWindow(win->src.window, SW_SHOWNORMAL);
-		
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-		
-		#ifdef RGFW_OPENGL
-		else 
-			wglShareLists(RGFW_root->src.ctx, win->src.ctx);
-		#endif
-
-		return win;
-	}
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		DWORD style = GetWindowLong(win->src.window, GWL_STYLE);
-
-		if (border == 0) {
-			SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
-			SetWindowPos(
-				win->src.window, HWND_TOP, 0, 0, 0, 0,
-				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
-			);
-		}
-		else {
-			SetWindowLong(win->src.window, GWL_STYLE, style | WS_OVERLAPPEDWINDOW);
-			SetWindowPos(
-				win->src.window, HWND_TOP, 0, 0, 0, 0,
-				SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
-			);
-		}
-	}
-
-
-	RGFW_area RGFW_getScreenSize(void) {
-		return RGFW_AREA(GetDeviceCaps(GetDC(NULL), HORZRES), GetDeviceCaps(GetDC(NULL), VERTRES));
-	}
-
-	RGFW_point RGFW_getGlobalMousePoint(void) {
-		POINT p;
-		GetCursorPos(&p);
-
-		return RGFW_POINT(p.x, p.y);
-	}
-
-	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
-		POINT p;
-		GetCursorPos(&p);
-		ScreenToClient(win->src.window, &p);
-
-		return RGFW_POINT(p.x, p.y);
-	}
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->src.minSize = a;
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-		win->src.maxSize = a;
-	}
-
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		ShowWindow(win->src.window, SW_MINIMIZE);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		ShowWindow(win->src.window, SW_RESTORE);
-	}
-
-
-	u8 RGFW_xinput2RGFW[] = {
-		RGFW_JS_A, /* or PS X button */
-		RGFW_JS_B, /* or PS circle button */
-		RGFW_JS_X, /* or PS square button */
-		RGFW_JS_Y, /* or PS triangle button */
-		RGFW_JS_R1, /* right bumper */
-		RGFW_JS_L1, /* left bump */
-		RGFW_JS_L2, /* left trigger*/
-		RGFW_JS_R2, /* right trigger */
-		0, 0, 0, 0, 0, 0, 0, 0,
-		RGFW_JS_UP, /* dpad up */
-		RGFW_JS_DOWN, /* dpad down*/
-		RGFW_JS_LEFT, /* dpad left */
-		RGFW_JS_RIGHT, /* dpad right */
-		RGFW_JS_START, /* start button */
-		RGFW_JS_SELECT/* select button */
-	};
-
-	static i32 RGFW_checkXInput(RGFW_window* win, RGFW_Event* e) {
-		RGFW_UNUSED(win)
-		
-		size_t i;
-		for (i = 0; i < 4; i++) {
-			XINPUT_KEYSTROKE keystroke;
-
-			if (XInputGetKeystroke == NULL)
-				return 0;
-
-			DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke);
-
-			if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) {
-				if (result != ERROR_SUCCESS)
-					return 0;
-
-				if (keystroke.VirtualKey > VK_PAD_BACK)
-					continue;
-
-				// RGFW_jsButtonPressed + 1 = RGFW_jsButtonReleased
-				e->type = RGFW_jsButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
-				e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800];
-				RGFW_jsPressed[i][e->button] = !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
-
-				return 1;
-			}
-
-			XINPUT_STATE state;
-			if (XInputGetState == NULL ||
-				XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED
-			)
-				return 0;
-#define INPUT_DEADZONE  ( 0.24f * (float)(0x7FFF) )  // Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed.
-
-			if ((state.Gamepad.sThumbLX < INPUT_DEADZONE &&
-				state.Gamepad.sThumbLX > -INPUT_DEADZONE) &&
-				(state.Gamepad.sThumbLY < INPUT_DEADZONE &&
-					state.Gamepad.sThumbLY > -INPUT_DEADZONE))
-			{
-				state.Gamepad.sThumbLX = 0;
-				state.Gamepad.sThumbLY = 0;
-			}
-
-			if ((state.Gamepad.sThumbRX < INPUT_DEADZONE &&
-				state.Gamepad.sThumbRX > -INPUT_DEADZONE) &&
-				(state.Gamepad.sThumbRY < INPUT_DEADZONE &&
-					state.Gamepad.sThumbRY > -INPUT_DEADZONE))
-			{
-				state.Gamepad.sThumbRX = 0;
-				state.Gamepad.sThumbRY = 0;
-			}
-
-			e->axisesCount = 2;
-			RGFW_point axis1 = RGFW_POINT(state.Gamepad.sThumbLX, state.Gamepad.sThumbLY);
-			RGFW_point axis2 = RGFW_POINT(state.Gamepad.sThumbRX, state.Gamepad.sThumbRY);
-
-			if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y || axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) {
-				e->type = RGFW_jsAxisMove;
-
-				e->axis[0] = axis1;
-				e->axis[1] = axis2;
-
-				return 1;
-			}
-
-			e->axis[0] = axis1;
-			e->axis[1] = axis2;
-		}
-
-		return 0;
-	}
-
-	void RGFW_stopCheckEvents(void) { 
-		PostMessageW(RGFW_root->src.window, WM_NULL, 0, 0);
-	}
-
-	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
-		RGFW_UNUSED(win);
-
-		MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (waitMS * 1e3), QS_ALLINPUT);
-	}
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-
-		if (win->event.type == RGFW_quit) {
-			return NULL;
-		}
-
-		MSG msg;
-
-		if (RGFW_eventWindow.src.window == win->src.window) {
-			if (RGFW_eventWindow.r.x != -1) {
-				win->r.x = RGFW_eventWindow.r.x;
-				win->r.y = RGFW_eventWindow.r.y;
-				win->event.type = RGFW_windowMoved;
-				RGFW_windowMoveCallback(win, win->r);
-			}
-
-			if (RGFW_eventWindow.r.w != -1) {
-				win->r.w = RGFW_eventWindow.r.w;
-				win->r.h = RGFW_eventWindow.r.h;
-				win->event.type = RGFW_windowResized;
-				RGFW_windowResizeCallback(win, win->r);
-			}
-
-			RGFW_eventWindow.src.window = NULL;
-			RGFW_eventWindow.r = RGFW_RECT(-1, -1, -1, -1);
-
-			return &win->event;
-		}
-
-
-		static HDROP drop;
-		
-		if (win->event.type == RGFW_dnd_init) {
-			if (win->event.droppedFilesCount) {
-				u32 i;
-				for (i = 0; i < win->event.droppedFilesCount; i++)
-					win->event.droppedFiles[i][0] = '\0';
-			}
-
-			win->event.droppedFilesCount = 0;
-			win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0);
-			//win->event.droppedFiles = (char**)RGFW_CALLOC(win->event.droppedFilesCount, sizeof(char*));
-
-			u32 i;
-			for (i = 0; i < win->event.droppedFilesCount; i++) {
-				const UINT length = DragQueryFileW(drop, i, NULL, 0);
-				WCHAR* buffer = (WCHAR*) RGFW_CALLOC((size_t) length + 1, sizeof(WCHAR));
-
-				DragQueryFileW(drop, i, buffer, length + 1);
-				strncpy(win->event.droppedFiles[i], createUTF8FromWideStringWin32(buffer), RGFW_MAX_PATH);
-				win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
-				RGFW_FREE(buffer);
-			}
-
-			DragFinish(drop);
-			RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-			
-			win->event.type = RGFW_dnd;
-			return &win->event;
-		}
-
-		win->event.inFocus = (GetForegroundWindow() == win->src.window);
-
-		if (RGFW_checkXInput(win, &win->event))
-			return &win->event;
-
-		static BYTE keyboardState[256];
-
-		if (PeekMessageA(&msg, win->src.window, 0u, 0u, PM_REMOVE)) {
-			switch (msg.message) {
-			case WM_CLOSE:
-			case WM_QUIT:
-				RGFW_windowQuitCallback(win);
-				win->event.type = RGFW_quit;
-				break;
-
-			case WM_ACTIVATE:
-				win->event.inFocus = (LOWORD(msg.wParam) == WA_INACTIVE);
-
-				if (win->event.inFocus) {
-					win->event.type = RGFW_focusIn;
-					RGFW_focusCallback(win, 1);
-				}
-				else {
-					win->event.type = RGFW_focusOut;
-					RGFW_focusCallback(win, 0);
-				}
-
-				break;
-			
-			case WM_PAINT:
-				win->event.type = RGFW_windowRefresh;
-				RGFW_windowRefreshCallback(win);
-				break;
-			
-			case WM_MOUSELEAVE:
-				win->event.type = RGFW_mouseLeave;
-				win->_winArgs |= RGFW_MOUSE_LEFT;
-				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-				break;
-			
-			case WM_KEYUP: {
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
-								
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-
-				static char keyName[16];
-				
-				{
-					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
-
-					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
-						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
-						CharLowerBuffA(keyName, 16);
-					}
-				}
-
-				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
-
-				strncpy(win->event.keyName, keyName, 16);
-
-				if (RGFW_isPressed(win, RGFW_ShiftL)) {
-					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
-						keyboardState, (LPWORD) win->event.keyName, 0);
-				}
-
-				win->event.type = RGFW_keyReleased;
-				RGFW_keyboard[win->event.keyCode].current = 0;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
-				break;
-			}
-			case WM_KEYDOWN: {
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW((u32) msg.wParam);
-
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_isPressed(win, win->event.keyCode);
-
-				static char keyName[16];
-				
-				{
-					GetKeyNameTextA((LONG) msg.lParam, keyName, 16);
-
-					if ((!(GetKeyState(VK_CAPITAL) & 0x0001) && !(GetKeyState(VK_SHIFT) & 0x8000)) ||
-						((GetKeyState(VK_CAPITAL) & 0x0001) && (GetKeyState(VK_SHIFT) & 0x8000))) {
-						CharLowerBuffA(keyName, 16);
-					}
-				}
-				
-				RGFW_updateLockState(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001));
-
-				strncpy(win->event.keyName, keyName, 16);
-
-				if (RGFW_isPressed(win, RGFW_ShiftL) & 0x8000) {
-					ToAscii((UINT) msg.wParam, MapVirtualKey((UINT) msg.wParam, MAPVK_VK_TO_CHAR),
-						keyboardState, (LPWORD) win->event.keyName, 0);
-				}
-
-				win->event.type = RGFW_keyPressed;
-				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
-				RGFW_keyboard[win->event.keyCode].current = 1;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
-				break;
-			}
-
-			case WM_MOUSEMOVE:
-				if ((win->_winArgs & RGFW_HOLD_MOUSE))
-					break;
-
-				win->event.type = RGFW_mousePosChanged;
-
-				win->event.point.x = GET_X_LPARAM(msg.lParam);
-				win->event.point.y = GET_Y_LPARAM(msg.lParam);
-				
-				RGFW_mousePosCallback(win, win->event.point);
-
-				if (win->_winArgs & RGFW_MOUSE_LEFT) {
-					win->_winArgs ^= RGFW_MOUSE_LEFT;
-					win->event.type = RGFW_mouseEnter;
-					RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-				}
-
-				break;
-
-			case WM_INPUT: {
-				if (!(win->_winArgs & RGFW_HOLD_MOUSE))
-					break;
-				
-				unsigned size = sizeof(RAWINPUT);
-				static RAWINPUT raw[sizeof(RAWINPUT)];
-				GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER));
-
-				if (raw->header.dwType != RIM_TYPEMOUSE || (raw->data.mouse.lLastX == 0 && raw->data.mouse.lLastY == 0) )
-					break;
-				
-				win->event.type = RGFW_mousePosChanged;
-				win->event.point.x = raw->data.mouse.lLastX;
-				win->event.point.y = raw->data.mouse.lLastY;
-				break;
-			}
-
-			case WM_LBUTTONDOWN:
-				win->event.button = RGFW_mouseLeft;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			case WM_RBUTTONDOWN:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			case WM_MBUTTONDOWN:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case WM_MOUSEWHEEL:
-				if (msg.wParam > 0)
-					win->event.button = RGFW_mouseScrollUp;
-				else
-					win->event.button = RGFW_mouseScrollDown;
-
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-
-				win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA;
-
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case WM_LBUTTONUP:
-			
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-			case WM_RBUTTONUP:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-			case WM_MBUTTONUP:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonReleased;
-
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-				/*
-					much of this event is source from glfw
-				*/
-			case WM_DROPFILES: {				
-				win->event.type = RGFW_dnd_init;
-
-				drop = (HDROP) msg.wParam;
-				POINT pt;
-
-				/* Move the mouse to the position of the drop */
-				DragQueryPoint(drop, &pt);
-
-				win->event.point.x = pt.x;
-				win->event.point.y = pt.y;
-
-				RGFW_dndInitCallback(win, win->event.point);
-			}
-				break;
-			case WM_GETMINMAXINFO:
-			{
-				if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0)
-					break;
-
-				MINMAXINFO* mmi = (MINMAXINFO*) msg.lParam;
-				mmi->ptMinTrackSize.x = win->src.minSize.w;
-				mmi->ptMinTrackSize.y = win->src.minSize.h;
-				mmi->ptMaxTrackSize.x = win->src.maxSize.w;
-				mmi->ptMaxTrackSize.y = win->src.maxSize.h;
-				return 0;
-			}
-			default:
-				win->event.type = 0;
-				break;
-			}
-
-			TranslateMessage(&msg);
-			DispatchMessageA(&msg);
-		}
-
-		else
-			win->event.type = 0;
-
-		if (!IsWindow(win->src.window)) {
-			win->event.type = RGFW_quit;
-			RGFW_windowQuitCallback(win);
-		}
-
-		if (win->event.type)
-			return &win->event;
-		else
-			return NULL;
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		#ifndef __cplusplus
-		WINDOWPLACEMENT placement = { 0 };
-		#else
-		WINDOWPLACEMENT placement = {  };
-		#endif
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMAXIMIZED;
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		#ifndef __cplusplus
-		WINDOWPLACEMENT placement = { 0 };
-		#else
-		WINDOWPLACEMENT placement = {  };
-		#endif
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMINIMIZED;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		#ifndef __cplusplus
-		WINDOWPLACEMENT placement = { 0 };
-		#else
-		WINDOWPLACEMENT placement = {  };
-		#endif
-		GetWindowPlacement(win->src.window, &placement);
-		return placement.showCmd == SW_SHOWMAXIMIZED;
-	}
-
-	typedef struct { int iIndex; HMONITOR hMonitor; } RGFW_mInfo;
-	BOOL CALLBACK GetMonitorByHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-		RGFW_UNUSED(hdcMonitor)
-		RGFW_UNUSED(lprcMonitor)
-
-		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
-		if (info->hMonitor == hMonitor)
-			return FALSE;
-
-		info->iIndex++;
-		return TRUE;
-	}
-	
-	#ifndef RGFW_NO_MONITOR
-	RGFW_monitor win32CreateMonitor(HMONITOR src) {
-		RGFW_monitor monitor;
-		MONITORINFO monitorInfo;
-
-		monitorInfo.cbSize = sizeof(MONITORINFO);
-		GetMonitorInfoA(src, &monitorInfo);
-
-		RGFW_mInfo info;
-		info.iIndex = 0;
-		info.hMonitor = src;
-
-		/* get the monitor's index */
-		if (EnumDisplayMonitors(NULL, NULL, GetMonitorByHandle, (LPARAM) &info)) {
-			DISPLAY_DEVICEA dd;
-			dd.cb = sizeof(dd);
-
-			/* loop through the devices until you find a device with the monitor's index */
-			size_t deviceIndex;
-			for (deviceIndex = 0; EnumDisplayDevicesA(0, (DWORD) deviceIndex, &dd, 0); deviceIndex++) {
-				char* deviceName = dd.DeviceName;
-				if (EnumDisplayDevicesA(deviceName, info.iIndex, &dd, 0)) {
-					strncpy(monitor.name, dd.DeviceString, 128); /*!< copy the monitor's name */
-					break;
-				}
-			}
-		}
-
-		monitor.rect.x = monitorInfo.rcWork.left;
-		monitor.rect.y = monitorInfo.rcWork.top;
-		monitor.rect.w = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
-		monitor.rect.h = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
-
-#ifndef RGFW_NO_DPI
-		#ifndef USER_DEFAULT_SCREEN_DPI
-		#define USER_DEFAULT_SCREEN_DPI 96
-		#endif
-
-		if (GetDpiForMonitor != NULL) {
-			u32 x, y;
-			GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y);
-			
-			monitor.scaleX = (float) (x) / (float) USER_DEFAULT_SCREEN_DPI;
-			monitor.scaleY = (float) (y) / (float) USER_DEFAULT_SCREEN_DPI;
-		}
-#endif
-
-		HDC hdc = GetDC(NULL);
-		/* get pixels per inch */
-		i32 ppiX = GetDeviceCaps(hdc, LOGPIXELSX);
-		i32 ppiY = GetDeviceCaps(hdc, LOGPIXELSY);
-		ReleaseDC(NULL, hdc);
-
-		/* Calculate physical height in inches */
-		monitor.physW = GetSystemMetrics(SM_CYSCREEN) / (float) ppiX;
-		monitor.physH = GetSystemMetrics(SM_CXSCREEN) / (float) ppiY;
-		
-		return monitor;
-	}
-	#endif /* RGFW_NO_MONITOR */
-	
-
-	#ifndef RGFW_NO_MONITOR
-	RGFW_monitor RGFW_monitors[6];
-	BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-		RGFW_UNUSED(hdcMonitor)
-		RGFW_UNUSED(lprcMonitor)
-
-		RGFW_mInfo* info = (RGFW_mInfo*) dwData;
-
-		if (info->iIndex >= 6)
-			return FALSE;
-
-		RGFW_monitors[info->iIndex] = win32CreateMonitor(hMonitor);
-		info->iIndex++;
-
-		return TRUE;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-        #ifdef __cplusplus
-        return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
-        #else
-		return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
-	    #endif
-    }
-
-	RGFW_monitor* RGFW_getMonitors(void) {
-		RGFW_mInfo info;
-		info.iIndex = 0;
-		while (EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info));
-			
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
-		return win32CreateMonitor(src);
-	}
-	#endif
-
-	HICON RGFW_loadHandleImage(RGFW_window* win, u8* src, RGFW_area a, BOOL icon) {
-		assert(win != NULL);
-
-		u32 i;
-		HDC dc;
-		HICON handle;
-		HBITMAP color, mask;
-		BITMAPV5HEADER bi;
-		ICONINFO ii;
-		u8* target = NULL;
-		u8* source = src;
-
-		ZeroMemory(&bi, sizeof(bi));
-		bi.bV5Size = sizeof(bi);
-		bi.bV5Width = a.w;
-		bi.bV5Height = -((LONG) a.h);
-		bi.bV5Planes = 1;
-		bi.bV5BitCount = 32;
-		bi.bV5Compression = BI_BITFIELDS;
-		bi.bV5RedMask = 0x00ff0000;
-		bi.bV5GreenMask = 0x0000ff00;
-		bi.bV5BlueMask = 0x000000ff;
-		bi.bV5AlphaMask = 0xff000000;
-
-		dc = GetDC(NULL);
-		color = CreateDIBSection(dc,
-			(BITMAPINFO*) &bi,
-			DIB_RGB_COLORS,
-			(void**) &target,
-			NULL,
-			(DWORD) 0);
-		ReleaseDC(NULL, dc);
-
-		mask = CreateBitmap(a.w, a.h, 1, 1, NULL);
-
-		for (i = 0; i < a.w * a.h; i++) {
-			target[0] = source[2];
-			target[1] = source[1];
-			target[2] = source[0];
-			target[3] = source[3];
-			target += 4;
-			source += 4;
-		}
-
-		ZeroMemory(&ii, sizeof(ii));
-		ii.fIcon = icon;
-		ii.xHotspot = 0;
-		ii.yHotspot = 0;
-		ii.hbmMask = mask;
-		ii.hbmColor = color;
-
-		handle = CreateIconIndirect(&ii);
-
-		DeleteObject(color);
-		DeleteObject(mask);
-
-		return handle;
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-		RGFW_UNUSED(channels)
-
-		HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(win, image, a, FALSE);
-		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) cursor);
-		SetCursor(cursor);
-		DestroyCursor(cursor);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-		assert(win != NULL);
-
-		if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u32)))
-			return;
-
-		char* icon = MAKEINTRESOURCEA(RGFW_mouseIconSrc[mouse]);
-
-		SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
-		SetCursor(LoadCursorA(NULL, icon));
-	}
-
-	void RGFW_window_hide(RGFW_window* win) {
-		ShowWindow(win->src.window, SW_HIDE);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		ShowWindow(win->src.window, SW_RESTORE);
-	}
-
-	void RGFW_window_close(RGFW_window* win) {
-		assert(win != NULL);
-
-#ifdef RGFW_EGL
-		RGFW_closeEGL(win);
-#endif
-
-		if (win == RGFW_root) {
-#ifdef RGFW_DIRECTX
-			RGFW_dxInfo.pDeviceContext->lpVtbl->Release(RGFW_dxInfo.pDeviceContext);
-			RGFW_dxInfo.pDevice->lpVtbl->Release(RGFW_dxInfo.pDevice);
-			RGFW_dxInfo.pAdapter->lpVtbl->Release(RGFW_dxInfo.pAdapter);
-			RGFW_dxInfo.pFactory->lpVtbl->Release(RGFW_dxInfo.pFactory);
-#endif
-		
-			if (RGFW_XInput_dll != NULL) {
-				FreeLibrary(RGFW_XInput_dll);
-				RGFW_XInput_dll = NULL;
-			}
-
-			#ifndef RGFW_NO_DPI
-			if (RGFW_Shcore_dll != NULL) {
-				FreeLibrary(RGFW_Shcore_dll);
-				RGFW_Shcore_dll = NULL;
-			}
-			#endif
-
-			if (wglinstance != NULL) {
-				FreeLibrary(wglinstance);
-				wglinstance = NULL;
-			}
-
-			RGFW_root = NULL;
-		}
-
-#ifdef RGFW_DIRECTX
-		win->src.swapchain->lpVtbl->Release(win->src.swapchain);
-		win->src.renderTargetView->lpVtbl->Release(win->src.renderTargetView);
-		win->src.pDepthStencilView->lpVtbl->Release(win->src.pDepthStencilView);
-#endif
-
-#ifdef RGFW_BUFFER
-		DeleteDC(win->src.hdcMem);
-		DeleteObject(win->src.bitmap);
-#endif
-
-#ifdef RGFW_OPENGL
-		wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */
-#endif
-		DeleteDC(win->src.hdc); /*!< delete device context */
-		DestroyWindow(win->src.window); /*!< delete window */
-
-#if defined(RGFW_OSMESA)
-		if (win->buffer != NULL)
-			RGFW_FREE(win->buffer);
-#endif
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-
-		RGFW_FREE(win);
-	}
-
-	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
-		assert(win != NULL);
-
-		win->r.x = v.x;
-		win->r.y = v.y;
-		SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE);
-	}
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		win->r.w = a.w;
-		win->r.h = a.h;
-		SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w, win->r.h + win->src.hOffset, SWP_NOMOVE);
-	}
-
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		SetWindowTextA(win->src.window, name);
-	}
-
-	/* sourced from GLFW */
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		assert(win != NULL);
-		
-		COLORREF key = 0;
-		BYTE alpha = 0;
-		DWORD flags = 0;
-		DWORD exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
-		
-		if (exStyle & WS_EX_LAYERED)
-			GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
-
-		if (passthrough)
-			exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
-		else
-		{
-			exStyle &= ~WS_EX_TRANSPARENT;
-			// NOTE: Window opacity also needs the layered window style so do not
-			//       remove it if the window is alpha blended
-			if (exStyle & WS_EX_LAYERED)
-			{
-				if (!(flags & LWA_ALPHA))
-					exStyle &= ~WS_EX_LAYERED;
-			}
-		}
-
-		SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
-
-		if (passthrough) {
-			SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
-		}
-	}
-	#endif
-
-	/* much of this function is sourced from GLFW */
-	void RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-		#ifndef RGFW_WIN95
-		RGFW_UNUSED(channels)
-
-		HICON handle = RGFW_loadHandleImage(win, src, a, TRUE);
-
-		SetClassLongPtrA(win->src.window, GCLP_HICON, (LPARAM) handle);
-
-		DestroyIcon(handle);
-		#else
-		RGFW_UNUSED(src)
-		RGFW_UNUSED(a)
-		RGFW_UNUSED(channels)
-		#endif
-	}
-
-	char* RGFW_readClipboard(size_t* size) {
-		/* Open the clipboard */
-		if (OpenClipboard(NULL) == 0)
-			return (char*) "";
-
-		/* Get the clipboard data as a Unicode string */
-		HANDLE hData = GetClipboardData(CF_UNICODETEXT);
-		if (hData == NULL) {
-			CloseClipboard();
-			return (char*) "";
-		}
-
-		wchar_t* wstr = (wchar_t*) GlobalLock(hData);
-
-		char* text;
-
-		{
-			setlocale(LC_ALL, "en_US.UTF-8");
-
-			size_t textLen = wcstombs(NULL, wstr, 0);
-			if (textLen == 0)
-				return (char*) "";
-
-			text = (char*) RGFW_MALLOC((textLen * sizeof(char)) + 1);
-
-			wcstombs(text, wstr, (textLen) +1);
-
-			if (size != NULL)
-				*size = textLen + 1;
-
-			text[textLen] = '\0';
-		}
-
-		/* Release the clipboard data */
-		GlobalUnlock(hData);
-		CloseClipboard();
-
-		return text;
-	}
-
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		HANDLE object;
-		WCHAR* buffer;
-
-		object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
-		if (!object)
-			return;
-
-		buffer = (WCHAR*) GlobalLock(object);
-		if (!buffer) {
-			GlobalFree(object);
-			return;
-		}
-
-		MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, textLen);
-		GlobalUnlock(object);
-
-		if (!OpenClipboard(RGFW_root->src.window)) {
-			GlobalFree(object);
-			return;
-		}
-
-		EmptyClipboard();
-		SetClipboardData(CF_UNICODETEXT, object);
-		CloseClipboard();
-	}
-
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		assert(win != NULL);
-
-		RGFW_UNUSED(jsNumber)
-
-		return RGFW_registerJoystickF(win, (char*) "");
-	}
-
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		assert(win != NULL);
-		RGFW_UNUSED(file)
-
-		return RGFW_joystickCount - 1;
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
-		assert(win != NULL);
-
-		SetCursorPos(p.x, p.y);
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		if (win == NULL)
-			wglMakeCurrent(NULL, NULL);
-		else
-			wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx);
-	}
-	#endif
-
-	#ifndef RGFW_EGL
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		
-		#if defined(RGFW_OPENGL)
-		typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
-		static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
-		static void* loadSwapFunc = (void*) 1;
-
-		if (loadSwapFunc == NULL) {
-			fprintf(stderr, "wglSwapIntervalEXT not supported\n");
-			return;
-		}
-
-		if (wglSwapIntervalEXT == NULL) {
-			loadSwapFunc = (void*) wglGetProcAddress("wglSwapIntervalEXT");
-			wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) loadSwapFunc;
-		}
-
-		if (wglSwapIntervalEXT(swapInterval) == FALSE)
-			fprintf(stderr, "Failed to set swap interval\n");
-		#else
-        RGFW_UNUSED(swapInterval);
-        #endif
-
-	}
-	#endif
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		//assert(win != NULL);
-		/* clear the window*/
-
-		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-
-			HGDIOBJ oldbmp = SelectObject(win->src.hdcMem, win->src.bitmap);
-			BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY);
-			SelectObject(win->src.hdcMem, oldbmp);
-#endif
-		}
-
-		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					SwapBuffers(win->src.hdc);
-			#endif
-
-			#if defined(RGFW_WINDOWS) && defined(RGFW_DIRECTX)
-					win->src.swapchain->lpVtbl->Present(win->src.swapchain, 0, 0);
-			#endif
-		}
-	}
-
-	char* createUTF8FromWideStringWin32(const WCHAR* source) {
-		char* target;
-		i32 size;
-
-		size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
-		if (!size) {
-			return NULL;
-		}
-
-		target = (char*) RGFW_CALLOC(size, 1);
-
-		if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) {
-			RGFW_FREE(target);
-			return NULL;
-		}
-
-		return target;
-	}
-	
-    static inline LARGE_INTEGER RGFW_win32_initTimer(void) {
-		static LARGE_INTEGER frequency = {{0, 0}};
-		if (frequency.QuadPart == 0) {
-			timeBeginPeriod(1);
-			QueryPerformanceFrequency(&frequency);
-		}
-
-		return frequency;
-	}
-
-	u64 RGFW_getTimeNS(void) {
-		LARGE_INTEGER frequency = RGFW_win32_initTimer();
-
-		LARGE_INTEGER counter;
-		QueryPerformanceCounter(&counter);
-
-		return (u64) ((counter.QuadPart * 1e9) / frequency.QuadPart);
-	}
-
-	u64 RGFW_getTime(void) {
-		LARGE_INTEGER frequency = RGFW_win32_initTimer();
-
-		LARGE_INTEGER counter;
-		QueryPerformanceCounter(&counter);
-		return (u64) (counter.QuadPart / (double) frequency.QuadPart);
-	}
-	
-	void RGFW_sleep(u64 ms) {
-		Sleep(ms);
-	}
-
-#ifndef RGFW_NO_THREADS
-	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); }
-	void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); }
-	void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); }
-	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); }
-#endif
-#endif /* RGFW_WINDOWS */
-
-/*
-	End of Windows defines
-*/
-
-
-
-/* 
-
-	Start of MacOS defines
-
-
-*/
-
-#if defined(RGFW_MACOS)
-	/*
-		based on silicon.h
-		start of cocoa wrapper
-	*/
-
-#include <CoreVideo/CVDisplayLink.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <objc/runtime.h>
-#include <objc/message.h>
-#include <mach/mach_time.h>
-
-	typedef CGRect NSRect;
-	typedef CGPoint NSPoint;
-	typedef CGSize NSSize;
-
-	typedef void NSBitmapImageRep;
-	typedef void NSCursor;
-	typedef void NSDraggingInfo;
-	typedef void NSWindow;
-	typedef void NSApplication;
-	typedef void NSScreen;
-	typedef void NSEvent;
-	typedef void NSString;
-	typedef void NSOpenGLContext;
-	typedef void NSPasteboard;
-	typedef void NSColor;
-	typedef void NSArray;
-	typedef void NSImageRep;
-	typedef void NSImage;
-	typedef void NSOpenGLView;
-
-
-	typedef const char* NSPasteboardType;
-	typedef unsigned long NSUInteger;
-	typedef long NSInteger;
-	typedef NSInteger NSModalResponse;
-
-#ifdef __arm64__
-	/* ARM just uses objc_msgSend */
-#define abi_objc_msgSend_stret objc_msgSend
-#define abi_objc_msgSend_fpret objc_msgSend
-#else /* __i386__ */ 
-	/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
-#define abi_objc_msgSend_stret objc_msgSend_stret
-#define abi_objc_msgSend_fpret objc_msgSend_fpret
-#endif
-
-#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
-#define objc_msgSend_bool			((BOOL (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void			((void (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void_id		((void (*)(id, SEL, id))objc_msgSend)
-#define objc_msgSend_uint			((NSUInteger (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void_bool		((void (*)(id, SEL, BOOL))objc_msgSend)
-#define objc_msgSend_bool_void		((BOOL (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_void_SEL		((void (*)(id, SEL, SEL))objc_msgSend)
-#define objc_msgSend_id				((id (*)(id, SEL))objc_msgSend)
-#define objc_msgSend_id_id				((id (*)(id, SEL, id))objc_msgSend)
-#define objc_msgSend_id_bool			((BOOL (*)(id, SEL, id))objc_msgSend)
-#define objc_msgSend_int ((id (*)(id, SEL, int))objc_msgSend)
-#define objc_msgSend_arr ((id (*)(id, SEL, int))objc_msgSend)
-#define objc_msgSend_ptr ((id (*)(id, SEL, void*))objc_msgSend)
-#define objc_msgSend_class ((id (*)(Class, SEL))objc_msgSend)
-#define objc_msgSend_class_char ((id (*)(Class, SEL, char*))objc_msgSend)
-
-	NSApplication* NSApp = NULL;
-
-	void NSRelease(id obj) {
-		objc_msgSend_void(obj, sel_registerName("release"));
-	}
-
-	#define release NSRelease
-
-	NSString* NSString_stringWithUTF8String(const char* str) {	
-		return ((id(*)(id, SEL, const char*))objc_msgSend)
-			((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
-	}
-
-	const char* NSString_to_char(NSString* str) {
-		return ((const char* (*)(id, SEL)) objc_msgSend) (str, sel_registerName("UTF8String"));
-	}
-
-	void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) {
-		Class selected_class;
-
-		if (strcmp(class_name, "NSView") == 0) {
-			selected_class = objc_getClass("ViewClass");
-		} else if (strcmp(class_name, "NSWindow") == 0) {
-			selected_class = objc_getClass("WindowClass");
-		} else {
-			selected_class = objc_getClass(class_name);
-		}
-
-		class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0);
-	}
-
-	/* Header for the array. */
-	typedef struct siArrayHeader {
-		size_t count;
-		/* TODO(EimaMei): Add a `type_width` later on. */
-	} siArrayHeader;
-
-	/* Gets the header of the siArray. */
-#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1)
-
-	void* si_array_init_reserve(size_t sizeof_element, size_t count) {
-		siArrayHeader* ptr = malloc(sizeof(siArrayHeader) + (sizeof_element * count));
-		void* array = ptr + sizeof(siArrayHeader);
-
-		siArrayHeader* header = SI_ARRAY_HEADER(array);
-		header->count = count;
-
-		return array;
-	}
-
-#define si_array_len(array) (SI_ARRAY_HEADER(array)->count)
-#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", function)
-	/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/
-#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", function)
-	
-	unsigned char* NSBitmapImageRep_bitmapData(NSBitmapImageRep* imageRep) {
-		return ((unsigned char* (*)(id, SEL))objc_msgSend)
-			(imageRep, sel_registerName("bitmapData"));
-	}
-
-#define NS_ENUM(type, name) type name; enum
-
-	typedef NS_ENUM(NSUInteger, NSBitmapFormat) {
-		NSBitmapFormatAlphaFirst = 1 << 0,       // 0 means is alpha last (RGBA, CMYKA, etc.)
-			NSBitmapFormatAlphaNonpremultiplied = 1 << 1,       // 0 means is premultiplied
-			NSBitmapFormatFloatingPointSamples = 1 << 2,  // 0 is integer
-
-			NSBitmapFormatSixteenBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 8),
-			NSBitmapFormatThirtyTwoBitLittleEndian API_AVAILABLE(macos(10.10)) = (1 << 9),
-			NSBitmapFormatSixteenBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 10),
-			NSBitmapFormatThirtyTwoBitBigEndian API_AVAILABLE(macos(10.10)) = (1 << 11)
-	};
-
-	NSBitmapImageRep* NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
-		void* func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
-
-		return (NSBitmapImageRep*) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, const char*, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
-	}
-
-	NSColor* NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
-		void* nsclass = objc_getClass("NSColor");
-		void* func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
-		return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
-			(nsclass, func, red, green, blue, alpha);
-	}
-
-	NSCursor* NSCursor_initWithImage(NSImage* newImage, NSPoint aPoint) {
-		void* func = sel_registerName("initWithImage:hotSpot:");
-		void* nsclass = objc_getClass("NSCursor");
-
-		return (NSCursor*) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
-			(NSAlloc(nsclass), func, newImage, aPoint);
-	}
-
-	void NSImage_addRepresentation(NSImage* image, NSImageRep* imageRep) {
-		void* func = sel_registerName("addRepresentation:");
-		objc_msgSend_void_id(image, func, imageRep);
-	}
-
-	NSImage* NSImage_initWithSize(NSSize size) {
-		void* func = sel_registerName("initWithSize:");
-		return ((id(*)(id, SEL, NSSize))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSImage")), func, size);
-	}
-#define NS_OPENGL_ENUM_DEPRECATED(minVers, maxVers) API_AVAILABLE(macos(minVers))
-	typedef NS_ENUM(NSInteger, NSOpenGLContextParameter) {
-		NSOpenGLContextParameterSwapInterval           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 222, /* 1 param.  0 -> Don't sync, 1 -> Sync to vertical retrace     */
-			NSOpenGLContextParametectxaceOrder           NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 235, /* 1 param.  1 -> Above Window (default), -1 -> Below Window    */
-			NSOpenGLContextParametectxaceOpacity         NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 236, /* 1 param.  1-> Surface is opaque (default), 0 -> non-opaque   */
-			NSOpenGLContextParametectxaceBackingSize     NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 304, /* 2 params.  Width/height of surface backing size              */
-			NSOpenGLContextParameterReclaimResources       NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 308, /* 0 params.                                                    */
-			NSOpenGLContextParameterCurrentRendererID      NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 309, /* 1 param.   Retrieves the current renderer ID                 */
-			NSOpenGLContextParameterGPUVertexProcessing    NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 310, /* 1 param.   Currently processing vertices with GPU (get)      */
-			NSOpenGLContextParameterGPUFragmentProcessing  NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 311, /* 1 param.   Currently processing fragments with GPU (get)     */
-			NSOpenGLContextParameterHasDrawable            NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 314, /* 1 param.   Boolean returned if drawable is attached          */
-			NSOpenGLContextParameterMPSwapsInFlight        NS_OPENGL_ENUM_DEPRECATED(10.0, 10.14) = 315, /* 1 param.   Max number of swaps queued by the MP GL engine    */
-
-			NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params.  Set or get the swap rectangle {x, y, w, h} */
-			NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
-			NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
-			NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
-			NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param.   Surface volatile state */
-	};
-
-
-	void NSOpenGLContext_setValues(NSOpenGLContext* context, const int* vals, NSOpenGLContextParameter param) {
-		void* func = sel_registerName("setValues:forParameter:");
-		((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
-			(context, func, vals, param);
-	}
-
-	void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) {
-		void* func = sel_registerName("initWithAttributes:");
-		return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), func, attribs);
-	}
-
-	NSOpenGLView* NSOpenGLView_initWithFrame(NSRect frameRect, uint32_t* format) {
-		void* func = sel_registerName("initWithFrame:pixelFormat:");
-		return (NSOpenGLView*) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSOpenGLView")), func, frameRect, format);
-	}
-
-	void NSCursor_performSelector(NSCursor* cursor, void* selector) {
-		void* func = sel_registerName("performSelector:");
-		objc_msgSend_void_SEL(cursor, func, selector);
-	}
-
-	NSPasteboard* NSPasteboard_generalPasteboard(void) {
-		return (NSPasteboard*) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
-	}
-
-	NSString** cstrToNSStringArray(char** strs, size_t len) {
-		static NSString* nstrs[6];
-		size_t i;
-		for (i = 0; i < len; i++)
-			nstrs[i] = NSString_stringWithUTF8String(strs[i]);
-
-		return nstrs;
-	}
-
-	const char* NSPasteboard_stringForType(NSPasteboard* pasteboard, NSPasteboardType dataType) {
-		void* func = sel_registerName("stringForType:");
-		return (const char*) NSString_to_char(((id(*)(id, SEL, const char*))objc_msgSend)(pasteboard, func, NSString_stringWithUTF8String(dataType)));
-	}
-
-	NSArray* c_array_to_NSArray(void* array, size_t len) {
-		SEL func = sel_registerName("initWithObjects:count:");
-		void* nsclass = objc_getClass("NSArray");
-		return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend)
-					(NSAlloc(nsclass), func, array, len);
-	}
- 
-	void NSregisterForDraggedTypes(void* view, NSPasteboardType* newTypes, size_t len) {
-		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
-
-		NSArray* array = c_array_to_NSArray(ntypes, len);
-		objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
-		NSRelease(array);
-	}
-
-	NSInteger NSPasteBoard_declareTypes(NSPasteboard* pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
-		NSString** ntypes = cstrToNSStringArray((char**)newTypes, len);
-
-		void* func = sel_registerName("declareTypes:owner:");
-
-		NSArray* array = c_array_to_NSArray(ntypes, len);
-
-		NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
-			(pasteboard, func, array, owner);
-		NSRelease(array);
-
-		return output;
-	}
-
-	bool NSPasteBoard_setString(NSPasteboard* pasteboard, const char* stringToWrite, NSPasteboardType dataType) {
-		void* func = sel_registerName("setString:forType:");
-		return ((bool (*)(id, SEL, id, NSPasteboardType))objc_msgSend)
-			(pasteboard, func, NSString_stringWithUTF8String(stringToWrite), NSString_stringWithUTF8String(dataType));
-	}
-
-	void NSRetain(id obj) { objc_msgSend_void(obj, sel_registerName("retain")); }
-
-	typedef enum NSApplicationActivationPolicy {
-		NSApplicationActivationPolicyRegular,
-		NSApplicationActivationPolicyAccessory,
-		NSApplicationActivationPolicyProhibited
-	} NSApplicationActivationPolicy;
-
-	typedef NS_ENUM(u32, NSBackingStoreType) {
-		NSBackingStoreRetained = 0,
-			NSBackingStoreNonretained = 1,
-			NSBackingStoreBuffered = 2
-	};
-
-	typedef NS_ENUM(u32, NSWindowStyleMask) {
-		NSWindowStyleMaskBorderless = 0,
-			NSWindowStyleMaskTitled = 1 << 0,
-			NSWindowStyleMaskClosable = 1 << 1,
-			NSWindowStyleMaskMiniaturizable = 1 << 2,
-			NSWindowStyleMaskResizable = 1 << 3,
-			NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
-			NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
-			NSWindowStyleMaskFullScreen = 1 << 14,
-			NSWindowStyleMaskFullSizeContentView = 1 << 15,
-			NSWindowStyleMaskUtilityWindow = 1 << 4,
-			NSWindowStyleMaskDocModalWindow = 1 << 6,
-			NSWindowStyleMaskNonactivatingPanel = 1 << 7,
-			NSWindowStyleMaskHUDWindow = 1 << 13
-	};
-
-	NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; // Replaces NSStringPboardType
-
-
-	typedef NS_ENUM(i32, NSDragOperation) {
-		NSDragOperationNone = 0,
-			NSDragOperationCopy = 1,
-			NSDragOperationLink = 2,
-			NSDragOperationGeneric = 4,
-			NSDragOperationPrivate = 8,
-			NSDragOperationMove = 16,
-			NSDragOperationDelete = 32,
-			NSDragOperationEvery = ULONG_MAX,
-
-			//NSDragOperationAll_Obsolete	API_DEPRECATED("", macos(10.0,10.10)) = 15, // Use NSDragOperationEvery
-			//NSDragOperationAll API_DEPRECATED("", macos(10.0,10.10)) = NSDragOperationAll_Obsolete, // Use NSDragOperationEvery
-	};
-
-	void* NSArray_objectAtIndex(NSArray* array, NSUInteger index) {
-		void* func = sel_registerName("objectAtIndex:");
-		return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index);
-	}
-
-	const char** NSPasteboard_readObjectsForClasses(NSPasteboard* pasteboard, Class* classArray, size_t len, void* options) {
-		void* func = sel_registerName("readObjectsForClasses:options:");
-
-		NSArray* array = c_array_to_NSArray(classArray, len);
-
-		NSArray* output = (NSArray*) ((id(*)(id, SEL, id, void*))objc_msgSend)
-			(pasteboard, func, array, options);
-
-		NSRelease(array);
-		NSUInteger count = ((NSUInteger(*)(id, SEL))objc_msgSend)(output, sel_registerName("count"));
-
-		const char** res = si_array_init_reserve(sizeof(const char*), count);
-
-		void* path_func = sel_registerName("path");
-
-		for (NSUInteger i = 0; i < count; i++) {
-			void* url = NSArray_objectAtIndex(output, i);
-			NSString* url_str = ((id(*)(id, SEL))objc_msgSend)(url, path_func);
-			res[i] = NSString_to_char(url_str);
-		}
-
-		return res;
-	}
-
-	void* NSWindow_contentView(NSWindow* window) {
-		void* func = sel_registerName("contentView");
-		return objc_msgSend_id(window, func);
-	}
-
-	/*
-		End of cocoa wrapper
-	*/
-
-	char* RGFW_mouseIconSrc[] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"};
-
-	void* RGFWnsglFramework = NULL;
-
-#ifdef RGFW_OPENGL
-	void* RGFW_getProcAddress(const char* procname) {
-		if (RGFWnsglFramework == NULL)
-			RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
-
-		CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
-
-		void* symbol = CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
-
-		CFRelease(symbolName);
-
-		return symbol;
-	}
-#endif
-
-	CVReturn displayCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) { 
-		RGFW_UNUSED(displayLink) RGFW_UNUSED(inNow) RGFW_UNUSED(inOutputTime) RGFW_UNUSED(flagsIn) RGFW_UNUSED(flagsOut) RGFW_UNUSED(displayLinkContext)
-		return kCVReturnSuccess; 
-	}
-
-	id NSWindow_delegate(RGFW_window* win) {
-		return (id) objc_msgSend_id(win->src.window, sel_registerName("delegate"));
-	}
-
-	u32 RGFW_OnClose(void* self) {
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-
-		win->event.type = RGFW_quit;
-		RGFW_windowQuitCallback(win);
-
-		return true;
-	}
-
-	/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
-	bool acceptsFirstResponder(void) { return true; }
-	bool performKeyEquivalent(NSEvent* event) { RGFW_UNUSED(event); return true; }
-
-	NSDragOperation draggingEntered(id self, SEL sel, id sender) { 
-		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  
-
-		return NSDragOperationCopy; 
-	}
-	NSDragOperation draggingUpdated(id self, SEL sel, id sender) { 
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return 0;
-		
-		if (!(win->_winArgs & RGFW_ALLOW_DND)) {
-			return 0;
-		}
-
-		win->event.type = RGFW_dnd_init;
-		win->src.dndPassed = 0;
-
-		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
-
-		win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
-		RGFW_dndInitCallback(win, win->event.point);
-
-		return NSDragOperationCopy; 
-	}
-	bool prepareForDragOperation(id self) {
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return true;
-		
-		if (!(win->_winArgs & RGFW_ALLOW_DND)) {
-			return false;
-		}
-
-		return true;
-	}
-
-	void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  return; }
-
-	/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */
-	bool performDragOperation(id self, SEL sel, id sender) {
-		RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-
-        if (win == NULL)
-			return false;
-
-		// NSPasteboard* pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
-
-        /////////////////////////////
-        id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
-
-        // Get the types of data available on the pasteboard
-        id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
-
-        // Get the string type for file URLs
-        id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
-
-        // Check if the pasteboard contains file URLs
-        if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
-		    #ifdef RGFW_DEBUG
-            printf("No files found on the pasteboard.\n");
-			#endif
-
-			return 0;
-		}
-
-		id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
-		int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
-
-		if (count == 0)
-			return 0;
-
-		for (int i = 0; i < count; i++) {
-			id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
-			const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
-			strncpy(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH);
-			win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
-		}
-		win->event.droppedFilesCount = count;
-
-		win->event.type = RGFW_dnd;
-		win->src.dndPassed = 0;
-		
-		NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
-		win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
-		
-		RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
-	
-    	return false;
-	}
-
-	static void NSMoveToResourceDir(void) {
-		/* sourced from glfw */
-		char resourcesPath[255];
-
-		CFBundleRef bundle = CFBundleGetMainBundle();
-		if (!bundle)
-			return;
-
-		CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
-		CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
-
-		if (
-			CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
-			CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
-			) {
-			CFRelease(last);
-			CFRelease(resourcesURL);
-			return;
-		}
-
-		CFRelease(last);
-		CFRelease(resourcesURL);
-
-		chdir(resourcesPath);
-	}
-
-
-	NSSize RGFW__osxWindowResize(void* self, SEL sel, NSSize frameSize) {
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return frameSize;
-		
-		win->r.w = frameSize.width;
-		win->r.h = frameSize.height;
-		win->event.type = RGFW_windowResized;
-		RGFW_windowResizeCallback(win, win->r);
-		return frameSize;
-	}
-
-	void RGFW__osxWindowMove(void* self, SEL sel) {
-		RGFW_UNUSED(sel); 
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return;
-		
-		NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)(win->src.window, sel_registerName("frame"));
-		win->r.x = (i32) frame.origin.x;
-		win->r.y = (i32) frame.origin.y;
-
-		win->event.type = RGFW_windowMoved;
-		RGFW_windowMoveCallback(win, win->r);
-	}
-
-	void RGFW__osxUpdateLayer(void* self, SEL sel) {
-		RGFW_UNUSED(sel);
-
-		RGFW_window* win = NULL;
-		object_getInstanceVariable(self, "RGFW_window", (void*)&win);
-		if (win == NULL)
-			return;
-		
-		win->event.type = RGFW_windowRefresh;
-		RGFW_windowRefreshCallback(win);
-	}
-
-	RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-	void RGFW_init_buffer(RGFW_window* win) {
-		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-				RGFW_bufferSize = RGFW_getScreenSize();
-				
-			win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
-
-		#ifdef RGFW_OSMESA
-				win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
-				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-		#endif
-		#else
-		RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
-		#endif
-	}
-
-
-	void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) {
-		objc_msgSend_void_id(win->src.view, sel_registerName("setLayer"), layer);
-	}
-
-	void* RGFW_cocoaGetLayer(void) {
-		return objc_msgSend_class(objc_getClass("CAMetalLayer"), sel_registerName("layer"));
-	}
-
-
-	NSPasteboardType const NSPasteboardTypeURL = "public.url";
-	NSPasteboardType const NSPasteboardTypeFileURL  = "public.file-url";
-
-	RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-		static u8 RGFW_loaded = 0;
-
-		/* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea???
-		Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance).
-		*/
-		si_func_to_SEL_with_name("NSObject", "windowShouldClose", RGFW_OnClose);
-
-		/* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
-		si_func_to_SEL("NSWindow", acceptsFirstResponder);
-		si_func_to_SEL("NSWindow", performKeyEquivalent);
-
-		// RR Create an autorelease pool
-		id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
-		pool = objc_msgSend_id(pool, sel_registerName("init"));
-
-		if (NSApp == NULL) {
-			NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
-
-			((void (*)(id, SEL, NSUInteger))objc_msgSend)
-				(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
-		}
-
-		RGFW_window* win = RGFW_window_basic_init(rect, args);
-		
-		RGFW_window_setMouseDefault(win);
-
-		NSRect windowRect;
-		windowRect.origin.x = win->r.x;
-		windowRect.origin.y = win->r.y;
-		windowRect.size.width = win->r.w;
-		windowRect.size.height = win->r.h;
-
-		NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled;
-
-		if (!(args & RGFW_NO_RESIZE))
-			macArgs |= NSWindowStyleMaskResizable;
-		if (!(args & RGFW_NO_BORDER))
-			macArgs |= NSWindowStyleMaskTitled;
-		else
-			macArgs = NSWindowStyleMaskBorderless;
-		{
-			void* nsclass = objc_getClass("NSWindow");
-			void* func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
-
-			win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
-				(NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false);
-		}
-
-		NSString* str = NSString_stringWithUTF8String(name);
-		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
-
-#ifdef RGFW_EGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			RGFW_createOpenGLContext(win);
-#endif
-
-#ifdef RGFW_OPENGL
-	if ((args & RGFW_NO_INIT_API) == 0) {
-		void* attrs = RGFW_initFormatAttribs(args & RGFW_OPENGL_SOFTWARE);
-		void* format = NSOpenGLPixelFormat_initWithAttributes(attrs);
-
-		if (format == NULL) {
-			printf("Failed to load pixel format for OpenGL\n");
-
-			void* attrs = RGFW_initFormatAttribs(1);
-			format = NSOpenGLPixelFormat_initWithAttributes(attrs);
-			if (format == NULL)
-				printf("and loading software rendering OpenGL failed\n");
-			else
-				printf("Switching to software rendering\n");
-		}
-		
-		/* the pixel format can be passed directly to opengl context creation to create a context 
-			this is because the format also includes information about the opengl version (which may be a bad thing) */
-		win->src.view = NSOpenGLView_initWithFrame((NSRect){{0, 0}, {win->r.w, win->r.h}}, format);
-		objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL"));
-		win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext"));
-	} else
-#endif
-	{
-		NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}};
-		win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend)
-			(NSAlloc((id)objc_getClass("NSView")), sel_registerName("initWithFrame:"),
-				contentRect);
-	}
-
-		void* contentView = NSWindow_contentView(win->src.window);
-		objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true);
-
-		objc_msgSend_void_id(win->src.window, sel_registerName("setContentView:"), win->src.view);
-
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0)
-			objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
-#endif
-		if (args & RGFW_TRANSPARENT_WINDOW) {
-#ifdef RGFW_OPENGL
-		if ((args & RGFW_NO_INIT_API) == 0) {
-			i32 opacity = 0;
-			#define NSOpenGLCPSurfaceOpacity 236
-			NSOpenGLContext_setValues(win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity);
-		}
-#endif
-
-			objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
-
-			objc_msgSend_void_id(win->src.window, sel_registerName("setBackgroundColor:"),
-				NSColor_colorWithSRGB(0, 0, 0, 0));
-		}
-
-		win->src.display = CGMainDisplayID();
-		CVDisplayLinkCreateWithCGDisplay(win->src.display, (CVDisplayLinkRef*)&win->src.displayLink);
-		CVDisplayLinkSetOutputCallback(win->src.displayLink, displayCallback, win);
-		CVDisplayLinkStart(win->src.displayLink);
-
-		RGFW_init_buffer(win);
-
-		#ifndef RGFW_NO_MONITOR
-		if (args & RGFW_SCALE_TO_MONITOR)
-			RGFW_window_scaleToMonitor(win);
-		#endif
-
-		if (args & RGFW_CENTER) {
-			RGFW_area screenR = RGFW_getScreenSize();
-			RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2));
-		}
-
-		if (args & RGFW_HIDE_MOUSE)
-			RGFW_window_showMouse(win, 0);
-
-		if (args & RGFW_COCOA_MOVE_TO_RESOURCE_DIR)
-			NSMoveToResourceDir();
-
-		Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
-
-		class_addIvar(
-			delegateClass, "RGFW_window",
-			sizeof(RGFW_window*), rint(log2(sizeof(RGFW_window*))),
-			"L"
-		);
-
-		class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}");
-		class_addMethod(delegateClass, sel_registerName("updateLayer:"), (IMP) RGFW__osxUpdateLayer, "");
-		class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, "");
-		class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
-		class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
-		class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
-		class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@");
-		class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@");
-
-		id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));
-
-		object_setInstanceVariable(delegate, "RGFW_window", win);
-
-		objc_msgSend_void_id(win->src.window, sel_registerName("setDelegate:"), delegate);
-
-		if (args & RGFW_ALLOW_DND) {
-			win->_winArgs |= RGFW_ALLOW_DND;
-
-			NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
-			NSregisterForDraggedTypes(win->src.window, types, 3);
-		}
-
-		// Show the window
-		objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
-		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
-
-		if (!RGFW_loaded) {
-			objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
-
-			RGFW_loaded = 1;
-		}
-
-		objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
-
-		objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
-
-		if (RGFW_root == NULL)
-			RGFW_root = win;
-
-		NSRetain(win->src.window);
-		NSRetain(NSApp);
-
-		return win;
-	}
-
-	void RGFW_window_setBorder(RGFW_window* win, u8 border) {
-		NSBackingStoreType storeType = NSWindowStyleMaskBorderless;
-		if (!border) {
-			storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
-		}
-		if (!(win->_winArgs & RGFW_NO_RESIZE)) {
-			storeType |= NSWindowStyleMaskResizable;
-		}
-		
-		((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)(win->src.window, sel_registerName("setStyleMask:"), storeType);
-
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setHasShadow:"), border);
-	}
-
-	RGFW_area RGFW_getScreenSize(void) {
-		static CGDirectDisplayID display = 0;
-
-		if (display == 0)
-			display = CGMainDisplayID();
-
-		return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display));
-	}
-
-	RGFW_point RGFW_getGlobalMousePoint(void) {
-		assert(RGFW_root != NULL);
-
-		CGEventRef e = CGEventCreate(NULL);
-		CGPoint point = CGEventGetLocation(e);
-		CFRelease(e);
-
-		return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */
-	}
-
-	RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
-		NSPoint p =  ((NSPoint(*)(id, SEL)) objc_msgSend)(win->src.window, sel_registerName("mouseLocationOutsideOfEventStream"));
-
-		return RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
-	}
-
-	u32 RGFW_keysPressed[10]; /*10 keys at a time*/
-	typedef NS_ENUM(u32, NSEventType) {        /* various types of events */
-		NSEventTypeLeftMouseDown = 1,
-			NSEventTypeLeftMouseUp = 2,
-			NSEventTypeRightMouseDown = 3,
-			NSEventTypeRightMouseUp = 4,
-			NSEventTypeMouseMoved = 5,
-			NSEventTypeLeftMouseDragged = 6,
-			NSEventTypeRightMouseDragged = 7,
-			NSEventTypeMouseEntered = 8,
-			NSEventTypeMouseExited = 9,
-			NSEventTypeKeyDown = 10,
-			NSEventTypeKeyUp = 11,
-			NSEventTypeFlagsChanged = 12,
-			NSEventTypeAppKitDefined = 13,
-			NSEventTypeSystemDefined = 14,
-			NSEventTypeApplicationDefined = 15,
-			NSEventTypePeriodic = 16,
-			NSEventTypeCursorUpdate = 17,
-			NSEventTypeScrollWheel = 22,
-			NSEventTypeTabletPoint = 23,
-			NSEventTypeTabletProximity = 24,
-			NSEventTypeOtherMouseDown = 25,
-			NSEventTypeOtherMouseUp = 26,
-			NSEventTypeOtherMouseDragged = 27,
-			/* The following event types are available on some hardware on 10.5.2 and later */
-			NSEventTypeGesture API_AVAILABLE(macos(10.5)) = 29,
-			NSEventTypeMagnify API_AVAILABLE(macos(10.5)) = 30,
-			NSEventTypeSwipe   API_AVAILABLE(macos(10.5)) = 31,
-			NSEventTypeRotate  API_AVAILABLE(macos(10.5)) = 18,
-			NSEventTypeBeginGesture API_AVAILABLE(macos(10.5)) = 19,
-			NSEventTypeEndGesture API_AVAILABLE(macos(10.5)) = 20,
-
-			NSEventTypeSmartMagnify API_AVAILABLE(macos(10.8)) = 32,
-			NSEventTypeQuickLook API_AVAILABLE(macos(10.8)) = 33,
-
-			NSEventTypePressure API_AVAILABLE(macos(10.10.3)) = 34,
-			NSEventTypeDirectTouch API_AVAILABLE(macos(10.10)) = 37,
-
-			NSEventTypeChangeMode API_AVAILABLE(macos(10.15)) = 38,
-	};
-
-	typedef NS_ENUM(unsigned long long, NSEventMask) { /* masks for the types of events */
-		NSEventMaskLeftMouseDown = 1ULL << NSEventTypeLeftMouseDown,
-			NSEventMaskLeftMouseUp = 1ULL << NSEventTypeLeftMouseUp,
-			NSEventMaskRightMouseDown = 1ULL << NSEventTypeRightMouseDown,
-			NSEventMaskRightMouseUp = 1ULL << NSEventTypeRightMouseUp,
-			NSEventMaskMouseMoved = 1ULL << NSEventTypeMouseMoved,
-			NSEventMaskLeftMouseDragged = 1ULL << NSEventTypeLeftMouseDragged,
-			NSEventMaskRightMouseDragged = 1ULL << NSEventTypeRightMouseDragged,
-			NSEventMaskMouseEntered = 1ULL << NSEventTypeMouseEntered,
-			NSEventMaskMouseExited = 1ULL << NSEventTypeMouseExited,
-			NSEventMaskKeyDown = 1ULL << NSEventTypeKeyDown,
-			NSEventMaskKeyUp = 1ULL << NSEventTypeKeyUp,
-			NSEventMaskFlagsChanged = 1ULL << NSEventTypeFlagsChanged,
-			NSEventMaskAppKitDefined = 1ULL << NSEventTypeAppKitDefined,
-			NSEventMaskSystemDefined = 1ULL << NSEventTypeSystemDefined,
-			NSEventMaskApplicationDefined = 1ULL << NSEventTypeApplicationDefined,
-			NSEventMaskPeriodic = 1ULL << NSEventTypePeriodic,
-			NSEventMaskCursorUpdate = 1ULL << NSEventTypeCursorUpdate,
-			NSEventMaskScrollWheel = 1ULL << NSEventTypeScrollWheel,
-			NSEventMaskTabletPoint = 1ULL << NSEventTypeTabletPoint,
-			NSEventMaskTabletProximity = 1ULL << NSEventTypeTabletProximity,
-			NSEventMaskOtherMouseDown = 1ULL << NSEventTypeOtherMouseDown,
-			NSEventMaskOtherMouseUp = 1ULL << NSEventTypeOtherMouseUp,
-			NSEventMaskOtherMouseDragged = 1ULL << NSEventTypeOtherMouseDragged,
-			/* The following event masks are available on some hardware on 10.5.2 and later */
-			NSEventMaskGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeGesture,
-			NSEventMaskMagnify API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeMagnify,
-			NSEventMaskSwipe API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeSwipe,
-			NSEventMaskRotate API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeRotate,
-			NSEventMaskBeginGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeBeginGesture,
-			NSEventMaskEndGesture API_AVAILABLE(macos(10.5)) = 1ULL << NSEventTypeEndGesture,
-
-			/* Note: You can only use these event masks on 64 bit. In other words, you cannot setup a local, nor global, event monitor for these event types on 32 bit. Also, you cannot search the event queue for them (nextEventMatchingMask:...) on 32 bit.
-			 */
-			NSEventMaskSmartMagnify API_AVAILABLE(macos(10.8)) = 1ULL << NSEventTypeSmartMagnify,
-			NSEventMaskPressure API_AVAILABLE(macos(10.10.3)) = 1ULL << NSEventTypePressure,
-			NSEventMaskDirectTouch API_AVAILABLE(macos(10.12.2)) = 1ULL << NSEventTypeDirectTouch,
-
-			NSEventMaskChangeMode API_AVAILABLE(macos(10.15)) = 1ULL << NSEventTypeChangeMode,
-
-			NSEventMaskAny = ULONG_MAX,
-
-	};
-
-	typedef enum NSEventModifierFlags {
-		NSEventModifierFlagCapsLock = 1 << 16,
-		NSEventModifierFlagShift = 1 << 17,
-		NSEventModifierFlagControl = 1 << 18,
-		NSEventModifierFlagOption = 1 << 19,
-		NSEventModifierFlagCommand = 1 << 20,
-		NSEventModifierFlagNumericPad = 1 << 21
-	} NSEventModifierFlags;
-
-	void RGFW_stopCheckEvents(void) { 
-		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
-        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
-		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
-			(NSApp, sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"), 
-				NSEventTypeApplicationDefined, (NSPoint){0, 0}, 0, 0, 0, NULL, 0, 0, 0);
-
-		((void (*)(id, SEL, id, bool))objc_msgSend)
-			(NSApp, sel_registerName("postEvent:atStart:"), e, 1);
-
-		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-	}
-
-	void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
-		RGFW_UNUSED(win);
-		
-		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
-        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
-		void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
-					(objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
-
-		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend)
-			(NSApp, sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:"), 
-				ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
-
-
-		if (e) {
-			objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
-		}
-
-		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-	}
-
-	RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-		assert(win != NULL);
-		
-		if (win->event.type == RGFW_quit)
-			return NULL;
-		
-		if ((win->event.type == RGFW_dnd || win->event.type == RGFW_dnd_init) && win->src.dndPassed == 0) {
-			win->src.dndPassed = 1;
-			return &win->event;
-		}
-
-		id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
-        eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
-
-		static void* eventFunc = NULL;
-		if (eventFunc == NULL) 
-			eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
-
-		if ((win->event.type == RGFW_windowMoved || win->event.type == RGFW_windowResized || win->event.type == RGFW_windowRefresh) && win->event.keyCode != 120) {
-			win->event.keyCode = 120;
-			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-			return &win->event;
-		}
-
-		void* date = NULL;
-
-		NSEvent* e = (NSEvent*) ((id(*)(id, SEL, NSEventMask, void*, NSString*, bool))objc_msgSend)
-			(NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
-
-		if (e == NULL) {
-			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-			return NULL;
-		}
-		
-		if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) {
-			((void (*)(id, SEL, id, bool))objc_msgSend)
-				(NSApp, sel_registerName("postEvent:atStart:"), e, 0);
-						
-			objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-			return NULL;
-		}
-
-		if (win->event.droppedFilesCount) {
-			u32 i;
-			for (i = 0; i < win->event.droppedFilesCount; i++)
-				win->event.droppedFiles[i][0] = '\0';
-		}
-
-		win->event.droppedFilesCount = 0;
-		win->event.type = 0;
-		
-		switch (objc_msgSend_uint(e, sel_registerName("type"))) {
-			case NSEventTypeMouseEntered: {
-				win->event.type = RGFW_mouseEnter;
-				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
-
-				win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y));
-				RGFW_mouseNotifyCallBack(win, win->event.point, 1);
-				break;
-			}
-			
-			case NSEventTypeMouseExited:
-				win->event.type = RGFW_mouseLeave;
-				RGFW_mouseNotifyCallBack(win, win->event.point, 0);
-				break;
-
-			case NSEventTypeKeyDown: {
-				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
-
-				win->event.type = RGFW_keyPressed;
-				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
-				strncpy(win->event.keyName, str, 16);
-				win->event.repeat = RGFW_isPressed(win, win->event.keyCode);
-				RGFW_keyboard[win->event.keyCode].current = 1;
-
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 1);
-				break;
-			}
-
-			case NSEventTypeKeyUp: {
-				u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
-				win->event.keyCode = RGFW_apiKeyCodeToRGFW(key);;
-
-				RGFW_keyboard[win->event.keyCode].prev = RGFW_keyboard[win->event.keyCode].current;
-
-				win->event.type = RGFW_keyReleased;
-				char* str = (char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("characters")));
-				strncpy(win->event.keyName, str, 16);
-
-				RGFW_keyboard[win->event.keyCode].current = 0;
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, 0);
-				break;
-			}
-
-			case NSEventTypeFlagsChanged: {
-				u32 flags = objc_msgSend_uint(e, sel_registerName("modifierFlags"));
-				RGFW_updateLockState(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255));
-				
-				u8 i;
-				for (i = 0; i < 9; i++)
-					RGFW_keyboard[i + RGFW_CapsLock].prev = 0;
-				
-				for (i = 0; i < 5; i++) {
-					u32 shift = (1 << (i + 16));
-					u32 key = i + RGFW_CapsLock;
-
-					if ((flags & shift) && !RGFW_wasPressed(win, key)) {
-						RGFW_keyboard[key].current = 1;
-
-						if (key != RGFW_CapsLock)
-							RGFW_keyboard[key+ 4].current = 1;
-						
-						win->event.type = RGFW_keyPressed;
-						win->event.keyCode = key;
-						break;
-					} 
-					
-					if (!(flags & shift) && RGFW_wasPressed(win, key)) {
-						RGFW_keyboard[key].current = 0;
-						
-						if (key != RGFW_CapsLock)
-							RGFW_keyboard[key + 4].current = 0;
-
-						win->event.type = RGFW_keyReleased;
-						win->event.keyCode = key;
-						break;
-					}
-				}
-
-				RGFW_keyCallback(win, win->event.keyCode, win->event.keyName, win->event.lockState, win->event.type == RGFW_keyPressed);
-
-				break;
-			}
-			case NSEventTypeLeftMouseDragged:
-			case NSEventTypeOtherMouseDragged:
-			case NSEventTypeRightMouseDragged:
-			case NSEventTypeMouseMoved:
-				win->event.type = RGFW_mousePosChanged;
-				NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
-				win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
-
-				if ((win->_winArgs & RGFW_HOLD_MOUSE)) {
-					p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX"));
-					p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
-					
-					win->event.point = RGFW_POINT((i32)p.x, (i32)p.y);
-				}
-
-				RGFW_mousePosCallback(win, win->event.point);
-				break;
-
-			case NSEventTypeLeftMouseDown:
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeOtherMouseDown:
-				win->event.button = RGFW_mouseMiddle;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeRightMouseDown:
-				win->event.button = RGFW_mouseRight;
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-
-			case NSEventTypeLeftMouseUp:
-				win->event.button = RGFW_mouseLeft;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeOtherMouseUp:
-				win->event.button = RGFW_mouseMiddle;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeRightMouseUp:
-				win->event.button = RGFW_mouseRight;
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 0;
-				win->event.type = RGFW_mouseButtonReleased;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
-				break;
-
-			case NSEventTypeScrollWheel: {
-				double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
-
-				if (deltaY > 0) {
-					win->event.button = RGFW_mouseScrollUp;
-				}
-				else if (deltaY < 0) {
-					win->event.button = RGFW_mouseScrollDown;
-				}
-
-				RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
-				RGFW_mouseButtons[win->event.button].current = 1;
-
-				win->event.scroll = deltaY;
-
-				win->event.type = RGFW_mouseButtonPressed;
-				RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
-				break;
-			}
-
-			default:
-				break;
-		}
-
-		objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
-		((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
-				
-		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
-		return &win->event;
-	}
-
-
-	void RGFW_window_move(RGFW_window* win, RGFW_point v) {
-		assert(win != NULL);
-
-		win->r.x = v.x;
-		win->r.y = v.y;
-		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
-			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
-	}
-
-	void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-		assert(win != NULL);
-
-		win->r.w = a.w;
-		win->r.h = a.h;
-		((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
-			(win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
-	}
-
-	void RGFW_window_minimize(RGFW_window* win) {
-		assert(win != NULL);
-
-		objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
-	}
-
-	void RGFW_window_restore(RGFW_window* win) {
-		assert(win != NULL);
-
-		objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
-	}
-
-	void RGFW_window_setName(RGFW_window* win, char* name) {
-		assert(win != NULL);
-
-		NSString* str = NSString_stringWithUTF8String(name);
-		objc_msgSend_void_id(win->src.window, sel_registerName("setTitle:"), str);
-	}
-
-	#ifndef RGFW_NO_PASSTHROUGH
-	void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
-	}
-	#endif
-
-	void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		((void (*)(id, SEL, NSSize))objc_msgSend)
-			(win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h});
-	}
-
-	void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
-		if (a.w == 0 && a.h == 0)
-			return;
-
-		((void (*)(id, SEL, NSSize))objc_msgSend)
-			(win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h});
-	}
-
-	void RGFW_window_setIcon(RGFW_window* win, u8* data, RGFW_area area, i32 channels) {
-		assert(win != NULL);
-
-		/* code by EimaMei  */
-		// Make a bitmap representation, then copy the loaded image into it.
-		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * channels, 8 * channels);
-		memcpy(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * channels);
-
-		// Add ze representation.
-		void* dock_image = NSImage_initWithSize((NSSize){area.w, area.h});
-		NSImage_addRepresentation(dock_image, (void*) representation);
-
-		// Finally, set the dock image to it.
-		objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
-		// Free the garbage.
-		release(dock_image);
-		release(representation);
-	}
-
-	NSCursor* NSCursor_arrowStr(char* str) {
-		void* nclass = objc_getClass("NSCursor");
-		void* func = sel_registerName(str);
-		return (NSCursor*) objc_msgSend_id(nclass, func);
-	}
-
-	void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) {
-		assert(win != NULL);
-
-		if (image == NULL) {
-			objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
-			return;
-		}
-
-		/* NOTE(EimaMei): Code by yours truly. */
-		// Make a bitmap representation, then copy the loaded image into it.
-		void* representation = NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * channels, 8 * channels);
-		memcpy(NSBitmapImageRep_bitmapData(representation), image, a.w * a.h * channels);
-
-		// Add ze representation.
-		void* cursor_image = NSImage_initWithSize((NSSize){a.w, a.h});
-		NSImage_addRepresentation(cursor_image, representation);
-
-		// Finally, set the cursor image.
-		void* cursor = NSCursor_initWithImage(cursor_image, (NSPoint){0.0, 0.0});
-
-		objc_msgSend_void(cursor, sel_registerName("set"));
-
-		// Free the garbage.
-		release(cursor_image);
-		release(representation);
-	}
-
-	void RGFW_window_setMouseDefault(RGFW_window* win) {
-		RGFW_window_setMouseStandard(win, RGFW_MOUSE_ARROW);
-	}
-
-	void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-		RGFW_UNUSED(win);
-
-		if (show) {
-			CGDisplayShowCursor(kCGDirectMainDisplay);
-		}
-		else {
-			CGDisplayHideCursor(kCGDirectMainDisplay);
-		}
-	}
-
-	void RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) {
-		if (stdMouses > ((sizeof(RGFW_mouseIconSrc)) / (sizeof(char*))))
-			return;
-		
-		char* mouseStr = RGFW_mouseIconSrc[stdMouses];
-		void* mouse = NSCursor_arrowStr(mouseStr);
-
-		if (mouse == NULL)
-			return;
-
-		RGFW_UNUSED(win);
-		CGDisplayShowCursor(kCGDirectMainDisplay);
-		objc_msgSend_void(mouse, sel_registerName("set"));
-	}
-	
-	void RGFW_releaseCursor(RGFW_window* win) {
-		RGFW_UNUSED(win);
-		CGAssociateMouseAndMouseCursorPosition(1);	
-	}
-
-	void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
-		RGFW_UNUSED(win)
-
-		CGWarpMouseCursorPosition(CGPointMake(r.x + (r.w / 2), r.y + (r.h / 2)));
-		CGAssociateMouseAndMouseCursorPosition(0);
-	}
-
-	void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
-		RGFW_UNUSED(win);
-		
-		CGWarpMouseCursorPosition(CGPointMake(v.x, v.y));		
-	}
-
-
-	void RGFW_window_hide(RGFW_window* win) {
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
-	}
-
-	void RGFW_window_show(RGFW_window* win) {
-		((id(*)(id, SEL, SEL))objc_msgSend)(win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
-		objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
-	}
-
-	u8 RGFW_window_isFullscreen(RGFW_window* win) {
-		assert(win != NULL);
-
-		NSWindowStyleMask mask = (NSWindowStyleMask) objc_msgSend_uint(win->src.window, sel_registerName("styleMask"));
-		return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
-	}
-
-	u8 RGFW_window_isHidden(RGFW_window* win) {
-		assert(win != NULL);
-
-		bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
-		return visible == NO && !RGFW_window_isMinimized(win);
-	}
-
-	u8 RGFW_window_isMinimized(RGFW_window* win) {
-		assert(win != NULL);
-
-		return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
-	}
-
-	u8 RGFW_window_isMaximized(RGFW_window* win) {
-		assert(win != NULL);
-
-		return objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
-	}
-
-	static RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display) {
-		RGFW_monitor monitor;
-
-		CGRect bounds = CGDisplayBounds(display);
-		monitor.rect = RGFW_RECT((int) bounds.origin.x, (int) bounds.origin.y, (int) bounds.size.width, (int) bounds.size.height);
-
-		CGSize screenSizeMM = CGDisplayScreenSize(display);
-		monitor.physW = screenSizeMM.width;
-		monitor.physH = screenSizeMM.height;
-
-		monitor.scaleX = ((monitor.rect.w / (screenSizeMM.width / 25.4)) / 96) + 0.25;
-		monitor.scaleY = ((monitor.rect.h / (screenSizeMM.height / 25.4)) / 96) + 0.25;
-
-		return monitor;
-	}
-
-
-	static RGFW_monitor RGFW_monitors[7];
-
-	RGFW_monitor* RGFW_getMonitors(void) {
-		static CGDirectDisplayID displays[7];
-		u32 count;
-
-		if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess)
-			return NULL;
-
-		for (u32 i = 0; i < count; i++)
-			RGFW_monitors[i] = RGFW_NSCreateMonitor(displays[i]);
-
-		return RGFW_monitors;
-	}
-
-	RGFW_monitor RGFW_getPrimaryMonitor(void) {
-		CGDirectDisplayID primary = CGMainDisplayID();
-		return RGFW_NSCreateMonitor(primary);
-	}
-
-	RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
-		return RGFW_NSCreateMonitor(win->src.display);
-	}
-
-	char* RGFW_readClipboard(size_t* size) {
-		char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString);
-		
-		size_t clip_len = 1;
-
-		if (clip != NULL) {
-			clip_len = strlen(clip) + 1; 
-		}
-
-		char* str = (char*)RGFW_MALLOC(sizeof(char) * clip_len);
-		
-		if (clip != NULL) {
-			strncpy(str, clip, clip_len);
-		}
-
-		str[clip_len] = '\0';
-		
-		if (size != NULL)
-			*size = clip_len;
-		return str;
-	}
-
-	void RGFW_writeClipboard(const char* text, u32 textLen) {
-		RGFW_UNUSED(textLen);
-
-		NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
-		NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
-
-		NSPasteBoard_setString(NSPasteboard_generalPasteboard(), text, NSPasteboardTypeString);
-	}
-
-	u16 RGFW_registerJoystick(RGFW_window* win, i32 jsNumber) {
-		RGFW_UNUSED(jsNumber);
-
-		assert(win != NULL);
-
-		return RGFW_registerJoystickF(win, (char*) "");
-	}
-
-	u16 RGFW_registerJoystickF(RGFW_window* win, char* file) {
-		RGFW_UNUSED(file);
-
-		assert(win != NULL);
-
-		return RGFW_joystickCount - 1;
-	}
-
-	#ifdef RGFW_OPENGL
-	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-		assert(win != NULL);
-		objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
-	}
-	#endif
-
-	#if !defined(RGFW_EGL)
-	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
-		assert(win != NULL);
-		#if defined(RGFW_OPENGL)
-		
-		NSOpenGLContext_setValues(win->src.ctx, &swapInterval, 222);
-		#else
-		RGFW_UNUSED(swapInterval);
-		#endif
-	}
-	#endif
-	
-	// Function to create a CGImageRef from an array of bytes
-	CGImageRef createImageFromBytes(unsigned char *buffer, int width, int height)
-	{
-		// Define color space
-        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
-        // Create bitmap context
-        CGContextRef context = CGBitmapContextCreate(
-        		buffer, 
-        		width, height,
-        		8,
-        		RGFW_bufferSize.w * 4, 
-        		colorSpace,
-        		kCGImageAlphaPremultipliedLast);
-        // Create image from bitmap context
-        CGImageRef image = CGBitmapContextCreateImage(context);
-        // Release the color space and context
-        CGColorSpaceRelease(colorSpace);
-        CGContextRelease(context);
-                         
-        return image;
-    }
-
-	void RGFW_window_swapBuffers(RGFW_window* win) {
-		assert(win != NULL);
-		/* clear the window*/
-
-		if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {
-#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-			#ifdef RGFW_OSMESA
-			RGFW_OSMesa_reorganize();
-			#endif
-
-			void* view = NSWindow_contentView(win->src.window);
-			void* layer = objc_msgSend_id(view, sel_registerName("layer"));
-
-			((void(*)(id, SEL, NSRect))objc_msgSend)(layer,
-				sel_registerName("setFrame:"),
-				(NSRect){{0, 0}, {win->r.w, win->r.h}});
-
-            CGImageRef image = createImageFromBytes(win->buffer, win->r.w, win->r.h);
-            // Get the current graphics context
-            id graphicsContext = objc_msgSend_class(objc_getClass("NSGraphicsContext"), sel_registerName("currentContext"));
-            // Get the CGContext from the current NSGraphicsContext
-            id cgContext = objc_msgSend_id(graphicsContext, sel_registerName("graphicsPort"));
-			// Draw the image in the context
-			NSRect bounds = (NSRect){{0,0}, {win->r.w, win->r.h}};
-		    CGContextDrawImage((void*)cgContext, *(CGRect*)&bounds, image);
-          	// Flush the graphics context to ensure the drawing is displayed
-            objc_msgSend_id(graphicsContext, sel_registerName("flushGraphics"));
-            
-            objc_msgSend_void_id(layer, sel_registerName("setContents:"), (id)image);
-            objc_msgSend_id(layer, sel_registerName("setNeedsDisplay"));
-            
-            CGImageRelease(image);
-#endif
-		}
-
-		if (!(win->_winArgs & RGFW_NO_GPU_RENDER)) {
-			#ifdef RGFW_EGL
-					eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
-			#elif defined(RGFW_OPENGL)
-					objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer"));
-			#endif
-		}
-	}
-
-	void RGFW_window_close(RGFW_window* win) {
-		assert(win != NULL);
-		release(win->src.view);
-
-#ifdef RGFW_ALLOC_DROPFILES
-		{
-			u32 i;
-			for (i = 0; i < RGFW_MAX_DROPS; i++)
-				RGFW_FREE(win->event.droppedFiles[i]);
-
-
-			RGFW_FREE(win->event.droppedFiles);
-		}
-#endif
-	
-#ifdef RGFW_BUFFER
-		release(win->src.bitmap);
-		release(win->src.image);
-#endif
-
-		CVDisplayLinkStop(win->src.displayLink);
-		CVDisplayLinkRelease(win->src.displayLink);
-
-		RGFW_FREE(win);
-	}
-
-	u64 RGFW_getTimeNS(void) {
-		static mach_timebase_info_data_t timebase_info;
-		if (timebase_info.denom == 0) {
-			mach_timebase_info(&timebase_info);
-		}
-		return mach_absolute_time() * timebase_info.numer / timebase_info.denom;
-	}
-
-	u64 RGFW_getTime(void) {
-		static mach_timebase_info_data_t timebase_info;
-		if (timebase_info.denom == 0) {
-			mach_timebase_info(&timebase_info);
-		}
-		return (double) mach_absolute_time() * (double) timebase_info.numer / ((double) timebase_info.denom * 1e9);
-	}
-#endif /* RGFW_MACOS */
-
-/*
-	End of MaOS defines
-*/
-
-/*
-	WEBASM defines
-*/
-
-#ifdef RGFW_WEBASM
-RGFW_Event RGFW_events[20];
-size_t RGFW_eventLen = 0;
-
-EM_BOOL Emscripten_on_keydown(int eventType, const EmscriptenKeyboardEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-	
-	RGFW_events[RGFW_eventLen].type = RGFW_keyPressed;
-	memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16);
-	RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode);
-	RGFW_events[RGFW_eventLen].lockState = 0;
-	RGFW_eventLen++;
-
-	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current;
-	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 1;
-	RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 1);
-	
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_keyup(int eventType, const EmscriptenKeyboardEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_keyReleased;
-	memcpy(RGFW_events[RGFW_eventLen].keyName, e->key, 16);
-	RGFW_events[RGFW_eventLen].keyCode = RGFW_apiKeyCodeToRGFW(e->keyCode);
-	RGFW_events[RGFW_eventLen].lockState = 0;
-	RGFW_eventLen++;
-
-	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].prev = RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current;
-	RGFW_keyboard[RGFW_apiKeyCodeToRGFW(e->keyCode)].current = 0;
-
-	RGFW_keyCallback(RGFW_root, RGFW_apiKeyCodeToRGFW(e->keyCode), RGFW_events[RGFW_eventLen].keyName, 0, 0);
-
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_windowResized;
-	RGFW_eventLen++;
-
-	RGFW_windowResizeCallback(RGFW_root, RGFW_RECT(0, 0, e->windowInnerWidth, e->windowInnerHeight));
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_windowResized;
-	RGFW_eventLen++;
-
-	RGFW_root->r = RGFW_RECT(0, 0, e->elementWidth, e->elementHeight);
-	RGFW_windowResizeCallback(RGFW_root, RGFW_root->r);
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_focusIn;
-	RGFW_eventLen++;
-
-	RGFW_root->event.inFocus = 1;
-	RGFW_focusCallback(RGFW_root, 1);
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_focusOut;
-	RGFW_eventLen++;
-
-	RGFW_root->event.inFocus = 0;
-	RGFW_focusCallback(RGFW_root, 0);
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged;
-
-	if ((RGFW_root->_winArgs & RGFW_HOLD_MOUSE)) {
-		RGFW_point p = RGFW_POINT(e->movementX, e->movementY);
-		RGFW_events[RGFW_eventLen].point = p;
-	}
-	else
-		RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
-	RGFW_eventLen++;
-	
-	RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
-	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
-	RGFW_events[RGFW_eventLen].button = e->button + 1; 
-	RGFW_events[RGFW_eventLen].scroll = 0;
-
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
-
-	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
-	RGFW_eventLen++;
-	
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased;
-	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->targetX, e->targetY);
-	RGFW_events[RGFW_eventLen].button = e->button + 1; 
-	RGFW_events[RGFW_eventLen].scroll = 0;
-
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0;
-
-	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0);
-	RGFW_eventLen++;
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
-	RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->mouse.targetX, e->mouse.targetY);
-	RGFW_events[RGFW_eventLen].button = RGFW_mouseScrollUp + (e->deltaY < 0); 
-	RGFW_events[RGFW_eventLen].scroll = e->deltaY;
-
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
-	RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
-
-	RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
-	RGFW_eventLen++;
-
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* e, void* userData) { 
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-    size_t i; 
-    for (i = 0; i < (size_t)e->numTouches; i++) { 
-	    RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonPressed;
-	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
-	    RGFW_events[RGFW_eventLen].button = 1; 
-	    RGFW_events[RGFW_eventLen].scroll = 0;
-
-
-	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
-	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 1;
-
-        RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
-
-	    RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 1);
-    	RGFW_eventLen++;
-    }
-
-	return EM_TRUE;
-}
-EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* e, void* userData) { 
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-    
-    size_t i; 
-    for (i = 0; i < (size_t)e->numTouches; i++) { 
-   	    RGFW_events[RGFW_eventLen].type = RGFW_mousePosChanged;
-	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
-
-        RGFW_mousePosCallback(RGFW_root, RGFW_events[RGFW_eventLen].point);
-	    RGFW_eventLen++;
-    }
-    return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* e, void* userData) {
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-	
-    size_t i; 
-    for (i = 0; i < (size_t)e->numTouches; i++) { 
-	    RGFW_events[RGFW_eventLen].type = RGFW_mouseButtonReleased;
-	    RGFW_events[RGFW_eventLen].point = RGFW_POINT(e->touches[i].targetX, e->touches[i].targetY);
-	    RGFW_events[RGFW_eventLen].button = 1; 
-	    RGFW_events[RGFW_eventLen].scroll = 0;
-
-	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].prev = RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current;	
-	    RGFW_mouseButtons[RGFW_events[RGFW_eventLen].button].current = 0;
-        
-	    RGFW_mouseButtonCallback(RGFW_root, RGFW_events[RGFW_eventLen].button, RGFW_events[RGFW_eventLen].scroll, 0);
-	    RGFW_eventLen++;
-    }
-	return EM_TRUE;
-}
-
-EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* e, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(e); return EM_TRUE; }
-
-EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) {	
-	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
-
-	if (gamepadEvent->index >= 4)
-		return 0;
-
-	RGFW_joysticks[gamepadEvent->index] = gamepadEvent->connected;
-
-    return 1; // The event was consumed by the callback handler
-}
-
-void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) {
-	if (!(RGFW_root->_winArgs & RGFW_ALLOW_DND))
-		return;
-
-	RGFW_events[RGFW_eventLen].droppedFilesCount = count;	
-	RGFW_dndCallback(RGFW_root, RGFW_events[RGFW_eventLen].droppedFiles, count);
-	RGFW_eventLen++;
-}
-
-b8 RGFW_stopCheckEvents_bool = RGFW_FALSE;
-void RGFW_stopCheckEvents(void) { 
-	RGFW_stopCheckEvents_bool = RGFW_TRUE;
-}
-
-void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
-	RGFW_UNUSED(win);
-
-	if (waitMS == 0)
-		return;
-	
-	u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6);
-
-	while ((RGFW_eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && 
-		(waitMS < 0 || (RGFW_getTimeNS() / 1e+6) - start < waitMS)
-	) {
-		emscripten_sleep(0);
-	}
-	
-	RGFW_stopCheckEvents_bool = RGFW_FALSE;
-}
-
-RGFWDEF void RGFW_init_buffer(RGFW_window* win);
-void RGFW_init_buffer(RGFW_window* win) {
-	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
-		if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0)
-			RGFW_bufferSize = RGFW_getScreenSize();
-		
-		win->buffer = RGFW_MALLOC(RGFW_bufferSize.w * RGFW_bufferSize.h * 4);
-	#ifdef RGFW_OSMESA
-			win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
-			OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, win->r.w, win->r.h);
-	#endif
-	#else
-	RGFW_UNUSED(win); /*!< if buffer rendering is not being used */
-	#endif
-}
-
-void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) { 
-	/* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */
-	/* TODO: find a better way to do this, 
-		strcpy doesn't seem to work, maybe because of asyncio
-	*/
-
-	RGFW_events[RGFW_eventLen].type = RGFW_dnd;
-	char** arr = (char**)&RGFW_events[RGFW_eventLen].droppedFiles[index];
-	*arr = file;
-}
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-
-void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
-
-void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
-    FILE* file = fopen(path, "w+");
-	if (file == NULL)
-		return;
-
-    fwrite(data, sizeof(char), len, file);
-    fclose(file);
-}
-
-RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, u16 args) {
-	RGFW_UNUSED(name) 
-
-	RGFW_UNUSED(RGFW_initFormatAttribs);
-	
-    RGFW_window* win = RGFW_window_basic_init(rect, args);
-
-    EmscriptenWebGLContextAttributes attrs;
-    attrs.alpha = EM_TRUE;
-    attrs.depth = EM_TRUE;
-	attrs.alpha = EM_TRUE;
-    attrs.stencil = RGFW_STENCIL;
-    attrs.antialias = RGFW_SAMPLES;
-    attrs.premultipliedAlpha = EM_TRUE;
-    attrs.preserveDrawingBuffer = EM_FALSE;
-	
-    if (RGFW_DOUBLE_BUFFER == 0)
-        attrs.renderViaOffscreenBackBuffer = 0;
-    else
-        attrs.renderViaOffscreenBackBuffer = RGFW_AUX_BUFFERS;
-    
-    attrs.failIfMajorPerformanceCaveat = EM_FALSE;
-	attrs.majorVersion = (RGFW_majorVersion == 0) ? 1 : RGFW_majorVersion;
-	attrs.minorVersion = RGFW_minorVersion;
-	
-    attrs.enableExtensionsByDefault = EM_TRUE;
-    attrs.explicitSwapControl = EM_TRUE;
-
-    emscripten_webgl_init_context_attributes(&attrs);
-    win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs);
-    emscripten_webgl_make_context_current(win->src.ctx);
-
-	#ifdef LEGACY_GL_EMULATION
-	EM_ASM("Module.useWebGL = true; GLImmediate.init();");	
-	#endif
-
-	emscripten_set_canvas_element_size("#canvas", rect.w, rect.h);
-	emscripten_set_window_title(name);
-
-	/* load callbacks */
-    emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keydown);
-    emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_keyup);
-    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
-    emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
-    emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
-    emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
-    emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
-    emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
-    emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
-    emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
-    emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
-    emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
-    emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
-    emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
-	emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad);
-	emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad);
-	
-	if (args & RGFW_ALLOW_DND)  {
-		win->_winArgs |= RGFW_ALLOW_DND;
-	}
-
-    EM_ASM({
-		var canvas = document.getElementById('canvas');
-        canvas.addEventListener('drop', function(e) {
-            e.preventDefault();
-            if (e.dataTransfer.file < 0)
-				return;
-
-			var filenamesArray = [];
-			var count = e.dataTransfer.files.length;
-
-			/* Read and save the files to emscripten's files */
-			var drop_dir = '.rgfw_dropped_files';
-			Module._RGFW_mkdir(drop_dir);
-
-			for (var i = 0; i < count; i++) {
-				var file = e.dataTransfer.files[i];
-
-				var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
-				var reader = new FileReader();
-				
-				reader.onloadend = (e) => {
-					if (reader.readyState != 2) {
-						out('failed to read dropped file: '+file.name+': '+reader.error);
-					}
-					else {
-						var data = e.target.result;
-						
-						_RGFW_writeFile(path, new Uint8Array(data), file.size);
-					}
-				};
-
-				reader.readAsArrayBuffer(file);		
-				// This works weird on modern opengl
-				var filename = stringToNewUTF8(path);
-
-				filenamesArray.push(filename);
-				
-				Module._RGFW_makeSetValue(i, filename);
-			}
-			
-			Module._Emscripten_onDrop(count);
-			
-			for (var i = 0; i < count; ++i) {
-				_free(filenamesArray[i]);
-			}
-        }, true);
-
-        canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
-    });
-
-	RGFW_init_buffer(win);
-	glViewport(0, 0, rect.w, rect.h);
-	
-	RGFW_root = win; 
-
-	if (args & RGFW_HIDE_MOUSE) {
-		RGFW_window_showMouse(win, 0);
-	}
-
-	if (args & RGFW_FULLSCREEN) {
-		RGFW_window_resize(win, RGFW_getScreenSize());
-	}
-
-    return win;
-}
-
-RGFW_Event* RGFW_window_checkEvent(RGFW_window* win) {
-	static u8 index = 0;
-	
-	if (index == 0) 
-		RGFW_resetKey();
-
-	/* check gamepads */
-    for (int i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) {
-		if (RGFW_joysticks[i] == 0)
-			continue;;
-		
-        EmscriptenGamepadEvent gamepadState;
-
-        if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS)
-			break;
-
-		// Register buttons data for every connected gamepad
-		for (int j = 0; (j < gamepadState.numButtons) && (j < 16); j++) {
-			u32 map[] = {
-				RGFW_JS_A, RGFW_JS_X, RGFW_JS_B, RGFW_JS_Y,
-				RGFW_JS_L1, RGFW_JS_R1, RGFW_JS_L2, RGFW_JS_R2,
-				RGFW_JS_SELECT, RGFW_JS_START,
-				0, 0,
-				RGFW_JS_UP, RGFW_JS_DOWN, RGFW_JS_LEFT, RGFW_JS_RIGHT
-			};
-
-			u32 button = map[j]; 
-			if (RGFW_jsPressed[i][button] != gamepadState.digitalButton[j]) {
-				win->event.type = RGFW_jsButtonPressed;
-				win->event.joystick = i;
-				win->event.button = map[j];
-				return &win->event;
-			}
-
-			RGFW_jsPressed[i][button] = gamepadState.digitalButton[j];
-		}
-
-		for (int j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) {
-			win->event.axisesCount = gamepadState.numAxes;
-			if (win->event.axis[j].x != gamepadState.axis[j] || 
-				win->event.axis[j].y != gamepadState.axis[j + 1]
-			) {
-				win->event.axis[j].x = gamepadState.axis[j];
-				win->event.axis[j].y = gamepadState.axis[j + 1];
-				win->event.type = RGFW_jsAxisMove;
-				win->event.joystick = i;
-				return &win->event;
-			}
-		}
-    }
-
-	/* check queued events */
-	if (RGFW_eventLen == 0)
-		return NULL;
-	
-	RGFW_events[index].frameTime = win->event.frameTime;
-	RGFW_events[index].frameTime2 = win->event.frameTime2;
-	RGFW_events[index].inFocus = win->event.inFocus;
-
-	win->event = RGFW_events[index];
-
-	RGFW_eventLen--;
-
-	if (RGFW_eventLen)
-		index++;
-	else
-		index = 0;
-
-	return &win->event;
-}
-
-void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
-	RGFW_UNUSED(win)
-	emscripten_set_canvas_element_size("#canvas", a.w, a.h);
-}
-
-/* NOTE: I don't know if this is possible */
-void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
-/* this one might be possible but it looks iffy */
-void RGFW_window_setMouse(RGFW_window* win, u8* image, RGFW_area a, i32 channels) { RGFW_UNUSED(win); RGFW_UNUSED(channels) RGFW_UNUSED(a) RGFW_UNUSED(image) }
-
-const char RGFW_CURSORS[11][12] = {
-    "default",
-    "default",
-    "text",
-    "crosshair",
-    "pointer",
-    "ew-resize",
-    "ns-resize",
-    "nwse-resize",
-    "nesw-resize",
-    "move",
-    "not-allowed"
-};
-
-void RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
-	RGFW_UNUSED(win)
-	EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, RGFW_CURSORS[mouse]);
-}
-
-void RGFW_window_setMouseDefault(RGFW_window* win) {
-	RGFW_window_setMouseStandard(win, RGFW_MOUSE_NORMAL);
-}
-
-void RGFW_window_showMouse(RGFW_window* win, i8 show) {
-	if (show)
-		RGFW_window_setMouseDefault(win);
-	else
-		EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
-}
-
-RGFW_point RGFW_getGlobalMousePoint(void) {
-    RGFW_point point;
-    point.x = EM_ASM_INT({
-        return window.mouseX || 0;
-    });
-    point.y = EM_ASM_INT({
-        return window.mouseY || 0;
-    });
-    return point;
-}
-
-RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
-	RGFW_UNUSED(win);
-	
-	EmscriptenMouseEvent mouseEvent;
-    emscripten_get_mouse_status(&mouseEvent);
-	return RGFW_POINT( mouseEvent.targetX,  mouseEvent.targetY);
-}
-
-void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) {
-	RGFW_UNUSED(win);
-
-    EM_ASM_({
-        var canvas = document.getElementById('canvas');
-        if ($0) {
-            canvas.style.pointerEvents = 'none';
-        } else {
-            canvas.style.pointerEvents = 'auto';
-        }
-    }, passthrough);
-}
-
-void RGFW_writeClipboard(const char* text, u32 textLen) {
-	RGFW_UNUSED(textLen)
-	EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
-}
-
-
-char* RGFW_readClipboard(size_t* size) {
-	/*
-		placeholder code for later
-		I'm not sure if this is possible do the the async stuff
-	*/
-	
-	if (size != NULL)
-		*size = 0;
-	
-	char* str = (char*)malloc(1);
-	str[0] = '\0';
-
-	return str;
-}
-
-void RGFW_window_swapBuffers(RGFW_window* win) {
-	RGFW_UNUSED(win);
-	
-	#ifdef RGFW_BUFFER
-	if (!(win->_winArgs & RGFW_NO_CPU_RENDER)) {		
-		glEnable(GL_TEXTURE_2D);
-
-		GLuint texture;
-		glGenTextures(1,&texture);
-
-		glBindTexture(GL_TEXTURE_2D,texture);
-		
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
-		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, RGFW_bufferSize.w, RGFW_bufferSize.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, win->buffer);
-		
-		float ratioX = ((float)win->r.w / (float)RGFW_bufferSize.w);
-		float ratioY = ((float)win->r.h / (float)RGFW_bufferSize.h);
-
-		// Set up the viewport
-		glClear(GL_COLOR_BUFFER_BIT);
-
-		glBegin(GL_TRIANGLES);
-			glTexCoord2f(0, ratioY); glColor3f(1, 1, 1); glVertex2f(-1, -1);
-			glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1);
-			glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1);
-
-			glTexCoord2f(ratioX, 0); glColor3f(1, 1, 1); glVertex2f(1, 1);
-			glTexCoord2f(ratioX, ratioY); glColor3f(1, 1, 1); glVertex2f(1, -1);
-			glTexCoord2f(0, 0); glColor3f(1, 1, 1); glVertex2f(-1, 1);
-		glEnd();
-
-		glDeleteTextures(1, &texture);
-	}
-	#endif
-
-	emscripten_webgl_commit_frame();
-	emscripten_sleep(0);
-}
-
-
-void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
-	if (win == NULL)
-	    emscripten_webgl_make_context_current(0);
-	else
-	    emscripten_webgl_make_context_current(win->src.ctx);
-}
-
-#ifndef RGFW_EGL
-void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
-#endif
-
-void RGFW_window_close(RGFW_window* win) {
-    emscripten_webgl_destroy_context(win->src.ctx);
-
-    free(win);
-}
-
-int RGFW_innerWidth(void) {   return EM_ASM_INT({ return window.innerWidth; });  }
-int RGFW_innerHeight(void) {  return EM_ASM_INT({ return window.innerHeight; });  }
-
-RGFW_area RGFW_getScreenSize(void) {
-	return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight());
-}
-
-void* RGFW_getProcAddress(const char* procname) { 
-	return emscripten_webgl_get_proc_address(procname);
-}
-
-void RGFW_sleep(u64 milisecond) {
-	emscripten_sleep(milisecond);
-}
-
-u64 RGFW_getTimeNS(void) {
-	return emscripten_get_now() * 1e+6;
-}
-
-u64 RGFW_getTime(void) {
-	return emscripten_get_now() * 1000;
-}
-
-void RGFW_releaseCursor(RGFW_window* win) {
-	RGFW_UNUSED(win);
-	emscripten_exit_pointerlock();
-}
-
-void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { 
-	RGFW_UNUSED(win); RGFW_UNUSED(r);
-
-	emscripten_request_pointerlock("#canvas", 1);
-}
-
-
-void RGFW_window_setName(RGFW_window* win, char* name) {
-	RGFW_UNUSED(win);
-	emscripten_set_window_title(name);
-}
-
-/* unsupported functions */
-RGFW_monitor* RGFW_getMonitors(void) { return NULL; }
-RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; }
-void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win) RGFW_UNUSED(v) }
-void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a)  }
-void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win) RGFW_UNUSED(a)  }
-void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win)}
-void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win) }
-void RGFW_window_setBorder(RGFW_window* win, b8 border) { RGFW_UNUSED(win) RGFW_UNUSED(border)  }
-void RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(win) RGFW_UNUSED(icon) RGFW_UNUSED(a) RGFW_UNUSED(channels)  }
-void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win) }
-void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win) }
-b8 RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
-b8 RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
-b8 RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win) return 0; }
-RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win) return (RGFW_monitor){}; }
-
-#endif
-
-/* end of web asm defines */
-
-/* unix (macOS, linux, web asm) only stuff */
-#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WEBASM)  || defined(RGFW_WAYLAND)
-/* unix threading */
-#ifndef RGFW_NO_THREADS
-#include <pthread.h>
-
-	RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) {
-		RGFW_UNUSED(args);
-		
-		RGFW_thread t;
-		pthread_create((pthread_t*) &t, NULL, *ptr, NULL);
-		return t;
-	}
-	void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); }
-	void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); }
-#ifdef __linux__
-	void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); }
-#endif
-#endif
-
-#ifndef RGFW_WEBASM
-/* unix sleep */
-	void RGFW_sleep(u64 ms) {
-		struct timespec time;
-		time.tv_sec = 0;
-		time.tv_nsec = ms * 1e+6;
-
-		nanosleep(&time, NULL);
-	}
-#endif
-
-#endif /* end of unix / mac stuff*/
-#endif /*RGFW_IMPLEMENTATION*/
-
-#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
-}
-#endif
+/*
+*
+*	RGFW 1.7.5-dev
+
+* Copyright (C) 2022-25 ColleagueRiley
+*
+* libpng license
+*
+* This software is provided 'as-is', without any express or implied
+* warranty.  In no event will the authors be held liable for any damages
+* arising from the use of this software.
+
+* Permission is granted to anyone to use this software for any purpose,
+* including commercial applications, and to alter it and redistribute it
+* freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not
+*    claim that you wrote the original software. If you use this software
+*    in a product, an acknowledgment in the product documentation would be
+*    appreciated but is not required.
+* 2. Altered source versions must be plainly marked as such, and must not be
+*    misrepresented as being the original software.
+* 3. This notice may not be removed or altered from any source distribution.
+*
+*
+*/
+
+/*
+	(MAKE SURE RGFW_IMPLEMENTATION is in exactly one header or you use -D RGFW_IMPLEMENTATION)
+	#define RGFW_IMPLEMENTATION - makes it so source code is included with header
+*/
+
+/*
+	#define RGFW_IMPLEMENTATION - (required) makes it so the source code is included
+	#define RGFW_DEBUG - (optional) makes it so RGFW prints debug messages and errors when they're found
+	#define RGFW_OSMESA - (optional) use OSmesa as backend (instead of system's opengl api + regular opengl)
+	#define RGFW_BUFFER - (optional) draw directly to (RGFW) window pixel buffer that is drawn to screen (the buffer is in the RGBA format)
+	#define RGFW_EGL - (optional) use EGL for loading an OpenGL context (instead of the system's opengl api)
+	#define RGFW_OPENGL_ES1 - (optional) use EGL to load and use Opengl ES (version 1) for backend rendering (instead of the system's opengl api)
+									This version doesn't work for desktops (I'm pretty sure)
+	#define RGFW_OPENGL_ES2 - (optional) use OpenGL ES (version 2)
+	#define RGFW_OPENGL_ES3 - (optional) use OpenGL ES (version 3)
+	#define RGFW_DIRECTX - (optional) include integration directX functions (windows only)
+	#define RGFW_VULKAN - (optional) include helpful vulkan integration functions and macros
+	#define RGFW_WEBGPU - (optional) use webGPU for rendering (Web ONLY)
+	#define RGFW_NO_API - (optional) don't use any rendering API (no opengl, no vulkan, no directX)
+
+	#define RGFW_LINK_EGL (optional) (windows only) if EGL is being used, if EGL functions should be defined dymanically (using GetProcAddress)
+	#define RGFW_X11 (optional) (unix only) if X11 should be used. This option is turned on by default by unix systems except for MacOS
+	#define RGFW_WAYLAND (optional) (unix only) use Wayland. (This can be used with X11)
+	#define RGFW_NO_X11 (optional) (unix only) don't fallback to X11 when using Wayland
+	#define RGFW_NO_LOAD_WGL (optional) (windows only) if WGL should be loaded dynamically during runtime
+	#define RGFW_NO_X11_CURSOR (optional) (unix only) don't use XCursor
+	#define RGFW_NO_X11_CURSOR_PRELOAD (optional) (unix only) use XCursor, but don't link it in code, (you'll have to link it with -lXcursor)
+	#define RGFW_NO_X11_EXT_PRELOAD (optional) (unix only) use Xext, but don't link it in code, (you'll have to link it with -lXext)
+    #define RGFW_NO_LOAD_WINMM (optional) (windows only) use winmm (timeBeginPeriod), but don't link it in code, (you'll have to link it with -lwinmm)
+	#define RGFW_NO_WINMM (optional) (windows only) don't use winmm
+	#define RGFW_NO_IOKIT (optional) (macOS) don't use IOKit
+	#define RGFW_NO_UNIX_CLOCK (optional) (unix) don't link unix clock functions
+	#define RGFW_NO_DWM (windows only) - do not use or link dwmapi
+	#define RGFW_USE_XDL (optional) (X11) if XDL (XLib Dynamic Loader) should be used to load X11 dynamically during runtime (must include XDL.h along with RGFW)
+	#define RGFW_COCOA_GRAPHICS_SWITCHING - (optional) (cocoa) use automatic graphics switching (allow the system to choose to use GPU or iGPU)
+	#define RGFW_COCOA_FRAME_NAME (optional) (cocoa) set frame name
+	#define RGFW_NO_DPI - do not calculate DPI (no XRM nor libShcore included)
+	#define RGFW_BUFFER_BGR - use the BGR format for bufffers instead of RGB, saves processing time
+    #define RGFW_ADVANCED_SMOOTH_RESIZE - use advanced methods for smooth resizing (may result in a spike in memory usage or worse performance) (eg. WM_TIMER and XSyncValue)
+
+	#define RGFW_ALLOC x  - choose the default allocation function (defaults to standard malloc)
+	#define RGFW_FREE x  - choose the default deallocation function (defaults to standard free)
+	#define RGFW_USERPTR x - choose the default userptr sent to the malloc call, (NULL by default)
+
+	#define RGFW_EXPORT - use when building RGFW
+	#define RGFW_IMPORT - use when linking with RGFW (not as a single-header)
+
+	#define RGFW_USE_INT - force the use c-types rather than stdint.h (for systems that might not have stdint.h (msvc))
+	#define RGFW_bool x - choose what type to use for bool, by default u32 is used
+*/
+
+/*
+Example to get you started :
+
+linux : gcc main.c -lX11 -lXrandr -lGL
+windows : gcc main.c -lopengl32 -lgdi32
+macos : gcc main.c -framework Cocoa -framework CoreVideo -framework OpenGL -framework IOKit
+
+#define RGFW_IMPLEMENTATION
+#include "RGFW.h"
+
+u8 icon[4 * 3 * 3] = {0xFF, 0x00, 0x00, 0xFF,    0xFF, 0x00, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF,   0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,     0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF};
+
+int main() {
+	RGFW_window* win = RGFW_createWindow("name", RGFW_RECT(100, 100, 500, 500), (u64)0);
+
+	RGFW_window_setIcon(win, icon, RGFW_AREA(3, 3), 4);
+
+	while (RGFW_window_shouldClose(win) == RGFW_FALSE) {
+		while (RGFW_window_checkEvent(win)) {
+		    if (win->event.type == RGFW_quit || RGFW_isPressed(win, RGFW_escape))
+			    break;
+        }
+
+		RGFW_window_swapBuffers(win);
+
+		glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+		glClear(GL_COLOR_BUFFER_BIT);
+	}
+
+	RGFW_window_close(win);
+}
+
+	compiling :
+
+	if you wish to compile the library all you have to do is create a new file with this in it
+
+	rgfw.c
+	#define RGFW_IMPLEMENTATION
+	#include "RGFW.h"
+
+	You may also want to add
+	`#define RGFW_EXPORT` when compiling and
+	`#define RGFW_IMPORT`when linking RGFW on it's own:
+	this reduces inline functions and prevents bloat in the object file
+
+	then you can use gcc (or whatever compile you wish to use) to compile the library into object file
+
+	ex. gcc -c RGFW.c -fPIC
+
+	after you compile the library into an object file, you can also turn the object file into an static or shared library
+
+	(commands ar and gcc can be replaced with whatever equivalent your system uses)
+
+	static : ar rcs RGFW.a RGFW.o
+	shared :
+		windows:
+			gcc -shared RGFW.o -lopengl32 -lgdi32 -o RGFW.dll
+		linux:
+			gcc -shared RGFW.o -lX11 -lGL -lXrandr -o RGFW.so
+		macos:
+			gcc -shared RGFW.o -framework CoreVideo -framework Cocoa -framework OpenGL -framework IOKit
+*/
+
+
+
+/*
+	Credits :
+		EimaMei/Sacode : Much of the code for creating windows using winapi, Wrote the Silicon library, helped with MacOS Support, siliapp.h -> referencing
+
+		stb - This project is heavily inspired by the stb single header files
+
+		GLFW:
+			certain parts of winapi and X11 are very poorly documented,
+			GLFW's source code was referenced and used throughout the project.
+
+		contributors : (feel free to put yourself here if you contribute)
+		krisvers -> code review
+		EimaMei (SaCode) -> code review
+		Code-Nycticebus -> bug fixes
+		Rob Rohan -> X11 bugs and missing features, MacOS/Cocoa fixing memory issues/bugs
+		AICDG (@THISISAGOODNAME) -> vulkan support (example)
+		@Easymode -> support, testing/debugging, bug fixes and reviews
+		Joshua Rowe (omnisci3nce) - bug fix, review (macOS)
+		@lesleyrs -> bug fix, review (OpenGL)
+        Nick Porcino (meshula) - testing, organization, review (MacOS, examples)
+		@DarekParodia -> code review (X11) (C++)
+*/
+
+#if _MSC_VER
+	#pragma comment(lib, "gdi32")
+	#pragma comment(lib, "shell32")
+	#pragma comment(lib, "User32")
+    #pragma warning( push )
+	#pragma warning( disable : 4996 4191 4127)
+    #if _MSC_VER < 600
+        #define RGFW_C89
+    #endif
+#else
+    #if defined(__STDC__) && !defined(__STDC_VERSION__)
+        #define RGFW_C89
+    #endif
+#endif
+
+#ifndef RGFW_USERPTR
+	#define RGFW_USERPTR NULL
+#endif
+
+#ifndef RGFW_UNUSED
+	#define RGFW_UNUSED(x) (void)(x)
+#endif
+
+#ifndef RGFW_ROUND
+	#define RGFW_ROUND(x) (i32)((x) >= 0 ? (x) + 0.5f : (x) - 0.5f)
+#endif
+
+#ifndef RGFW_ALLOC
+	#include <stdlib.h>
+	#define RGFW_ALLOC malloc
+	#define RGFW_FREE free
+#endif
+
+#ifndef RGFW_ASSERT
+	#include <assert.h>
+	#define RGFW_ASSERT assert
+#endif
+
+#if !defined(RGFW_MEMCPY) || !defined(RGFW_STRNCMP) || !defined(RGFW_STRNCPY) || !defined(RGFW_MEMSET)
+	#include <string.h>
+#endif
+
+#ifndef RGFW_MEMSET
+	#define RGFW_MEMSET(ptr, value, num) memset(ptr, value, num)
+#endif
+
+#ifndef RGFW_MEMCPY
+	#define RGFW_MEMCPY(dist, src, len) memcpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRNCMP
+	#define RGFW_STRNCMP(s1, s2, max) strncmp(s1, s2, max)
+#endif
+
+#ifndef RGFW_STRNCPY
+	#define RGFW_STRNCPY(dist, src, len) strncpy(dist, src, len)
+#endif
+
+#ifndef RGFW_STRSTR
+	#define RGFW_STRSTR(str, substr) strstr(str, substr)
+#endif
+
+#ifndef RGFW_STRTOL
+	/* required for X11 XDnD and X11 Monitor DPI */
+	#include <stdlib.h>
+	#define RGFW_STRTOL(str, endptr, base) strtol(str, endptr, base)
+	#define RGFW_ATOF(num) atof(num)
+#endif
+
+#ifdef RGFW_WIN95 /* for windows 95 testing (not that it really works) */
+	#define RGFW_NO_MONITOR
+	#define RGFW_NO_PASSTHROUGH
+#endif
+
+#if defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT)
+	#if defined(_WIN32)
+		#if defined(__TINYC__) && (defined(RGFW_EXPORT) ||  defined(RGFW_IMPORT))
+			#define __declspec(x) __attribute__((x))
+		#endif
+
+		#if defined(RGFW_EXPORT)
+			#define RGFWDEF __declspec(dllexport)
+		#else
+			#define RGFWDEF __declspec(dllimport)
+		#endif
+	#else
+		#if defined(RGFW_EXPORT)
+			#define RGFWDEF __attribute__((visibility("default")))
+		#endif
+	#endif
+    #ifndef RGFWDEF
+        #define RGFWDEF
+    #endif
+#endif
+
+#ifndef RGFWDEF
+	#ifdef RGFW_C89
+		#define RGFWDEF __inline
+	#else
+		#define RGFWDEF inline
+	#endif
+#endif
+
+#ifndef RGFW_ENUM
+	#define RGFW_ENUM(type, name) type name; enum
+#endif
+
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+	extern "C" {
+#endif
+
+	/* makes sure the header file part is only defined once by default */
+#ifndef RGFW_HEADER
+
+#define RGFW_HEADER
+
+#include <stddef.h>
+#ifndef RGFW_INT_DEFINED
+	#ifdef RGFW_USE_INT /* optional for any system that might not have stdint.h */
+		typedef unsigned char       u8;
+		typedef signed char         i8;
+		typedef unsigned short     u16;
+		typedef signed short 	   i16;
+		typedef unsigned long int  u32;
+		typedef signed long int    i32;
+		typedef unsigned long long u64;
+		typedef signed long long   i64;
+	#else /* use stdint standard types instead of c "standard" types */
+		#include <stdint.h>
+
+		typedef uint8_t     u8;
+		typedef int8_t      i8;
+		typedef uint16_t   u16;
+		typedef int16_t    i16;
+		typedef uint32_t   u32;
+		typedef int32_t    i32;
+		typedef uint64_t   u64;
+		typedef int64_t    i64;
+	#endif
+	#define RGFW_INT_DEFINED
+#endif
+
+#ifndef RGFW_BOOL_DEFINED
+    #define RGFW_BOOL_DEFINED
+    typedef u8 RGFW_bool;
+#endif
+
+#define RGFW_BOOL(x) (RGFW_bool)((x) ? RGFW_TRUE : RGFW_FALSE) /* force an value to be 0 or 1 */
+#define RGFW_TRUE (RGFW_bool)1
+#define RGFW_FALSE (RGFW_bool)0
+
+/* these OS macros look better & are standardized */
+/* plus it helps with cross-compiling */
+
+#ifdef __EMSCRIPTEN__
+	#define RGFW_WASM
+
+	#if !defined(RGFW_NO_API) && !defined(RGFW_WEBGPU)
+		#define RGFW_OPENGL
+	#endif
+
+	#ifdef RGFW_EGL
+		#undef RGFW_EGL
+	#endif
+
+	#include <emscripten/html5.h>
+	#include <emscripten/key_codes.h>
+
+	#ifdef RGFW_WEBGPU
+		#include <emscripten/html5_webgpu.h>
+	#endif
+#endif
+
+#if defined(RGFW_X11) && defined(__APPLE__) && !defined(RGFW_CUSTOM_BACKEND)
+	#define RGFW_MACOS_X11
+	#define RGFW_UNIX
+	#undef __APPLE__
+#endif
+
+#if defined(_WIN32) && !defined(RGFW_X11) && !defined(RGFW_UNIX) && !defined(RGFW_WASM) && !defined(RGFW_CUSTOM_BACKEND) /* (if you're using X11 on windows some how) */
+	#define RGFW_WINDOWS
+	/* make sure the correct architecture is defined */
+	#if defined(_WIN64)
+		#define _AMD64_
+		#undef _X86_
+	#else
+		#undef _AMD64_
+		#ifndef _X86_
+			#define _X86_
+		#endif
+	#endif
+
+	#ifndef RGFW_NO_XINPUT
+		#ifdef __MINGW32__ /* try to find the right header */
+			#include <xinput.h>
+		#else
+			#include <XInput.h>
+		#endif
+	#endif
+#endif
+#if defined(RGFW_WAYLAND)
+	#define RGFW_DEBUG /* wayland will be in debug mode by default for now */
+    #if !defined(RGFW_NO_API) && (!defined(RGFW_BUFFER) || defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA)
+		#define RGFW_EGL
+		#define RGFW_OPENGL
+		#include <wayland-egl.h>
+	#endif
+
+	#define RGFW_UNIX
+	#include <wayland-client.h>
+#endif
+#if !defined(RGFW_NO_X11) && (defined(__unix__) || defined(RGFW_MACOS_X11) || defined(RGFW_X11))  && !defined(RGFW_WASM)  && !defined(RGFW_CUSTOM_BACKEND)
+	#define RGFW_MACOS_X11
+	#define RGFW_X11
+	#define RGFW_UNIX
+	#include <X11/Xlib.h>
+	#include <X11/Xutil.h>
+#elif defined(__APPLE__) && !defined(RGFW_MACOS_X11) && !defined(RGFW_X11)  && !defined(RGFW_WASM)  && !defined(RGFW_CUSTOM_BACKEND)
+	#define RGFW_MACOS
+	#if !defined(RGFW_BUFFER_BGR)
+		#define RGFW_BUFFER_BGR
+	#else
+		#undef RGFW_BUFFER_BGR
+	#endif
+#endif
+
+#if (defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)) && !defined(RGFW_EGL)
+	#define RGFW_EGL
+#endif
+
+#if !defined(RGFW_OSMESA) && !defined(RGFW_EGL) && !defined(RGFW_OPENGL) && !defined(RGFW_DIRECTX) && !defined(RGFW_BUFFER) && !defined(RGFW_NO_API)
+	#define RGFW_OPENGL
+#endif
+
+#ifdef RGFW_EGL
+	#include <EGL/egl.h>
+#elif defined(RGFW_OSMESA)
+	#ifdef RGFW_WINDOWS
+	#define OEMRESOURCE
+	#include <GL/gl.h>
+    	#ifndef GLAPIENTRY
+	#define GLAPIENTRY APIENTRY
+	#endif
+    	#ifndef GLAPI
+	#define GLAPI WINGDIAPI
+    	#endif
+	#endif
+
+	#ifndef __APPLE__
+		#include <GL/osmesa.h>
+	#else
+		#include <OpenGL/osmesa.h>
+	#endif
+#endif
+
+#if (defined(RGFW_OPENGL) || defined(RGFW_WEGL)) && defined(_MSC_VER)
+    #pragma comment(lib, "opengl32")
+#endif
+
+#if defined(RGFW_OPENGL) && defined(RGFW_X11)
+	#ifndef GLX_MESA_swap_control
+		#define  GLX_MESA_swap_control
+	#endif
+	#include <GL/glx.h> /* GLX defs, xlib.h, gl.h */
+#endif
+
+#define RGFW_COCOA_FRAME_NAME NULL
+
+/*! (unix) Toggle use of wayland. This will be on by default if you use `RGFW_WAYLAND` (if you don't use RGFW_WAYLAND, you don't expose WAYLAND functions)
+	this is mostly used to allow you to force the use of XWayland
+*/
+RGFWDEF void RGFW_useWayland(RGFW_bool wayland);
+RGFWDEF RGFW_bool RGFW_usingWayland(void);
+/*
+	regular RGFW stuff
+*/
+
+#define RGFW_key u8
+
+typedef RGFW_ENUM(u8, RGFW_eventType) {
+	/*! event codes */
+	RGFW_eventNone = 0, /*!< no event has been sent */
+ 	RGFW_keyPressed, /* a key has been pressed */
+	RGFW_keyReleased, /*!< a key has been released */
+	/*! key event note
+		the code of the key pressed is stored in
+		RGFW_event.key
+		!!Keycodes defined at the bottom of the RGFW_HEADER part of this file!!
+
+		while a string version is stored in
+		RGFW_event.KeyString
+
+		RGFW_event.keyMod holds the current keyMod
+		this means if CapsLock, NumLock are active or not
+	*/
+	RGFW_mouseButtonPressed, /*!< a mouse button has been pressed (left,middle,right) */
+	RGFW_mouseButtonReleased, /*!< a mouse button has been released (left,middle,right) */
+	RGFW_mousePosChanged, /*!< the position of the mouse has been changed */
+	/*! mouse event note
+		the x and y of the mouse can be found in the vector, RGFW_event.point
+
+		RGFW_event.button holds which mouse button was pressed
+	*/
+	RGFW_gamepadConnected, /*!< a gamepad was connected */
+	RGFW_gamepadDisconnected, /*!< a gamepad was disconnected */
+	RGFW_gamepadButtonPressed, /*!< a gamepad button was pressed */
+	RGFW_gamepadButtonReleased, /*!< a gamepad button was released */
+	RGFW_gamepadAxisMove, /*!< an axis of a gamepad was moved */
+	/*! gamepad event note
+		RGFW_event.gamepad holds which gamepad was altered, if any
+		RGFW_event.button holds which gamepad button was pressed
+
+		RGFW_event.axis holds the data of all the axises
+		RGFW_event.axisesCount says how many axises there are
+	*/
+	RGFW_windowMoved, /*!< the window was moved (by the user) */
+	RGFW_windowResized, /*!< the window was resized (by the user), [on WASM this means the browser was resized] */
+	RGFW_focusIn, /*!< window is in focus now */
+	RGFW_focusOut, /*!< window is out of focus now */
+	RGFW_mouseEnter, /* mouse entered the window */
+	RGFW_mouseLeave, /* mouse left the window */
+	RGFW_windowRefresh, /* The window content needs to be refreshed */
+
+	/* attribs change event note
+		The event data is sent straight to the window structure
+		with win->r.x, win->r.y, win->r.w and win->r.h
+	*/
+	RGFW_quit, /*!< the user clicked the quit button */
+	RGFW_DND, /*!< a file has been dropped into the window */
+	RGFW_DNDInit, /*!< the start of a dnd event, when the place where the file drop is known */
+	/* dnd data note
+		The x and y coords of the drop are stored in the vector RGFW_event.point
+
+		RGFW_event.droppedFilesCount holds how many files were dropped
+
+		This is also the size of the array which stores all the dropped file string,
+		RGFW_event.droppedFiles
+	*/
+	RGFW_windowMaximized, /*!< the window was maximized */
+	RGFW_windowMinimized, /*!< the window was minimized */
+	RGFW_windowRestored, /*!< the window was restored */
+	RGFW_scaleUpdated /*!< content scale factor changed */
+};
+
+/*! mouse button codes (RGFW_event.button) */
+typedef RGFW_ENUM(u8, RGFW_mouseButton) {
+	RGFW_mouseLeft = 0, /*!< left mouse button is pressed */
+	RGFW_mouseMiddle, /*!< mouse-wheel-button is pressed */
+	RGFW_mouseRight, /*!< right mouse button is pressed */
+	RGFW_mouseScrollUp, /*!< mouse wheel is scrolling up */
+	RGFW_mouseScrollDown, /*!< mouse wheel is scrolling down */
+	RGFW_mouseMisc1, RGFW_mouseMisc2, RGFW_mouseMisc3, RGFW_mouseMisc4, RGFW_mouseMisc5,
+	RGFW_mouseFinal
+};
+
+#ifndef RGFW_MAX_PATH
+#define RGFW_MAX_PATH 260 /* max length of a path (for dnd) */
+#endif
+#ifndef RGFW_MAX_DROPS
+#define RGFW_MAX_DROPS 260 /* max items you can drop at once */
+#endif
+
+#define RGFW_BIT(x) (1 << x)
+
+/* for RGFW_event.lockstate */
+typedef RGFW_ENUM(u8, RGFW_keymod) {
+	RGFW_modCapsLock = RGFW_BIT(0),
+	RGFW_modNumLock  = RGFW_BIT(1),
+	RGFW_modControl  = RGFW_BIT(2),
+	RGFW_modAlt = RGFW_BIT(3),
+	RGFW_modShift  = RGFW_BIT(4),
+	RGFW_modSuper = RGFW_BIT(5),
+	RGFW_modScrollLock = RGFW_BIT(6)
+};
+
+/*! gamepad button codes (based on xbox/playstation), you may need to change these values per controller */
+typedef RGFW_ENUM(u8, RGFW_gamepadCodes) {
+	RGFW_gamepadNone = 0, /*!< or PS X button */
+	RGFW_gamepadA, /*!< or PS X button */
+	RGFW_gamepadB, /*!< or PS circle button */
+	RGFW_gamepadY, /*!< or PS triangle button */
+	RGFW_gamepadX, /*!< or PS square button */
+	RGFW_gamepadStart, /*!< start button */
+	RGFW_gamepadSelect, /*!< select button */
+	RGFW_gamepadHome, /*!< home button */
+	RGFW_gamepadUp, /*!< dpad up */
+	RGFW_gamepadDown, /*!< dpad down */
+	RGFW_gamepadLeft, /*!< dpad left */
+	RGFW_gamepadRight, /*!< dpad right */
+	RGFW_gamepadL1, /*!< left bump */
+	RGFW_gamepadL2, /*!< left trigger */
+	RGFW_gamepadR1, /*!< right bumper */
+	RGFW_gamepadR2, /*!< right trigger */
+	RGFW_gamepadL3,  /* left thumb stick */
+	RGFW_gamepadR3, /*!< right thumb stick */
+	RGFW_gamepadFinal
+};
+
+/*! basic vector type, if there's not already a point/vector type of choice */
+#ifndef RGFW_point
+	typedef struct RGFW_point { i32 x, y; } RGFW_point;
+#endif
+
+/*! basic rect type, if there's not already a rect type of choice */
+#ifndef RGFW_rect
+	typedef struct RGFW_rect { i32 x, y, w, h; } RGFW_rect;
+#endif
+
+/*! basic area type, if there's not already a area type of choice */
+#ifndef RGFW_area
+	typedef struct RGFW_area { u32 w, h; } RGFW_area;
+#endif
+
+#if defined(__cplusplus) && !defined(__APPLE__)
+#define RGFW_POINT(x, y) {(i32)x, (i32)y}
+#define RGFW_RECT(x, y, w, h) {(i32)x, (i32)y, (i32)w, (i32)h}
+#define RGFW_AREA(w, h) {(u32)w, (u32)h}
+#else
+#define RGFW_POINT(x, y) (RGFW_point){(i32)(x), (i32)(y)}
+#define RGFW_RECT(x, y, w, h) (RGFW_rect){(i32)(x), (i32)(y), (i32)(w), (i32)(h)}
+#define RGFW_AREA(w, h) (RGFW_area){(u32)(w), (u32)(h)}
+#endif
+
+#ifndef RGFW_NO_MONITOR
+	/* monitor mode data | can be changed by the user (with functions)*/
+	typedef struct RGFW_monitorMode {
+		RGFW_area area; /*!< monitor workarea size */
+		u32 refreshRate; /*!< monitor refresh rate */
+		u8 red, blue, green;
+	} RGFW_monitorMode;
+
+	/*! structure for monitor data */
+	typedef struct RGFW_monitor {
+		i32 x, y; /*!< x - y of the monitor workarea */
+		char name[128]; /*!< monitor name */
+		float scaleX, scaleY; /*!< monitor content scale */
+		float pixelRatio; /*!< pixel ratio for monitor (1.0 for regular, 2.0 for hiDPI)  */
+		float physW, physH; /*!< monitor physical size in inches */
+
+		RGFW_monitorMode mode;
+	} RGFW_monitor;
+
+	/*! get an array of all the monitors (max 6) */
+	RGFWDEF RGFW_monitor* RGFW_getMonitors(size_t* len);
+	/*! get the primary monitor */
+	RGFWDEF RGFW_monitor RGFW_getPrimaryMonitor(void);
+
+	typedef RGFW_ENUM(u8, RGFW_modeRequest) {
+		RGFW_monitorScale = RGFW_BIT(0), /*!< scale the monitor size */
+		RGFW_monitorRefresh = RGFW_BIT(1), /*!< change the refresh rate */
+		RGFW_monitorRGB = RGFW_BIT(2), /*!< change the monitor RGB bits size */
+		RGFW_monitorAll = RGFW_monitorScale | RGFW_monitorRefresh | RGFW_monitorRGB
+	};
+
+	/*! request a specific mode */
+	RGFWDEF RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request);
+	/*! check if 2 monitor modes are the same */
+	RGFWDEF RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request);
+#endif
+
+/* RGFW mouse loading */
+typedef void RGFW_mouse;
+
+/*!< loads mouse icon from bitmap (similar to RGFW_window_setIcon). Icon NOT resized by default */
+RGFWDEF RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels);
+/*!< frees RGFW_mouse data */
+RGFWDEF void RGFW_freeMouse(RGFW_mouse* mouse);
+
+/* NOTE: some parts of the data can represent different things based on the event (read comments in RGFW_event struct) */
+/*! Event structure for checking/getting events */
+typedef struct RGFW_event {
+	RGFW_eventType type; /*!< which event has been sent?*/
+	RGFW_point point; /*!< mouse x, y of event (or drop point) */
+	RGFW_point vector; /*!< raw mouse movement */
+	float scaleX, scaleY; /*!< DPI scaling */
+
+	RGFW_key key; /*!< the physical key of the event, refers to where key is physically !!Keycodes defined at the bottom of the RGFW_HEADER part of this file!! */
+	u8 keyChar; /*!< mapped key char of the event */
+
+	RGFW_bool repeat; /*!< key press event repeated (the key is being held) */
+	RGFW_keymod keyMod;
+
+	u8 button; /* !< which mouse (or gamepad) button was pressed */
+	double scroll; /*!< the raw mouse scroll value */
+
+	u16 gamepad; /*! which gamepad this event applies to (if applicable to any) */
+	u8 axisesCount; /*!< number of axises */
+
+	u8 whichAxis; /* which axis was effected */
+	RGFW_point axis[4]; /*!< x, y of axises (-100 to 100) */
+
+	/*! drag and drop data */
+	/* 260 max paths with a max length of 260 */
+	char** droppedFiles; /*!< dropped files */
+	size_t droppedFilesCount; /*!< house many files were dropped */
+
+	void* _win; /*!< the window this event applies too (for event queue events) */
+} RGFW_event;
+
+/*! source data for the window (used by the APIs) */
+#ifdef RGFW_WINDOWS
+typedef struct RGFW_window_src {
+	HWND window; /*!< source window */
+	HDC hdc; /*!< source HDC */
+	i32 wOffset; /*!< width offset for window */
+	i32 hOffset; /*!< height offset for window */
+	HICON hIconSmall, hIconBig; /*!< source window icons */
+	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		HGLRC ctx; /*!< source graphics context */
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+	#endif
+
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		HDC hdcMem;
+		HBITMAP bitmap;
+		u8* bitmapBits;
+	#endif
+	RGFW_area maxSize, minSize, aspectRatio; /*!< for setting max/min resize (RGFW_WINDOWS) */
+} RGFW_window_src;
+#elif defined(RGFW_UNIX)
+typedef struct RGFW_window_src {
+#if defined(RGFW_X11)
+	Display* display; /*!< source display */
+	Window window; /*!< source window */
+	#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		GLXContext ctx; /*!< source graphics context */
+        GLXFBConfig bestFbc; 
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+	#endif
+
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			XImage* bitmap;
+	#endif
+	GC gc;
+	XVisualInfo visual;
+    #ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+        i64 counter_value;
+        XID counter;
+    #endif
+    RGFW_rect r;
+#endif /* RGFW_X11 */
+#if defined(RGFW_WAYLAND)
+	struct wl_display* wl_display;
+	struct wl_surface* surface;
+	struct wl_buffer* wl_buffer;
+	struct wl_keyboard* keyboard;
+
+	struct wl_compositor* compositor;
+	struct xdg_surface* xdg_surface;
+	struct xdg_toplevel* xdg_toplevel;
+	struct zxdg_toplevel_decoration_v1* decoration;
+	struct xdg_wm_base* xdg_wm_base;
+	struct wl_shm* shm;
+	struct wl_seat *seat;
+	u8* buffer;
+	#if defined(RGFW_EGL)
+		struct wl_egl_window* eglWindow;
+	#endif
+	#if defined(RGFW_EGL) && !defined(RGFW_X11)
+			EGLSurface EGL_surface;
+			EGLDisplay EGL_display;
+			EGLContext EGL_context;
+	#elif defined(RGFW_OSMESA) && !defined(RGFW_X11)
+		OSMesaContext ctx;
+	#endif
+#endif /* RGFW_WAYLAND */
+} RGFW_window_src;
+#endif /* RGFW_UNIX */
+#if defined(RGFW_MACOS)
+typedef struct RGFW_window_src {
+	void* window;
+#if (defined(RGFW_OPENGL)) && !defined(RGFW_OSMESA) && !defined(RGFW_EGL)
+		void* ctx; /*!< source graphics context */
+#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+#elif defined(RGFW_EGL)
+		EGLSurface EGL_surface;
+		EGLDisplay EGL_display;
+		EGLContext EGL_context;
+#endif
+
+	void* view; /* apple viewpoint thingy */
+	void* mouse;
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+#endif
+} RGFW_window_src;
+#elif defined(RGFW_WASM)
+typedef struct RGFW_window_src {
+	#if defined(RGFW_WEBGPU)
+		WGPUInstance ctx;
+        WGPUDevice device;
+        WGPUQueue queue;
+	#elif defined(RGFW_OSMESA)
+		OSMesaContext ctx;
+	#else
+		EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx;
+	#endif
+} RGFW_window_src;
+#endif
+
+/*! Optional arguments for making a windows */
+typedef RGFW_ENUM(u32, RGFW_windowFlags) {
+	RGFW_windowNoInitAPI = RGFW_BIT(0), /* do NOT init an API (including the software rendering buffer) (mostly for bindings. you can also use `#define RGFW_NO_API`) */
+	RGFW_windowNoBorder = RGFW_BIT(1), /*!< the window doesn't have a border */
+	RGFW_windowNoResize = RGFW_BIT(2), /*!< the window cannot be resized by the user */
+	RGFW_windowAllowDND = RGFW_BIT(3), /*!< the window supports drag and drop */
+	RGFW_windowHideMouse = RGFW_BIT(4), /*! the window should hide the mouse (can be toggled later on using `RGFW_window_mouseShow`) */
+	RGFW_windowFullscreen = RGFW_BIT(5), /*!< the window is fullscreen by default */
+	RGFW_windowTransparent = RGFW_BIT(6), /*!< the window is transparent (only properly works on X11 and MacOS, although it's meant for for windows) */
+	RGFW_windowCenter = RGFW_BIT(7), /*! center the window on the screen */
+	RGFW_windowOpenglSoftware = RGFW_BIT(8), /*! use OpenGL software rendering */
+	RGFW_windowCocoaCHDirToRes = RGFW_BIT(9), /*! (cocoa only), change directory to resource folder */
+	RGFW_windowScaleToMonitor = RGFW_BIT(10), /*! scale the window to the screen */
+	RGFW_windowHide = RGFW_BIT(11), /*! the window is hidden */
+	RGFW_windowMaximize = RGFW_BIT(12),
+	RGFW_windowCenterCursor = RGFW_BIT(13),
+	RGFW_windowFloating = RGFW_BIT(14), /*!< create a floating window */
+	RGFW_windowFreeOnClose = RGFW_BIT(15), /*!< free (RGFW_window_close) the RGFW_window struct when the window is closed (by the end user) */
+	RGFW_windowFocusOnShow = RGFW_BIT(16), /*!< focus the window when it's shown */
+	RGFW_windowMinimize = RGFW_BIT(17), /*!< focus the window when it's shown */
+	RGFW_windowFocus = RGFW_BIT(18), /*!< if the window is in focus */
+	RGFW_windowedFullscreen = RGFW_windowNoBorder | RGFW_windowMaximize
+};
+
+typedef struct RGFW_window {
+	RGFW_window_src src; /*!< src window data */
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	u8* buffer; /*!< buffer for non-GPU systems (OSMesa, basic software rendering) */
+	/* when rendering using RGFW_BUFFER, the buffer is in the RGBA format */
+	RGFW_area bufferSize;
+#endif
+	void* userPtr; /* ptr for usr data */
+
+	RGFW_event event; /*!< current event */
+
+	RGFW_rect r; /*!< the x, y, w and h of the struct */
+    
+    /*! which key RGFW_window_shouldClose checks. Settting this to RGFW_keyNULL disables the feature. */
+    RGFW_key exitKey;     
+	RGFW_point _lastMousePoint; /*!< last cusor point (for raw mouse data) */
+    
+	u32 _flags; /*!< windows flags (for RGFW to check) */
+	RGFW_rect _oldRect; /*!< rect before fullscreen */
+} RGFW_window; /*!< window structure for managing the window */
+
+#if defined(RGFW_X11) || defined(RGFW_MACOS)
+	typedef u64 RGFW_thread; /*!< thread type unix */
+#else
+	typedef void* RGFW_thread; /*!< thread type for windows */
+#endif
+
+/*! scale monitor to window size */
+RGFWDEF RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win);
+
+/** * @defgroup Window_management
+* @{ */
+
+
+/*!
+ * the class name for X11 and WinAPI. apps with the same class will be grouped by the WM
+ * by default the class name will == the root window's name
+*/
+RGFWDEF void RGFW_setClassName(const char* name);
+RGFWDEF void RGFW_setXInstName(const char* name); /*!< X11 instance name (window name will by used by default) */
+
+/*! (cocoa only) change directory to resource folder */
+RGFWDEF void RGFW_moveToMacOSResourceDir(void);
+
+/* NOTE: (windows) if the executable has an icon resource named RGFW_ICON, it will be set as the initial icon for the window */
+
+RGFWDEF RGFW_window* RGFW_createWindow(
+	const char* name, /* name of the window */
+	RGFW_rect rect, /* rect of window */
+	RGFW_windowFlags flags /* extra arguments ((u32)0 means no flags used)*/
+); /*!< function to create a window and struct */
+
+RGFWDEF RGFW_window* RGFW_createWindowPtr(
+	const char* name, /* name of the window */
+	RGFW_rect rect, /* rect of window */
+	RGFW_windowFlags flags, /* extra arguments (NULL / (u32)0 means no flags used) */
+	RGFW_window* win /* ptr to the window struct you want to use */
+); /*!< function to create a window (without allocating a window struct) */
+
+RGFWDEF void RGFW_window_initBuffer(RGFW_window* win);
+RGFWDEF void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area);
+RGFWDEF void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area);
+
+/*! set the window flags (will undo flags if they don't match the old ones) */
+RGFWDEF void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags);
+
+/*! get the size of the screen to an area struct */
+RGFWDEF RGFW_area RGFW_getScreenSize(void);
+
+
+/*!
+	this function checks an *individual* event (and updates window structure attributes)
+	this means, using this function without a while loop may cause event lag
+
+	ex.
+
+	while (RGFW_window_checkEvent(win) != NULL) [this keeps checking events until it reaches the last one]
+
+	this function is optional if you choose to use event callbacks,
+	although you still need some way to tell RGFW to process events eg. `RGFW_window_checkEvents`
+*/
+
+RGFWDEF RGFW_event* RGFW_window_checkEvent(RGFW_window* win); /*!< check current event (returns a pointer to win->event or NULL if there is no event)*/
+
+/*!
+	for RGFW_window_eventWait and RGFW_window_checkEvents
+	waitMS -> Allows the function to keep checking for events even after `RGFW_window_checkEvent == NULL`
+			  if waitMS == 0, the loop will not wait for events
+			  if waitMS > 0, the loop will wait that many miliseconds after there are no more events until it returns
+			  if waitMS == -1 or waitMS == the max size of an unsigned 32-bit int, the loop will not return until it gets another event
+*/
+typedef RGFW_ENUM(i32, RGFW_eventWait) {
+	RGFW_eventNoWait = 0,
+	RGFW_eventWaitNext = -1
+};
+
+/*! sleep until RGFW gets an event or the timer ends (defined by OS) */
+RGFWDEF void RGFW_window_eventWait(RGFW_window* win, i32 waitMS);
+
+/*!
+	check all the events until there are none left.
+	This should only be used if you're using callbacks only
+*/
+RGFWDEF void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS);
+
+/*!
+	tell RGFW_window_eventWait to stop waiting (to be ran from another thread)
+*/
+RGFWDEF void RGFW_stopCheckEvents(void);
+
+/*! window managment functions */
+RGFWDEF void RGFW_window_close(RGFW_window* win); /*!< close the window and free leftover data */
+
+/*! move a window to a given point */
+RGFWDEF void RGFW_window_move(RGFW_window* win,
+	RGFW_point v /*!< new pos */
+);
+
+#ifndef RGFW_NO_MONITOR
+	/*! move window to a specific monitor */
+	RGFWDEF void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m /* monitor */);
+#endif
+
+/*! resize window to a current size/area */
+RGFWDEF void RGFW_window_resize(RGFW_window* win, /*!< source window */
+	RGFW_area a /*!< new size */
+);
+
+/*! set window aspect ratio */
+RGFWDEF void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a);
+/*! set the minimum dimensions of a window */
+RGFWDEF void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a);
+/*! set the maximum dimensions of a window */
+RGFWDEF void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a);
+
+RGFWDEF void RGFW_window_focus(RGFW_window* win); /*!< sets the focus to this window */
+RGFWDEF RGFW_bool RGFW_window_isInFocus(RGFW_window* win); /*!< checks the focus to this window */
+RGFWDEF void RGFW_window_raise(RGFW_window* win); /*!< raise the window (to the top) */
+RGFWDEF void RGFW_window_maximize(RGFW_window* win); /*!< maximize the window */
+RGFWDEF void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen); /*!< turn fullscreen on / off for a window */
+RGFWDEF void RGFW_window_center(RGFW_window* win); /*!< center the window */
+RGFWDEF void RGFW_window_minimize(RGFW_window* win); /*!< minimize the window (in taskbar (per OS))*/
+RGFWDEF void RGFW_window_restore(RGFW_window* win); /*!< restore the window from minimized (per OS)*/
+RGFWDEF void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating); /*!< make the window a floating window */
+RGFWDEF void RGFW_window_setOpacity(RGFW_window* win, u8 opacity); /*!< sets the opacity of a window */
+
+RGFWDEF RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win);
+
+/*! if the window should have a border or not (borderless) based on bool value of `border` */
+RGFWDEF void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border);
+RGFWDEF RGFW_bool RGFW_window_borderless(RGFW_window* win);
+
+/*! turn on / off dnd (RGFW_windowAllowDND stil must be passed to the window)*/
+RGFWDEF void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow);
+/*! check if DND is allowed */
+RGFWDEF RGFW_bool RGFW_window_allowsDND(RGFW_window* win);
+
+
+#ifndef RGFW_NO_PASSTHROUGH
+	/*! turn on / off mouse passthrough */
+	RGFWDEF void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough);
+#endif
+
+/*! rename window to a given string */
+RGFWDEF void RGFW_window_setName(RGFW_window* win,
+	const char* name
+);
+
+RGFWDEF RGFW_bool RGFW_window_setIcon(RGFW_window* win, /*!< source window */
+	u8* icon /*!< icon bitmap */,
+	RGFW_area a /*!< width and height of the bitmap */,
+	i32 channels /*!< how many channels the bitmap has (rgb : 3, rgba : 4) */
+); /*!< image MAY be resized by default, set both the taskbar and window icon */
+
+typedef RGFW_ENUM(u8, RGFW_icon) {
+	RGFW_iconTaskbar = RGFW_BIT(0),
+	RGFW_iconWindow = RGFW_BIT(1),
+	RGFW_iconBoth = RGFW_iconTaskbar | RGFW_iconWindow
+};
+RGFWDEF RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type);
+
+/*!< sets mouse to RGFW_mouse icon (loaded from a bitmap struct) */
+RGFWDEF void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse);
+
+/*!< sets the mouse to a standard API cursor (based on RGFW_MOUSE, as seen at the end of the RGFW_HEADER part of this file) */
+RGFWDEF	RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse);
+
+RGFWDEF RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win); /*!< sets the mouse to the default mouse icon */
+/*
+	Locks cursor at the center of the window
+	win->event.point becomes raw mouse movement data
+
+	this is useful for a 3D camera
+*/
+RGFWDEF void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area);
+/*! if the mouse is held by RGFW */
+RGFWDEF RGFW_bool RGFW_window_mouseHeld(RGFW_window* win);
+/*! stop holding the mouse and let it move freely */
+RGFWDEF void RGFW_window_mouseUnhold(RGFW_window* win);
+
+/*! hide the window */
+RGFWDEF void RGFW_window_hide(RGFW_window* win);
+/*! show the window */
+RGFWDEF void RGFW_window_show(RGFW_window* win);
+
+/*
+	makes it so `RGFW_window_shouldClose` returns true or overrides a window close
+	by modifying window flags
+*/
+RGFWDEF void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose);
+
+/*! where the mouse is on the screen */
+RGFWDEF RGFW_point RGFW_getGlobalMousePoint(void);
+
+/*! where the mouse is on the window */
+RGFWDEF RGFW_point RGFW_window_getMousePoint(RGFW_window* win);
+
+/*! show the mouse or hide the mouse */
+RGFWDEF void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show);
+/*! if the mouse is hidden */
+RGFWDEF RGFW_bool RGFW_window_mouseHidden(RGFW_window* win);
+/*! move the mouse to a given point */
+RGFWDEF void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v);
+
+/*! if the window should close (RGFW_close was sent or escape was pressed) */
+RGFWDEF RGFW_bool RGFW_window_shouldClose(RGFW_window* win);
+/*! if the window is fullscreen */
+RGFWDEF RGFW_bool RGFW_window_isFullscreen(RGFW_window* win);
+/*! if the window is hidden */
+RGFWDEF RGFW_bool RGFW_window_isHidden(RGFW_window* win);
+/*! if the window is minimized */
+RGFWDEF RGFW_bool RGFW_window_isMinimized(RGFW_window* win);
+/*! if the window is maximized */
+RGFWDEF RGFW_bool RGFW_window_isMaximized(RGFW_window* win);
+/*! if the window is floating */
+RGFWDEF RGFW_bool RGFW_window_isFloating(RGFW_window* win);
+/** @} */
+
+/** * @defgroup Monitor
+* @{ */
+
+#ifndef RGFW_NO_MONITOR
+/*
+	scale the window to the monitor.
+	This is run by default if the user uses the arg `RGFW_scaleToMonitor` during window creation
+*/
+RGFWDEF void RGFW_window_scaleToMonitor(RGFW_window* win);
+/*! get the struct of the window's monitor  */
+RGFWDEF RGFW_monitor RGFW_window_getMonitor(RGFW_window* win);
+#endif
+
+/** @} */
+
+/** * @defgroup Input
+* @{ */
+
+/*! if window == NULL, it checks if the key is pressed globally. Otherwise, it checks only if the key is pressed while the window in focus. */
+RGFWDEF RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key); /*!< if key is pressed (key code)*/
+
+RGFWDEF RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key); /*!< if key was pressed (checks previous state only) (key code) */
+
+RGFWDEF RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key); /*!< if key is held (key code) */
+RGFWDEF RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key); /*!< if key is released (key code) */
+
+/* if a key is pressed and then released, pretty much the same as RGFW_isReleased */
+RGFWDEF RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key /*!< key code */);
+
+/*! if a mouse button is pressed */
+RGFWDEF RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
+/*! if a mouse button is held */
+RGFWDEF RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
+/*! if a mouse button was released */
+RGFWDEF RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
+/*! if a mouse button was pressed (checks previous state only) */
+RGFWDEF RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button /*!< mouse button code */ );
+/** @} */
+
+/** * @defgroup Clipboard
+* @{ */
+typedef ptrdiff_t RGFW_ssize_t;
+
+RGFWDEF const char* RGFW_readClipboard(size_t* size); /*!< read clipboard data */
+/*! read clipboard data or send a NULL str to just get the length of the clipboard data */
+RGFWDEF RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity);
+RGFWDEF void RGFW_writeClipboard(const char* text, u32 textLen); /*!< write text to the clipboard */
+/** @} */
+
+
+
+/** * @defgroup error handling
+* @{ */
+typedef RGFW_ENUM(u8, RGFW_debugType) {
+	RGFW_typeError = 0, RGFW_typeWarning, RGFW_typeInfo
+};
+
+typedef RGFW_ENUM(u8, RGFW_errorCode) {
+	RGFW_noError = 0, /*!< no error */
+	RGFW_errOpenglContext, RGFW_errEGLContext, /*!< error with the OpenGL context */
+	RGFW_errWayland,
+	RGFW_errDirectXContext,
+	RGFW_errIOKit,
+	RGFW_errClipboard,
+	RGFW_errFailedFuncLoad,
+	RGFW_errBuffer,
+	RGFW_infoMonitor, RGFW_infoWindow, RGFW_infoBuffer, RGFW_infoGlobal, RGFW_infoOpenGL,
+	RGFW_warningWayland, RGFW_warningOpenGL
+};
+
+typedef struct RGFW_debugContext { RGFW_window* win; RGFW_monitor* monitor; u32 srcError; } RGFW_debugContext;
+
+#if defined(__cplusplus) && !defined(__APPLE__)
+#define RGFW_DEBUG_CTX(win, err) {win, NULL, err}
+#define RGFW_DEBUG_CTX_MON(monitor) {_RGFW.root, &monitor, 0}
+#else
+#define RGFW_DEBUG_CTX(win, err) (RGFW_debugContext){win, NULL, err}
+#define RGFW_DEBUG_CTX_MON(monitor) (RGFW_debugContext){_RGFW.root, &monitor, 0}
+#endif
+
+typedef void (* RGFW_debugfunc)(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg);
+RGFWDEF RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func);
+RGFWDEF void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg);
+/** @} */
+
+/**
+
+
+	event callbacks.
+	These are completely optional, so you can use the normal
+	RGFW_checkEvent() method if you prefer that
+
+* @defgroup Callbacks
+* @{
+*/
+
+/*! RGFW_windowMoved, the window and its new rect value  */
+typedef void (* RGFW_windowMovedfunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_windowResized, the window and its new rect value  */
+typedef void (* RGFW_windowResizedfunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_windowRestored, the window and its new rect value  */
+typedef void (* RGFW_windowRestoredfunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_windowMaximized, the window and its new rect value  */
+typedef void (* RGFW_windowMaximizedfunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_windowMinimized, the window and its new rect value  */
+typedef void (* RGFW_windowMinimizedfunc)(RGFW_window* win, RGFW_rect r);
+/*! RGFW_quit, the window that was closed */
+typedef void (* RGFW_windowQuitfunc)(RGFW_window* win);
+/*! RGFW_focusIn / RGFW_focusOut, the window who's focus has changed and if its in focus */
+typedef void (* RGFW_focusfunc)(RGFW_window* win, RGFW_bool inFocus);
+/*! RGFW_mouseEnter / RGFW_mouseLeave, the window that changed, the point of the mouse (enter only) and if the mouse has entered */
+typedef void (* RGFW_mouseNotifyfunc)(RGFW_window* win, RGFW_point point, RGFW_bool status);
+/*! RGFW_mousePosChanged, the window that the move happened on, and the new point of the mouse  */
+typedef void (* RGFW_mousePosfunc)(RGFW_window* win, RGFW_point point, RGFW_point vector);
+/*! RGFW_DNDInit, the window, the point of the drop on the windows */
+typedef void (* RGFW_dndInitfunc)(RGFW_window* win, RGFW_point point);
+/*! RGFW_windowRefresh, the window that needs to be refreshed */
+typedef void (* RGFW_windowRefreshfunc)(RGFW_window* win);
+/*! RGFW_keyPressed / RGFW_keyReleased, the window that got the event, the mapped key, the physical key, the string version, the state of the mod keys, if it was a press (else it's a release) */
+typedef void (* RGFW_keyfunc)(RGFW_window* win, u8 key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool pressed);
+/*! RGFW_mouseButtonPressed / RGFW_mouseButtonReleased, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release)  */
+typedef void (* RGFW_mouseButtonfunc)(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed);
+/*! RGFW_gamepadButtonPressed, the window that got the event, the button that was pressed, the scroll value, if it was a press (else it's a release) */
+typedef void (* RGFW_gamepadButtonfunc)(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed);
+/*! RGFW_gamepadAxisMove, the window that got the event, the gamepad in question, the axis values and the axis count */
+typedef void (* RGFW_gamepadAxisfunc)(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis);
+/*! RGFW_gamepadConnected / RGFW_gamepadDisconnected, the window that got the event, the gamepad in question, if the controller was connected (else it was disconnected) */
+typedef void (* RGFW_gamepadfunc)(RGFW_window* win, u16 gamepad, RGFW_bool connected);
+/*! RGFW_dnd, the window that had the drop, the drop data and the number of files dropped */
+typedef void (* RGFW_dndfunc)(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount);
+/*! RGFW_scaleUpdated, the window the event was sent to, content scaleX, content scaleY */
+typedef void (* RGFW_scaleUpdatedfunc)(RGFW_window* win, float scaleX, float scaleY);
+
+/*! set callback for a window move event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowMovedfunc RGFW_setWindowMovedCallback(RGFW_windowMovedfunc func);
+/*! set callback for a window resize event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowResizedfunc RGFW_setWindowResizedCallback(RGFW_windowResizedfunc func);
+/*! set callback for a window quit event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowQuitfunc RGFW_setWindowQuitCallback(RGFW_windowQuitfunc func);
+/*! set callback for a mouse move event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mousePosfunc RGFW_setMousePosCallback(RGFW_mousePosfunc func);
+/*! set callback for a window refresh event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_windowRefreshfunc RGFW_setWindowRefreshCallback(RGFW_windowRefreshfunc func);
+/*! set callback for a window focus change event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_focusfunc RGFW_setFocusCallback(RGFW_focusfunc func);
+/*! set callback for a mouse notify event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mouseNotifyfunc RGFW_setMouseNotifyCallback(RGFW_mouseNotifyfunc func);
+/*! set callback for a drop event event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_dndfunc RGFW_setDndCallback(RGFW_dndfunc func);
+/*! set callback for a start of a drop event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_dndInitfunc RGFW_setDndInitCallback(RGFW_dndInitfunc func);
+/*! set callback for a key (press / release) event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_keyfunc RGFW_setKeyCallback(RGFW_keyfunc func);
+/*! set callback for a mouse button (press / release) event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_mouseButtonfunc RGFW_setMouseButtonCallback(RGFW_mouseButtonfunc func);
+/*! set callback for a controller button (press / release) event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_gamepadButtonfunc RGFW_setGamepadButtonCallback(RGFW_gamepadButtonfunc func);
+/*! set callback for a gamepad axis move event. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_gamepadAxisfunc RGFW_setGamepadAxisCallback(RGFW_gamepadAxisfunc func);
+/*! set callback for when a controller is connected or disconnected. Returns the previous callback function (if it was set) */
+RGFWDEF RGFW_gamepadfunc RGFW_setGamepadCallback(RGFW_gamepadfunc func);
+/*! set call back for when window is maximized. Returns the previous callback function (if it was set) */
+RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMaximizedCallback(RGFW_windowResizedfunc func);
+/*! set call back for when window is minimized. Returns the previous callback function (if it was set) */
+RGFWDEF RGFW_windowResizedfunc RGFW_setWindowMinimizedCallback(RGFW_windowResizedfunc func);
+/*! set call back for when window is restored. Returns the previous callback function (if it was set) */
+RGFWDEF RGFW_windowResizedfunc RGFW_setWindowRestoredCallback(RGFW_windowResizedfunc func);
+/*! set callback for when the DPI changes. Returns previous callback function (if it was set)  */
+RGFWDEF RGFW_scaleUpdatedfunc RGFW_setScaleUpdatedCallback(RGFW_scaleUpdatedfunc func);
+/** @} */
+
+/** * @defgroup Threads
+* @{ */
+
+#ifndef RGFW_NO_THREADS
+/*! threading functions */
+
+/*! NOTE! (for X11/linux) : if you define a window in a thread, it must be run after the original thread's window is created or else there will be a memory error */
+/*
+	I'd suggest you use sili's threading functions instead
+	if you're going to use sili
+	which is a good idea generally
+*/
+
+#if defined(__unix__) || defined(__APPLE__) || defined(RGFW_WASM) || defined(RGFW_CUSTOM_BACKEND)
+	typedef void* (* RGFW_threadFunc_ptr)(void*);
+#else
+	typedef DWORD (__stdcall *RGFW_threadFunc_ptr) (LPVOID lpThreadParameter);
+#endif
+
+RGFWDEF RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args); /*!< create a thread */
+RGFWDEF void RGFW_cancelThread(RGFW_thread thread); /*!< cancels a thread */
+RGFWDEF void RGFW_joinThread(RGFW_thread thread); /*!< join thread to current thread */
+RGFWDEF void RGFW_setThreadPriority(RGFW_thread thread, u8 priority); /*!< sets the priority priority  */
+#endif
+
+/** @} */
+
+/** * @defgroup gamepad
+* @{ */
+
+typedef RGFW_ENUM(u8, RGFW_gamepadType) {
+	RGFW_gamepadMicrosoft = 0, RGFW_gamepadSony, RGFW_gamepadNintendo, RGFW_gamepadLogitech, RGFW_gamepadUnknown
+};
+
+/*! gamepad count starts at 0*/
+RGFWDEF u32 RGFW_isPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
+RGFWDEF u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
+RGFWDEF u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
+RGFWDEF u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button);
+RGFWDEF RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis);
+RGFWDEF const char* RGFW_getGamepadName(RGFW_window* win, u16 controller);
+RGFWDEF size_t RGFW_getGamepadCount(RGFW_window* win);
+RGFWDEF RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller);
+
+/** @} */
+
+/** * @defgroup graphics_API
+* @{ */
+
+/*!< make the window the current opengl drawing context
+
+	NOTE:
+ 	if you want to switch the graphics context's thread,
+	you have to run RGFW_window_makeCurrent(NULL); on the old thread
+	then RGFW_window_makeCurrent(valid_window) on the new thread
+*/
+RGFWDEF void RGFW_window_makeCurrent(RGFW_window* win);
+
+/*! get current RGFW window graphics context */
+RGFWDEF RGFW_window* RGFW_getCurrent(void);
+
+/* supports openGL, directX, OSMesa, EGL and software rendering */
+RGFWDEF void RGFW_window_swapBuffers(RGFW_window* win); /*!< swap the rendering buffer */
+RGFWDEF void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval);
+/*!< render the software rendering buffer (this is called by RGFW_window_swapInterval)  */
+RGFWDEF void RGFW_window_swapBuffers_software(RGFW_window* win);
+
+typedef void (*RGFW_proc)(void); /* function pointer equivalent of void* */
+
+/*! native API functions */
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
+/*!< create an opengl context for the RGFW window, run by createWindow by default (unless the RGFW_windowNoInitAPI is included) */
+RGFWDEF void RGFW_window_initOpenGL(RGFW_window* win);
+/*!< called by `RGFW_window_close` by default (unless the RGFW_windowNoInitAPI is set) */
+RGFWDEF void RGFW_window_freeOpenGL(RGFW_window* win);
+
+/*! OpenGL init hints */
+typedef RGFW_ENUM(u8, RGFW_glHints)  {
+	RGFW_glStencil = 0,  /*!< set stencil buffer bit size (8 by default) */
+	RGFW_glSamples, /*!< set number of sampiling buffers (4 by default) */
+	RGFW_glStereo, /*!< use GL_STEREO (GL_FALSE by default) */
+	RGFW_glAuxBuffers, /*!< number of aux buffers (0 by default) */
+	RGFW_glDoubleBuffer, /*!< request double buffering */
+	RGFW_glRed, RGFW_glGreen, RGFW_glBlue, RGFW_glAlpha, /*!< set RGBA bit sizes */
+	RGFW_glDepth,
+	RGFW_glAccumRed, RGFW_glAccumGreen, RGFW_glAccumBlue,RGFW_glAccumAlpha, /*!< set accumulated RGBA bit sizes */
+	RGFW_glSRGB, /*!< request sRGA */
+	RGFW_glRobustness, /*!< request a robust context */
+	RGFW_glDebug, /*!< request opengl debugging */
+	RGFW_glNoError, /*!< request no opengl errors */
+	RGFW_glReleaseBehavior,
+	RGFW_glProfile,
+	RGFW_glMajor, RGFW_glMinor,
+	RGFW_glFinalHint = 32, /*!< the final hint (not for setting) */
+	RGFW_releaseFlush = 0,  RGFW_glReleaseNone, /* RGFW_glReleaseBehavior options */
+	RGFW_glCore = 0,  RGFW_glCompatibility /*!< RGFW_glProfile options */
+};
+RGFWDEF void RGFW_setGLHint(RGFW_glHints hint, i32 value);
+RGFWDEF RGFW_bool RGFW_extensionSupported(const char* extension, size_t len);	/*!< check if whether the specified API extension is supported by the current OpenGL or OpenGL ES context */
+RGFWDEF RGFW_proc RGFW_getProcAddress(const char* procname); /*!< get native opengl proc address */
+RGFWDEF void RGFW_window_makeCurrent_OpenGL(RGFW_window* win); /*!< to be called by RGFW_window_makeCurrent */
+RGFWDEF void RGFW_window_swapBuffers_OpenGL(RGFW_window* win); /*!< swap opengl buffer (only) called by RGFW_window_swapInterval  */
+void* RGFW_getCurrent_OpenGL(void); /*!< get the current context (OpenGL backend (GLX) (WGL) (EGL) (cocoa) (webgl))*/
+
+RGFWDEF RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len);	/*!< check if whether the specified platform-specific API extension is supported by the current OpenGL or OpenGL ES context */
+#endif
+#ifdef RGFW_VULKAN
+	#if defined(RGFW_WAYLAND) && defined(RGFW_X11)
+    	#define VK_USE_PLATFORM_WAYLAND_KHR
+		#define VK_USE_PLATFORM_XLIB_KHR
+        #define RGFW_VK_SURFACE ((RGFW_usingWayland()) ? ("VK_KHR_wayland_surface") : ("VK_KHR_xlib_surface"))
+    #elif defined(RGFW_WAYLAND)
+		#define VK_USE_PLATFORM_WAYLAND_KHR
+		#define VK_USE_PLATFORM_XLIB_KHR
+        #define RGFW_VK_SURFACE "VK_KHR_wayland_surface"
+    #elif defined(RGFW_X11)
+		#define VK_USE_PLATFORM_XLIB_KHR
+		#define RGFW_VK_SURFACE "VK_KHR_xlib_surface"
+	#elif defined(RGFW_WINDOWS)
+		#define VK_USE_PLATFORM_WIN32_KHR
+		#define OEMRESOURCE
+		#define RGFW_VK_SURFACE "VK_KHR_win32_surface"
+	#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+		#define VK_USE_PLATFORM_MACOS_MVK
+		#define RGFW_VK_SURFACE "VK_MVK_macos_surface"
+	#else
+		#define RGFW_VK_SURFACE NULL
+	#endif
+
+/* if you don't want to use the above macros */
+RGFWDEF const char** RGFW_getVKRequiredInstanceExtensions(size_t* count); /*!< gets (static) extension array (and size (which will be 2)) */
+
+#include <vulkan/vulkan.h>
+
+RGFWDEF VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface);
+RGFWDEF RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex);
+#endif
+#ifdef RGFW_DIRECTX
+#ifndef RGFW_WINDOWS
+	#undef RGFW_DIRECTX
+#else
+	#define OEMRESOURCE
+	#include <dxgi.h>
+
+	#ifndef __cplusplus
+		#define __uuidof(T) IID_##T
+	#endif
+RGFWDEF int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain);
+#endif
+#endif
+
+/** @} */
+
+/** * @defgroup Supporting
+* @{ */
+
+/*! optional init/deinit function */
+RGFWDEF i32 RGFW_init(void); /*!< is called by default when the first window is created by default */
+RGFWDEF void RGFW_deinit(void); /*!< is called by default when the last open window is closed */
+
+RGFWDEF double RGFW_getTime(void); /*!< get time in seconds since RGFW_setTime, which ran when the first window is open  */
+RGFWDEF u64 RGFW_getTimeNS(void); /*!< get time in nanoseconds RGFW_setTime, which ran when the first window is open */
+RGFWDEF void RGFW_sleep(u64 milisecond); /*!< sleep for a set time */
+RGFWDEF void RGFW_setTime(double time); /*!< set timer in seconds */
+RGFWDEF u64 RGFW_getTimerValue(void); /*!< get API timer value */
+RGFWDEF u64 RGFW_getTimerFreq(void); /*!< get API time freq */
+
+/*< updates fps / sets fps to cap (must by ran manually by the user at the end of a frame), returns current fps */
+RGFWDEF u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap);
+
+/*!< change which window is the root window */
+RGFWDEF void RGFW_setRootWindow(RGFW_window* win);
+RGFWDEF RGFW_window* RGFW_getRootWindow(void);
+
+/*! standard event queue, used for injecting events and returning source API callback events like any other queue check */
+/* these are all used internally by RGFW */
+void RGFW_eventQueuePush(RGFW_event event);
+RGFW_event* RGFW_eventQueuePop(RGFW_window* win);
+
+/* for C++ / C89 */
+#define RGFW_eventQueuePushEx(eventInit) { RGFW_event e; eventInit; RGFW_eventQueuePush(e); }
+
+/*!
+	key codes and mouse icon enums
+*/
+#undef RGFW_key
+typedef RGFW_ENUM(u8, RGFW_key) {
+	RGFW_keyNULL = 0,
+	RGFW_escape = '\033',
+	RGFW_backtick = '`',
+	RGFW_0 = '0',
+	RGFW_1 = '1',
+	RGFW_2 = '2',
+	RGFW_3 = '3',
+	RGFW_4 = '4',
+	RGFW_5 = '5',
+	RGFW_6 = '6',
+	RGFW_7 = '7',
+	RGFW_8 = '8',
+	RGFW_9 = '9',
+
+	RGFW_minus = '-',
+	RGFW_equals = '=',
+	RGFW_backSpace = '\b',
+	RGFW_tab = '\t',
+	RGFW_space = ' ',
+
+	RGFW_a = 'a',
+	RGFW_b = 'b',
+	RGFW_c = 'c',
+	RGFW_d = 'd',
+	RGFW_e = 'e',
+	RGFW_f = 'f',
+	RGFW_g = 'g',
+	RGFW_h = 'h',
+	RGFW_i = 'i',
+	RGFW_j = 'j',
+	RGFW_k = 'k',
+	RGFW_l = 'l',
+	RGFW_m = 'm',
+	RGFW_n = 'n',
+	RGFW_o = 'o',
+	RGFW_p = 'p',
+	RGFW_q = 'q',
+	RGFW_r = 'r',
+	RGFW_s = 's',
+	RGFW_t = 't',
+	RGFW_u = 'u',
+	RGFW_v = 'v',
+	RGFW_w = 'w',
+	RGFW_x = 'x',
+	RGFW_y = 'y',
+	RGFW_z = 'z',
+
+	RGFW_period = '.',
+	RGFW_comma = ',',
+	RGFW_slash = '/',
+	RGFW_bracket = '[',
+    RGFW_closeBracket = ']',   
+    RGFW_semicolon = ';',
+	RGFW_apostrophe = '\'',
+	RGFW_backSlash = '\\',
+	RGFW_return = '\n',
+	RGFW_enter = RGFW_return,
+
+	RGFW_delete = '\177', /* 127 */
+
+	RGFW_F1,
+	RGFW_F2,
+	RGFW_F3,
+	RGFW_F4,
+	RGFW_F5,
+	RGFW_F6,
+	RGFW_F7,
+	RGFW_F8,
+	RGFW_F9,
+	RGFW_F10,
+	RGFW_F11,
+	RGFW_F12,
+
+	RGFW_capsLock,
+	RGFW_shiftL,
+	RGFW_controlL,
+	RGFW_altL,
+	RGFW_superL,
+	RGFW_shiftR,
+	RGFW_controlR,
+	RGFW_altR,
+	RGFW_superR,
+	RGFW_up,
+	RGFW_down,
+	RGFW_left,
+	RGFW_right,
+	RGFW_insert,
+	RGFW_end,
+	RGFW_home,
+	RGFW_pageUp,
+	RGFW_pageDown,
+
+	RGFW_numLock,
+	RGFW_KP_Slash,
+	RGFW_multiply,
+	RGFW_KP_Minus,
+	RGFW_KP_1,
+	RGFW_KP_2,
+	RGFW_KP_3,
+	RGFW_KP_4,
+	RGFW_KP_5,
+	RGFW_KP_6,
+	RGFW_KP_7,
+	RGFW_KP_8,
+	RGFW_KP_9,
+	RGFW_KP_0,
+	RGFW_KP_Period,
+	RGFW_KP_Return,
+	RGFW_scrollLock,
+    RGFW_printScreen,
+    RGFW_pause,
+    RGFW_keyLast = 256 /* padding for alignment ~(175 by default) */
+ };
+
+
+/*! converts api keycode to the RGFW unmapped/physical key */
+RGFWDEF u32 RGFW_apiKeyToRGFW(u32 keycode);
+/*! converts RGFW keycode to the unmapped/physical api key */
+RGFWDEF u32 RGFW_rgfwToApiKey(u32 keycode);
+/*! converts RGFW keycode to the mapped keychar */
+RGFWDEF u8 RGFW_rgfwToKeyChar(u32 keycode);
+
+typedef RGFW_ENUM(u8, RGFW_mouseIcons) {
+	RGFW_mouseNormal = 0,
+	RGFW_mouseArrow,
+	RGFW_mouseIbeam,
+	RGFW_mouseCrosshair,
+	RGFW_mousePointingHand,
+	RGFW_mouseResizeEW,
+	RGFW_mouseResizeNS,
+	RGFW_mouseResizeNWSE,
+	RGFW_mouseResizeNESW,
+	RGFW_mouseResizeAll,
+	RGFW_mouseNotAllowed,
+    RGFW_mouseIconFinal = 16 /* padding for alignment */
+};
+/** @} */
+
+#endif /* RGFW_HEADER */
+#if defined(RGFW_X11) || defined(RGFW_WAYLAND)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h) l
+#elif defined(RGFW_WINDOWS)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h) w
+#elif defined(RGFW_MACOS)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h) m
+#elif defined(RGFW_WASM)
+	#define RGFW_OS_BASED_VALUE(l, w, m, h) h
+#endif
+
+
+#ifdef RGFW_IMPLEMENTATION
+RGFW_bool RGFW_useWaylandBool = 1;
+void RGFW_useWayland(RGFW_bool wayland) { RGFW_useWaylandBool = wayland;  }
+RGFW_bool RGFW_usingWayland(void) { return RGFW_useWaylandBool; }
+
+#if !defined(RGFW_NO_X11) && defined(RGFW_WAYLAND)
+#define RGFW_GOTO_WAYLAND(fallback) if (RGFW_useWaylandBool && fallback == 0) goto wayland
+#define RGFW_WAYLAND_LABEL wayland:; 
+#else
+#define RGFW_GOTO_WAYLAND(fallback)
+#define RGFW_WAYLAND_LABEL 
+#endif
+
+char* RGFW_clipboard_data;
+void RGFW_clipboard_switch(char* newstr);
+void RGFW_clipboard_switch(char* newstr) {
+	if (RGFW_clipboard_data != NULL)
+		RGFW_FREE(RGFW_clipboard_data);
+	RGFW_clipboard_data =  newstr;
+}
+
+#define RGFW_CHECK_CLIPBOARD() \
+	if (size <= 0 && RGFW_clipboard_data != NULL) \
+		return (const char*)RGFW_clipboard_data; \
+	else if (size <= 0) \
+		return "\0";
+
+const char* RGFW_readClipboard(size_t* len) {
+	RGFW_ssize_t size = RGFW_readClipboardPtr(NULL, 0);
+    RGFW_CHECK_CLIPBOARD();
+    char* str = (char*)RGFW_ALLOC((size_t)size);
+    RGFW_ASSERT(str != NULL);
+    str[0] = '\0';
+
+    size = RGFW_readClipboardPtr(str, (size_t)size);
+
+    RGFW_CHECK_CLIPBOARD();
+
+	if (len != NULL) *len = (size_t)size;
+
+	RGFW_clipboard_switch(str);
+	return (const char*)str;
+}
+
+RGFW_debugfunc RGFW_debugCallback = NULL;
+RGFW_debugfunc RGFW_setDebugCallback(RGFW_debugfunc func) {
+	RGFW_debugfunc RGFW_debugCallbackPrev = RGFW_debugCallback;
+	RGFW_debugCallback = func;
+	return RGFW_debugCallbackPrev;
+}
+
+#ifdef RGFW_DEBUG
+#include <stdio.h>
+#endif
+
+void RGFW_sendDebugInfo(RGFW_debugType type, RGFW_errorCode err, RGFW_debugContext ctx, const char* msg) {
+	if (RGFW_debugCallback) RGFW_debugCallback(type, err, ctx, msg);
+	#ifdef RGFW_DEBUG
+	switch (type) {
+		case RGFW_typeInfo: printf("RGFW INFO (%i %i): %s", type, err, msg); break;
+		case RGFW_typeError: printf("RGFW DEBUG (%i %i): %s", type, err, msg); break;
+		case RGFW_typeWarning: printf("RGFW WARNING (%i %i): %s", type, err, msg); break;
+		default: break;
+	}
+
+	switch (err) {
+		#ifdef RGFW_BUFFER
+		case RGFW_errBuffer: case RGFW_infoBuffer: printf(" buffer size: %i %i\n", ctx.win->bufferSize.w, ctx.win->bufferSize.h); break;
+		#endif
+		case RGFW_infoMonitor: printf(": scale (%s):\n   rect: {%i, %i, %i, %i}\n   physical size:%f %f\n   scale: %f %f\n   pixelRatio: %f\n   refreshRate: %i\n   depth: %i\n", ctx.monitor->name, ctx.monitor->x, ctx.monitor->y, ctx.monitor->mode.area.w, ctx.monitor->mode.area.h, ctx.monitor->physW, ctx.monitor->physH, ctx.monitor->scaleX, ctx.monitor->scaleY, ctx.monitor->pixelRatio, ctx.monitor->mode.refreshRate, ctx.monitor->mode.red + ctx.monitor->mode.green + ctx.monitor->mode.blue); break;
+		case RGFW_infoWindow: printf("  with rect of {%i, %i, %i, %i} \n", ctx.win->r.x, ctx.win->r.y,ctx. win->r.w, ctx.win->r.h); break;
+		case RGFW_errDirectXContext: printf(" srcError %i\n", ctx.srcError); break;
+		default: printf("\n");
+	}
+	#endif
+}
+
+u64 RGFW_timerOffset = 0;
+void RGFW_setTime(double time) {
+    RGFW_timerOffset = RGFW_getTimerValue() - (u64)(time * (double)RGFW_getTimerFreq());
+}
+
+double RGFW_getTime(void) {
+	return (double) ((double)(RGFW_getTimerValue() - RGFW_timerOffset) / (double)RGFW_getTimerFreq());
+}
+
+u64 RGFW_getTimeNS(void) {
+	return (u64)(((double)((RGFW_getTimerValue() - RGFW_timerOffset)) * 1e9) / (double)RGFW_getTimerFreq());
+}
+
+/*
+RGFW_IMPLEMENTATION starts with generic RGFW defines
+
+This is the start of keycode data
+*/
+
+
+
+/*
+	the c++ compiler doesn't support setting up an array like,
+	we'll have to do it during runtime using a function & this messy setup
+*/
+
+#ifndef RGFW_CUSTOM_BACKEND
+
+#if !defined(__cplusplus) && !defined(RGFW_C89)
+#define RGFW_NEXT ,
+#define RGFW_MAP
+#else
+#define RGFW_NEXT ;
+#define RGFW_MAP RGFW_keycodes
+#endif
+
+u32 RGFW_apiKeycodes[RGFW_keyLast] = { 0 };
+
+u8 RGFW_keycodes [RGFW_OS_BASED_VALUE(256, 512, 128, 256)] = {
+#if defined(__cplusplus) || defined(RGFW_C89)
+	0
+};
+void RGFW_init_keys(void);
+void RGFW_init_keys(void) {
+#endif
+	RGFW_MAP [RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] = RGFW_backtick 		RGFW_NEXT
+
+	RGFW_MAP [RGFW_OS_BASED_VALUE(19, 0x00B, 29, DOM_VK_0)] = RGFW_0 					RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(10, 0x002, 18, DOM_VK_1)] = RGFW_1						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(11, 0x003, 19, DOM_VK_2)] = RGFW_2						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(12, 0x004, 20, DOM_VK_3)] = RGFW_3						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(13, 0x005, 21, DOM_VK_4)] = RGFW_4						RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(14, 0x006, 23, DOM_VK_5)] = RGFW_5                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(15, 0x007, 22, DOM_VK_6)] = RGFW_6                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(16, 0x008, 26, DOM_VK_7)] = RGFW_7                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(17, 0x009, 28, DOM_VK_8)] = RGFW_8                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(18, 0x00A, 25, DOM_VK_9)] = RGFW_9,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(65, 0x039, 49, DOM_VK_SPACE)] = RGFW_space,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(38, 0x01E, 0, DOM_VK_A)] = RGFW_a                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(56, 0x030, 11, DOM_VK_B)] = RGFW_b                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(54, 0x02E, 8, DOM_VK_C)] = RGFW_c                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(40, 0x020, 2, DOM_VK_D)] = RGFW_d                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(26, 0x012, 14, DOM_VK_E)] = RGFW_e                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(41, 0x021, 3, DOM_VK_F)] = RGFW_f                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(42, 0x022, 5, DOM_VK_G)] = RGFW_g                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(43, 0x023, 4, DOM_VK_H)] = RGFW_h                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(31, 0x017, 34, DOM_VK_I)] = RGFW_i                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(44, 0x024, 38, DOM_VK_J)] = RGFW_j                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(45, 0x025, 40, DOM_VK_K)] = RGFW_k                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(46, 0x026, 37, DOM_VK_L)] = RGFW_l                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(58, 0x032, 46, DOM_VK_M)] = RGFW_m                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(57, 0x031, 45, DOM_VK_N)] = RGFW_n                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(32, 0x018, 31, DOM_VK_O)] = RGFW_o                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(33, 0x019, 35, DOM_VK_P)] = RGFW_p                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(24, 0x010, 12, DOM_VK_Q)] = RGFW_q                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(27, 0x013, 15, DOM_VK_R)] = RGFW_r                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(39, 0x01F, 1, DOM_VK_S)] = RGFW_s                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(28, 0x014, 17, DOM_VK_T)] = RGFW_t                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(30, 0x016, 32, DOM_VK_U)] = RGFW_u                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(55, 0x02F, 9, DOM_VK_V)] = RGFW_v                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(25, 0x011, 13, DOM_VK_W)] = RGFW_w                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(53, 0x02D, 7, DOM_VK_X)] = RGFW_x                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(29, 0x015, 16, DOM_VK_Y)] = RGFW_y                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(52, 0x02C, 6, DOM_VK_Z)] = RGFW_z,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(60, 0x034, 47, DOM_VK_PERIOD)] = RGFW_period             			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(59, 0x033, 43, DOM_VK_COMMA)] = RGFW_comma               			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(61, 0x035, 44, DOM_VK_SLASH)] = RGFW_slash               			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(34, 0x01A, 33, DOM_VK_OPEN_BRACKET)] = RGFW_bracket      			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(35, 0x01B, 30, DOM_VK_CLOSE_BRACKET)] = RGFW_closeBracket             RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(47, 0x027, 41, DOM_VK_SEMICOLON)] = RGFW_semicolon                 RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(48, 0x028, 39, DOM_VK_QUOTE)] = RGFW_apostrophe                 			RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(51, 0x02B, 42, DOM_VK_BACK_SLASH)] = RGFW_backSlash,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(36, 0x01C, 36, DOM_VK_RETURN)] = RGFW_return              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(119, 0x153, 118, DOM_VK_DELETE)] = RGFW_delete                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(77, 0x145, 72, DOM_VK_NUM_LOCK)] = RGFW_numLock               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(106, 0x135, 82, DOM_VK_DIVIDE)] = RGFW_KP_Slash               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(63, 0x037, 76, DOM_VK_MULTIPLY)] = RGFW_multiply              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(82, 0x04A, 67, DOM_VK_SUBTRACT)] = RGFW_KP_Minus              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(87, 0x04F, 84, DOM_VK_NUMPAD1)] = RGFW_KP_1               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(88, 0x050, 85, DOM_VK_NUMPAD2)] = RGFW_KP_2               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(89, 0x051, 86, DOM_VK_NUMPAD3)] = RGFW_KP_3               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(83, 0x04B, 87, DOM_VK_NUMPAD4)] = RGFW_KP_4               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(84, 0x04C, 88, DOM_VK_NUMPAD5)] = RGFW_KP_5               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(85, 0x04D, 89, DOM_VK_NUMPAD6)] = RGFW_KP_6               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(79, 0x047, 90, DOM_VK_NUMPAD7)] = RGFW_KP_7               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(80, 0x048, 92, DOM_VK_NUMPAD8)] = RGFW_KP_8               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(81, 0x049, 93, DOM_VK_NUMPAD9)] = RGFW_KP_9               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(90, 0x052, 83, DOM_VK_NUMPAD0)] = RGFW_KP_0               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(91, 0x053, 65, DOM_VK_DECIMAL)] = RGFW_KP_Period              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(104, 0x11C, 77, 0)] = RGFW_KP_Return,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(20, 0x00C, 27, DOM_VK_HYPHEN_MINUS)] = RGFW_minus              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(21, 0x00D, 24, DOM_VK_EQUALS)] = RGFW_equals               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(22, 0x00E, 51, DOM_VK_BACK_SPACE)] = RGFW_backSpace              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(23, 0x00F, 48, DOM_VK_TAB)] = RGFW_tab                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(66, 0x03A, 57, DOM_VK_CAPS_LOCK)] = RGFW_capsLock               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(50, 0x02A, 56, DOM_VK_SHIFT)] = RGFW_shiftL               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(37, 0x01D, 59, DOM_VK_CONTROL)] = RGFW_controlL               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(64, 0x038, 58, DOM_VK_ALT)] = RGFW_altL                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(133, 0x15B, 55, DOM_VK_WIN)] = RGFW_superL,
+	#if !defined(RGFW_MACOS) && !defined(RGFW_WASM)
+	RGFW_MAP [RGFW_OS_BASED_VALUE(105, 0x11D, 59, 0)] = RGFW_controlR               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(134, 0x15C, 55, 0)] = RGFW_superR,
+	RGFW_MAP [RGFW_OS_BASED_VALUE(62, 0x036, 56, 0)] = RGFW_shiftR              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(108, 0x138, 58, 0)] = RGFW_altR,
+	#endif
+	RGFW_MAP [RGFW_OS_BASED_VALUE(67, 0x03B, 127, DOM_VK_F1)] = RGFW_F1                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(68, 0x03C, 121, DOM_VK_F2)] = RGFW_F2                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(69, 0x03D, 100, DOM_VK_F3)] = RGFW_F3                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(70, 0x03E, 119, DOM_VK_F4)] = RGFW_F4                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(71, 0x03F, 97, DOM_VK_F5)] = RGFW_F5              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(72, 0x040, 98, DOM_VK_F6)] = RGFW_F6              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(73, 0x041, 99, DOM_VK_F7)] = RGFW_F7              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(74, 0x042, 101, DOM_VK_F8)] = RGFW_F8                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(75, 0x043, 102, DOM_VK_F9)] = RGFW_F9                 		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(76, 0x044, 110, DOM_VK_F10)] = RGFW_F10               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(95, 0x057, 104, DOM_VK_F11)] = RGFW_F11               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(96, 0x058, 111, DOM_VK_F12)] = RGFW_F12               RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(111, 0x148, 126, DOM_VK_UP)] = RGFW_up                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(116, 0x150, 125, DOM_VK_DOWN)] = RGFW_down                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(113, 0x14B, 123, DOM_VK_LEFT)] = RGFW_left                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(114, 0x14D, 124, DOM_VK_RIGHT)] = RGFW_right              RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(118, 0x152, 115, DOM_VK_INSERT)] = RGFW_insert                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(115, 0x14F, 120, DOM_VK_END)] = RGFW_end                  		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(112, 0x149, 117, DOM_VK_PAGE_UP)] = RGFW_pageUp                		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(117, 0x151, 122, DOM_VK_PAGE_DOWN)] = RGFW_pageDown            RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(9, 0x001, 53, DOM_VK_ESCAPE)] = RGFW_escape                   		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(110, 0x147, 116, DOM_VK_HOME)] = RGFW_home                    		RGFW_NEXT
+	RGFW_MAP [RGFW_OS_BASED_VALUE(78, 0x046, 107, DOM_VK_SCROLL_LOCK)] = RGFW_scrollLock               RGFW_NEXT
+    RGFW_MAP [RGFW_OS_BASED_VALUE(107, 0x137, 105, DOM_VK_PRINTSCREEN)] = RGFW_printScreen              RGFW_NEXT
+    RGFW_MAP [RGFW_OS_BASED_VALUE(128, 0x045, 113, DOM_VK_PAUSE)] = RGFW_pause               RGFW_NEXT
+#if defined(__cplusplus) || defined(RGFW_C89)
+}
+#else
+};
+#endif
+
+#undef RGFW_NEXT
+#undef RGFW_MAP
+
+u32 RGFW_apiKeyToRGFW(u32 keycode) {
+    #if defined(__cplusplus) || defined(RGFW_C89)
+	if (RGFW_keycodes[RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)] != RGFW_backtick) {
+		RGFW_init_keys();
+	}
+	#endif
+
+	/* make sure the key isn't out of bounds */
+	if (keycode > sizeof(RGFW_keycodes) / sizeof(u8))
+		return 0;
+
+	return RGFW_keycodes[keycode];
+}
+
+u32 RGFW_rgfwToApiKey(u32 keycode) {
+	if (RGFW_apiKeycodes[RGFW_backtick] != RGFW_OS_BASED_VALUE(49, 0x029, 50, DOM_VK_BACK_QUOTE)) {
+        for (u32 i = 0; i < RGFW_keyLast; i++) {
+            for (u32 y = 0; y < sizeof(RGFW_keycodes); y++) {
+                if (RGFW_keycodes[y] == i) {
+                    RGFW_apiKeycodes[i] = y;
+                    break;
+                }
+            }
+        }
+    }
+
+	/* make sure the key isn't out of bounds */
+	if (keycode > sizeof(RGFW_apiKeycodes) / sizeof(u32))
+		return 0;
+
+	return RGFW_apiKeycodes[keycode];
+}
+#endif /* RGFW_CUSTOM_BACKEND */
+
+typedef struct {
+	RGFW_bool current  : 1;
+	RGFW_bool prev  : 1;
+} RGFW_keyState;
+
+RGFW_keyState RGFW_keyboard[RGFW_keyLast] = { {0, 0} };
+
+RGFWDEF void RGFW_resetKeyPrev(void);
+void RGFW_resetKeyPrev(void) {
+	size_t i; /*!< reset each previous state  */
+    for (i = 0; i < RGFW_keyLast; i++) RGFW_keyboard[i].prev = 0;
+}
+RGFWDEF void RGFW_resetKey(void);
+void RGFW_resetKey(void) { RGFW_MEMSET(RGFW_keyboard, 0, sizeof(RGFW_keyboard)); }
+/*
+	this is the end of keycode data
+*/
+
+/* gamepad data */
+RGFW_keyState RGFW_gamepadPressed[4][32]; /*!< if a key is currently pressed or not (per gamepad) */
+RGFW_point RGFW_gamepadAxes[4][4]; /*!< if a key is currently pressed or not (per gamepad) */
+
+RGFW_gamepadType RGFW_gamepads_type[4]; /*!< if a key is currently pressed or not (per gamepad) */
+i32 RGFW_gamepads[4] = {0, 0, 0, 0}; /*!< limit of 4 gamepads at a time */
+char RGFW_gamepads_name[4][128]; /*!< gamepad names */
+u16 RGFW_gamepadCount = 0; /*!< the actual amount of gamepads */
+
+/*
+	event callback defines start here
+*/
+
+
+/*
+	These exist to avoid the
+	if (func == NULL) check
+	for (allegedly) better performance
+
+	RGFW_EMPTY_DEF exists to prevent the missing-prototypes warning
+*/
+static void RGFW_windowMovedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+static void RGFW_windowResizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+static void RGFW_windowRestoredfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+static void RGFW_windowMinimizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+static void RGFW_windowMaximizedfuncEMPTY(RGFW_window* win, RGFW_rect r) { RGFW_UNUSED(win); RGFW_UNUSED(r); }
+static void RGFW_windowQuitfuncEMPTY(RGFW_window* win) { RGFW_UNUSED(win); }
+static void RGFW_focusfuncEMPTY(RGFW_window* win, RGFW_bool inFocus) {RGFW_UNUSED(win); RGFW_UNUSED(inFocus);}
+static void RGFW_mouseNotifyfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_bool status) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(status);}
+static void RGFW_mousePosfuncEMPTY(RGFW_window* win, RGFW_point point, RGFW_point vector) {RGFW_UNUSED(win); RGFW_UNUSED(point); RGFW_UNUSED(vector);}
+static void RGFW_dndInitfuncEMPTY(RGFW_window* win, RGFW_point point) {RGFW_UNUSED(win); RGFW_UNUSED(point);}
+static void RGFW_windowRefreshfuncEMPTY(RGFW_window* win) {RGFW_UNUSED(win); }
+static void RGFW_keyfuncEMPTY(RGFW_window* win, RGFW_key key, u8 keyChar, RGFW_keymod keyMod, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(key); RGFW_UNUSED(keyChar); RGFW_UNUSED(keyMod); RGFW_UNUSED(pressed);}
+static void RGFW_mouseButtonfuncEMPTY(RGFW_window* win, RGFW_mouseButton button, double scroll, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(button); RGFW_UNUSED(scroll); RGFW_UNUSED(pressed);}
+static void RGFW_gamepadButtonfuncEMPTY(RGFW_window* win, u16 gamepad, u8 button, RGFW_bool pressed) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(button); RGFW_UNUSED(pressed); }
+static void RGFW_gamepadAxisfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_point axis[2], u8 axisesCount, u8 whichAxis) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(axis); RGFW_UNUSED(axisesCount); RGFW_UNUSED(whichAxis); }
+static void RGFW_gamepadfuncEMPTY(RGFW_window* win, u16 gamepad, RGFW_bool connected) {RGFW_UNUSED(win); RGFW_UNUSED(gamepad); RGFW_UNUSED(connected);}
+static void RGFW_dndfuncEMPTY(RGFW_window* win, char** droppedFiles, size_t droppedFilesCount) {RGFW_UNUSED(win); RGFW_UNUSED(droppedFiles); RGFW_UNUSED(droppedFilesCount);}
+static void RGFW_scaleUpdatedfuncEMPTY(RGFW_window* win, float scaleX, float scaleY) {RGFW_UNUSED(win); RGFW_UNUSED(scaleX); RGFW_UNUSED(scaleY); }
+
+#define RGFW_CALLBACK_DEFINE(x, x2) \
+RGFW_##x##func RGFW_##x##Callback = RGFW_##x##funcEMPTY; \
+RGFW_##x##func RGFW_set##x2##Callback(RGFW_##x##func func) { \
+    RGFW_##x##func prev = RGFW_##x##Callback; \
+    RGFW_##x##Callback = func; \
+    return prev; \
+}
+RGFW_CALLBACK_DEFINE(windowMaximized, WindowMaximized)
+RGFW_CALLBACK_DEFINE(windowMinimized, WindowMinimized)
+RGFW_CALLBACK_DEFINE(windowRestored, WindowRestored)
+RGFW_CALLBACK_DEFINE(windowMoved, WindowMoved)
+RGFW_CALLBACK_DEFINE(windowResized, WindowResized)
+RGFW_CALLBACK_DEFINE(windowQuit, WindowQuit)
+RGFW_CALLBACK_DEFINE(mousePos, MousePos)
+RGFW_CALLBACK_DEFINE(windowRefresh, WindowRefresh)
+RGFW_CALLBACK_DEFINE(focus, Focus)
+RGFW_CALLBACK_DEFINE(mouseNotify, MouseNotify)
+RGFW_CALLBACK_DEFINE(dnd, Dnd)
+RGFW_CALLBACK_DEFINE(dndInit, DndInit)
+RGFW_CALLBACK_DEFINE(key, Key)
+RGFW_CALLBACK_DEFINE(mouseButton, MouseButton)
+RGFW_CALLBACK_DEFINE(gamepadButton, GamepadButton)
+RGFW_CALLBACK_DEFINE(gamepadAxis, GamepadAxis)
+RGFW_CALLBACK_DEFINE(gamepad, Gamepad)
+RGFW_CALLBACK_DEFINE(scaleUpdated, ScaleUpdated)
+#undef RGFW_CALLBACK_DEFINE
+
+void RGFW_window_checkEvents(RGFW_window* win, i32 waitMS) {
+	RGFW_window_eventWait(win, waitMS);
+
+	while (RGFW_window_checkEvent(win) != NULL && RGFW_window_shouldClose(win) == 0) {
+		if (win->event.type == RGFW_quit) return;
+	}
+
+	#ifdef RGFW_WASM /* WASM needs to run the sleep function for asyncify */
+		RGFW_sleep(0);
+	#endif
+}
+
+void RGFW_window_checkMode(RGFW_window* win);
+void RGFW_window_checkMode(RGFW_window* win) {
+	if (RGFW_window_isMinimized(win)) {
+		win->_flags |= RGFW_windowMinimize;
+		RGFW_windowMinimizedCallback(win, win->r);
+	} else if (RGFW_window_isMaximized(win)) {
+		win->_flags |= RGFW_windowMaximize;
+		RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win);
+		RGFW_windowMaximizedCallback(win, win->r);
+	} else if (((win->_flags & RGFW_windowMinimize) && !RGFW_window_isMaximized(win)) ||
+				(win->_flags & RGFW_windowMaximize && !RGFW_window_isMaximized(win))) {
+		win->_flags &= ~(u32)RGFW_windowMinimize;
+		if (RGFW_window_isMaximized(win) == RGFW_FALSE) win->_flags &= ~(u32)RGFW_windowMaximize;
+		RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
+		RGFW_windowRestoredCallback(win, win->r);
+	}
+}
+
+/*
+no more event call back defines
+*/
+
+#define SET_ATTRIB(a, v) { \
+    RGFW_ASSERT(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
+    attribs[index++] = a; \
+    attribs[index++] = v; \
+}
+
+#define RGFW_EVENT_PASSED 		RGFW_BIT(24) /* if a queued event was passed */
+#define RGFW_EVENT_QUIT 		RGFW_BIT(25) /* the window close button was pressed */
+#define RGFW_HOLD_MOUSE			RGFW_BIT(26) /*!< hold the moues still */
+#define RGFW_MOUSE_LEFT 		RGFW_BIT(27) /* if mouse left the window */
+#define RGFW_WINDOW_ALLOC 		RGFW_BIT(28) /* if window was allocated by RGFW */
+#define RGFW_BUFFER_ALLOC 		RGFW_BIT(29) /* if window.buffer was allocated by RGFW */
+#define RGFW_WINDOW_INIT 		RGFW_BIT(30) /* if window.buffer was allocated by RGFW */
+#define RGFW_INTERNAL_FLAGS (RGFW_EVENT_QUIT | RGFW_EVENT_PASSED | RGFW_HOLD_MOUSE |  RGFW_MOUSE_LEFT | RGFW_WINDOW_ALLOC | RGFW_BUFFER_ALLOC | RGFW_windowFocus)
+
+RGFW_window* RGFW_createWindow(const char* name, RGFW_rect rect, RGFW_windowFlags flags) {
+	RGFW_window* win = (RGFW_window*)RGFW_ALLOC(sizeof(RGFW_window));
+	RGFW_ASSERT(win != NULL);
+    win->_flags = RGFW_WINDOW_ALLOC;
+    return RGFW_createWindowPtr(name, rect, flags, win);
+}
+
+#if defined(RGFW_USE_XDL) && defined(RGFW_X11)
+	#define XDL_IMPLEMENTATION
+	#include "XDL.h"
+#endif
+
+#define RGFW_MAX_EVENTS 32
+typedef struct RGFW_globalStruct {
+    RGFW_window* root;
+    RGFW_window* current;
+    i32 windowCount;
+    i32 eventLen;
+    i32 eventIndex;
+
+    #ifdef RGFW_X11
+        Display* display;
+        Window helperWindow;
+    	char* clipboard; /* for writing to the clipboard selection */
+	    size_t clipboard_len;
+    #endif
+    #ifdef RGFW_WAYLAND
+	    struct wl_display* wl_display;
+    #endif
+    #if defined(RGFW_X11) || defined(RGFW_WINDOWS) || defined(RGFW_WAYLAND)
+        RGFW_mouse* hiddenMouse;
+    #endif
+    RGFW_event events[RGFW_MAX_EVENTS];
+
+} RGFW_globalStruct;
+#if !defined(RGFW_C89) && !defined(__cplusplus)
+RGFW_globalStruct _RGFW = {.root = NULL, .current = NULL, .windowCount = -1, .eventLen = 0, .eventIndex = 0};
+#define _RGFW_init RGFW_TRUE
+#else
+RGFW_bool _RGFW_init = RGFW_FALSE;
+RGFW_globalStruct _RGFW;
+#endif
+
+void RGFW_eventQueuePush(RGFW_event event) {
+	if (_RGFW.eventLen >= RGFW_MAX_EVENTS) return;
+	_RGFW.events[_RGFW.eventLen] = event;
+    _RGFW.eventLen++;
+}
+
+RGFW_event* RGFW_eventQueuePop(RGFW_window* win) {
+	RGFW_event* ev; 
+    if (_RGFW.eventLen == 0) return NULL;
+
+	ev = (RGFW_event*)&_RGFW.events[_RGFW.eventIndex];
+
+    _RGFW.eventLen--;
+    if (_RGFW.eventLen >= 0 && _RGFW.eventIndex < _RGFW.eventLen) {
+		_RGFW.eventIndex++;
+    } else if (_RGFW.eventLen == 0) {
+        _RGFW.eventIndex = 0;
+    }
+
+	if (ev->_win != win && ev->_win != NULL) {
+        RGFW_eventQueuePush(*ev);
+        return NULL;
+	}
+
+    ev->droppedFilesCount = win->event.droppedFilesCount;
+	ev->droppedFiles = win->event.droppedFiles;
+	return ev;
+}
+
+RGFW_event* RGFW_window_checkEventCore(RGFW_window* win);
+RGFW_event* RGFW_window_checkEventCore(RGFW_window* win) {
+	RGFW_event* ev;
+    RGFW_ASSERT(win != NULL);
+    if (win->event.type == 0 && _RGFW.eventLen == 0)
+		RGFW_resetKeyPrev();
+
+	if (win->event.type == RGFW_quit && win->_flags & RGFW_windowFreeOnClose) {
+        static RGFW_event event;
+        event = win->event;
+        RGFW_window_close(win);
+	    return &event;
+    }
+
+	if (win->event.type != RGFW_DNDInit) win->event.type = 0;
+
+	/* check queued events */
+	ev = RGFW_eventQueuePop(win);
+	if (ev != NULL) {
+		if (ev->type == RGFW_quit) RGFW_window_setShouldClose(win, RGFW_TRUE);
+		win->event = *ev;
+    }
+	else return NULL;
+
+	return &win->event;
+}
+
+
+RGFWDEF void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags);
+void RGFW_setRootWindow(RGFW_window* win) { _RGFW.root = win; }
+RGFW_window* RGFW_getRootWindow(void) { return _RGFW.root; }
+
+/* do a basic initialization for RGFW_window, this is to standard it for each OS */
+void RGFW_window_basic_init(RGFW_window* win, RGFW_rect rect, RGFW_windowFlags flags) {
+	RGFW_UNUSED(flags);
+    if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init();
+    _RGFW.windowCount++;
+
+    /* rect based the requested flags */
+	if (_RGFW.root == NULL) {
+		RGFW_setRootWindow(win);
+		RGFW_setTime(0);
+	}
+
+	if (!(win->_flags & RGFW_WINDOW_ALLOC)) win->_flags = 0;
+
+	/* set and init the new window's data */
+	win->r = rect;
+	win->exitKey = RGFW_escape;
+	win->event.droppedFilesCount = 0;
+
+	win->_flags = 0 | (win->_flags & RGFW_WINDOW_ALLOC);
+    win->_flags |= flags;
+	win->event.keyMod = 0;
+	win->_lastMousePoint.x = 0; 
+	win->_lastMousePoint.y = 0; 
+
+	win->event.droppedFiles = (char**)RGFW_ALLOC(RGFW_MAX_PATH * RGFW_MAX_DROPS);
+	RGFW_ASSERT(win->event.droppedFiles != NULL);
+     
+    { 
+        u32 i;
+        for (i = 0; i < RGFW_MAX_DROPS; i++)
+		    win->event.droppedFiles[i] = (char*)(win->event.droppedFiles + RGFW_MAX_DROPS + (i * RGFW_MAX_PATH));
+    }
+}
+
+void RGFW_window_setFlags(RGFW_window* win, RGFW_windowFlags flags) {
+	RGFW_windowFlags cmpFlags = win->_flags;
+	if (win->_flags & RGFW_WINDOW_INIT) cmpFlags = 0;
+
+	#ifndef RGFW_NO_MONITOR
+	if (flags & RGFW_windowScaleToMonitor)			RGFW_window_scaleToMonitor(win);
+	#endif
+
+	if (flags & RGFW_windowCenter)					RGFW_window_center(win);
+	if (flags & RGFW_windowCenterCursor)
+		RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
+	if (flags & RGFW_windowNoBorder)				RGFW_window_setBorder(win, 0);
+	else RGFW_window_setBorder(win, 1);
+	if (flags & RGFW_windowFullscreen)				RGFW_window_setFullscreen(win, RGFW_TRUE);
+	else if (cmpFlags & RGFW_windowFullscreen) 	RGFW_window_setFullscreen(win, 0);
+	if (flags & RGFW_windowMaximize)				RGFW_window_maximize(win);
+	else if (cmpFlags & RGFW_windowMaximize) 	RGFW_window_restore(win);
+	if (flags & RGFW_windowMinimize)				RGFW_window_minimize(win);
+	else if (cmpFlags & RGFW_windowMinimize) 	RGFW_window_restore(win);
+	if (flags & RGFW_windowHideMouse)				RGFW_window_showMouse(win, 0);
+	else if (cmpFlags & RGFW_windowHideMouse)  	RGFW_window_showMouse(win, 1);
+	if (flags & RGFW_windowHide)				RGFW_window_hide(win);
+	else if (cmpFlags & RGFW_windowHide)  		RGFW_window_show(win);
+	if (flags & RGFW_windowCocoaCHDirToRes)			RGFW_moveToMacOSResourceDir();
+	if (flags & RGFW_windowFloating)				RGFW_window_setFloating(win, 1);
+	else if (cmpFlags & RGFW_windowFloating)		RGFW_window_setFloating(win, 0);
+	if (flags & RGFW_windowFocus)					RGFW_window_focus(win);
+
+	if (flags & RGFW_windowNoResize) {
+	    RGFW_window_setMaxSize(win, RGFW_AREA(win->r.w, win->r.h));
+	    RGFW_window_setMinSize(win, RGFW_AREA(win->r.w, win->r.h));
+	} else if (cmpFlags & RGFW_windowNoResize) {
+		RGFW_window_setMaxSize(win, RGFW_AREA(0, 0));
+		RGFW_window_setMinSize(win, RGFW_AREA(0, 0));
+	}
+
+	win->_flags = flags | (win->_flags & RGFW_INTERNAL_FLAGS);
+}
+
+RGFW_bool RGFW_window_opengl_isSoftware(RGFW_window* win) {
+    return RGFW_BOOL(win->_flags |= RGFW_windowOpenglSoftware);
+}
+
+RGFW_bool RGFW_window_isInFocus(RGFW_window* win) {
+#ifdef RGFW_WASM
+    return RGFW_TRUE;
+#else
+    return RGFW_BOOL(win->_flags & RGFW_windowFocus);
+#endif
+}
+
+void RGFW_window_initBuffer(RGFW_window* win) {
+    RGFW_area area = RGFW_getScreenSize();
+    if ((win->_flags & RGFW_windowNoResize))
+        area = RGFW_AREA(win->r.w, win->r.h);
+
+    RGFW_window_initBufferSize(win, area);
+}
+
+void RGFW_window_initBufferSize(RGFW_window* win, RGFW_area area) {
+#if defined(RGFW_BUFFER) || defined(RGFW_OSMESA)
+    win->_flags |= RGFW_BUFFER_ALLOC;
+	#ifndef RGFW_WINDOWS
+        u8* buffer = (u8*)RGFW_ALLOC(area.w * area.h * 4);
+        RGFW_ASSERT(buffer != NULL);
+
+        RGFW_window_initBufferPtr(win, buffer, area);
+    #else /* windows's bitmap allocs memory for us */
+	RGFW_window_initBufferPtr(win, (u8*)NULL, area);
+	#endif
+#else
+    RGFW_UNUSED(win); RGFW_UNUSED(area);
+#endif
+}
+
+#ifdef RGFW_MACOS
+RGFWDEF void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer);
+RGFWDEF void* RGFW_cocoaGetLayer(void);
+#endif
+
+const char* RGFW_className = NULL;
+void RGFW_setClassName(const char* name) { RGFW_className = name; }
+
+#ifndef RGFW_X11
+void RGFW_setXInstName(const char* name) { RGFW_UNUSED(name); }
+#endif
+
+RGFW_keyState RGFW_mouseButtons[RGFW_mouseFinal] = {  {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+
+RGFW_bool RGFW_isMousePressed(RGFW_window* win, RGFW_mouseButton button) {
+	return RGFW_mouseButtons[button].current && (win == NULL || RGFW_window_isInFocus(win));
+}
+RGFW_bool RGFW_wasMousePressed(RGFW_window* win, RGFW_mouseButton button) {
+	return RGFW_mouseButtons[button].prev && (win != NULL || RGFW_window_isInFocus(win));
+}
+RGFW_bool RGFW_isMouseHeld(RGFW_window* win, RGFW_mouseButton button) {
+	return (RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
+}
+RGFW_bool RGFW_isMouseReleased(RGFW_window* win, RGFW_mouseButton button) {
+	return (!RGFW_isMousePressed(win, button) && RGFW_wasMousePressed(win, button));
+}
+
+RGFW_point RGFW_window_getMousePoint(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	return win->_lastMousePoint;
+}
+
+RGFW_bool RGFW_isPressed(RGFW_window* win, RGFW_key key) {
+	return RGFW_keyboard[key].current && (win == NULL || RGFW_window_isInFocus(win));
+}
+
+RGFW_bool RGFW_wasPressed(RGFW_window* win, RGFW_key key) {
+	return RGFW_keyboard[key].prev && (win == NULL || RGFW_window_isInFocus(win));
+}
+
+RGFW_bool RGFW_isHeld(RGFW_window* win, RGFW_key key) {
+	return (RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
+}
+
+RGFW_bool RGFW_isClicked(RGFW_window* win, RGFW_key key) {
+	return (RGFW_wasPressed(win, key) && !RGFW_isPressed(win, key));
+}
+
+RGFW_bool RGFW_isReleased(RGFW_window* win, RGFW_key key) {
+	return (!RGFW_isPressed(win, key) && RGFW_wasPressed(win, key));
+}
+
+void RGFW_window_makeCurrent(RGFW_window* win) {
+    _RGFW.current = win;
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
+    RGFW_window_makeCurrent_OpenGL(win);
+#endif
+}
+
+RGFW_window* RGFW_getCurrent(void) {
+    return _RGFW.current;
+}
+
+void RGFW_window_swapBuffers(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_window_swapBuffers_software(win);
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
+	RGFW_window_swapBuffers_OpenGL(win);
+#endif
+}
+
+RGFWDEF void RGFW_setBit(u32* data, u32 bit, RGFW_bool value);
+void RGFW_setBit(u32* data, u32 bit, RGFW_bool value) {
+	if (value)
+		*data |= bit;
+	else if (!value && (*(data) & bit))
+		*data ^= bit;
+}
+
+void RGFW_window_center(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_area screenR = RGFW_getScreenSize();
+	RGFW_window_move(win, RGFW_POINT((i32)(screenR.w - (u32)win->r.w) / 2, (screenR.h - (u32)win->r.h) / 2));
+}
+
+RGFW_bool RGFW_monitor_scaleToWindow(RGFW_monitor mon, RGFW_window* win) {
+	RGFW_monitorMode mode;
+    RGFW_ASSERT(win != NULL);
+
+	mode.area.w = (u32)win->r.w;
+	mode.area.h = (u32)win->r.h;
+	return RGFW_monitor_requestMode(mon, mode, RGFW_monitorScale);
+}
+
+void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode);
+void RGFW_splitBPP(u32 bpp, RGFW_monitorMode* mode) {
+    if (bpp == 32) bpp = 24;
+    mode->red = mode->green = mode->blue = (u8)(bpp / 3);
+
+    u32 delta = bpp - (mode->red * 3); /* handle leftovers */
+    if (delta >= 1) mode->green = mode->green + 1;
+    if (delta == 2) mode->red = mode->red + 1;
+}
+
+RGFW_bool RGFW_monitorModeCompare(RGFW_monitorMode mon, RGFW_monitorMode mon2, RGFW_modeRequest request) {
+	return (((mon.area.w == mon2.area.w && mon.area.h == mon2.area.h) || !(request & RGFW_monitorScale)) &&
+			((mon.refreshRate == mon2.refreshRate) || !(request & RGFW_monitorRefresh)) &&
+			((mon.red == mon2.red && mon.green == mon2.green && mon.blue == mon2.blue) || !(request & RGFW_monitorRGB)));
+}
+
+RGFW_bool RGFW_window_shouldClose(RGFW_window* win) {
+	return (win == NULL || (win->_flags & RGFW_EVENT_QUIT)|| (win->exitKey && RGFW_isPressed(win, win->exitKey)));
+}
+
+void RGFW_window_setShouldClose(RGFW_window* win, RGFW_bool shouldClose) {
+	if (shouldClose)  {
+		win->_flags |= RGFW_EVENT_QUIT;
+		RGFW_windowQuitCallback(win);
+	} else {
+		win->_flags &= ~(u32)RGFW_EVENT_QUIT;
+	}
+}
+
+#ifndef RGFW_NO_MONITOR
+void RGFW_window_scaleToMonitor(RGFW_window* win) {
+	RGFW_monitor monitor = RGFW_window_getMonitor(win);
+	if (monitor.scaleX == 0 && monitor.scaleY == 0)
+		return;
+
+	RGFW_window_resize(win, RGFW_AREA((u32)(monitor.scaleX * (float)win->r.w), (u32)(monitor.scaleY * (float)win->r.h)));
+}
+
+void RGFW_window_moveToMonitor(RGFW_window* win, RGFW_monitor m) {
+	RGFW_window_move(win, RGFW_POINT(m.x + win->r.x, m.y + win->r.y));
+}
+#endif
+
+RGFW_bool RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) {
+	return RGFW_window_setIconEx(win, icon, a, channels, RGFW_iconBoth);
+}
+
+RGFWDEF void RGFW_captureCursor(RGFW_window* win, RGFW_rect);
+RGFWDEF void RGFW_releaseCursor(RGFW_window* win);
+
+
+RGFW_bool RGFW_window_mouseHeld(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_HOLD_MOUSE); }
+
+void RGFW_window_mouseHold(RGFW_window* win, RGFW_area area) {
+	if (!area.w && !area.h)
+		area = RGFW_AREA(win->r.w / 2, win->r.h / 2);
+
+	win->_flags |= RGFW_HOLD_MOUSE;
+    RGFW_captureCursor(win, win->r);
+	RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
+}
+
+void RGFW_window_mouseUnhold(RGFW_window* win) {
+	win->_flags &= ~(u32)RGFW_HOLD_MOUSE;
+	RGFW_releaseCursor(win);
+}
+
+u32 RGFW_checkFPS(double startTime, u32 frameCount, u32 fpsCap) {
+	double deltaTime = RGFW_getTime() - startTime;
+	if (deltaTime == 0) return 0;
+
+	double fps = (frameCount / deltaTime); /* the numer of frames over the time it took for them to render */
+	if (fpsCap && fps > fpsCap) {
+		double frameTime = (double)frameCount / (double)fpsCap; /* how long it should take to finish the frames */
+		double sleepTime = frameTime - deltaTime; /* subtract how long it should have taken with how long it did take */
+
+		if (sleepTime > 0) RGFW_sleep((u32)(sleepTime * 1000));
+	}
+
+	return (u32) fps;
+}
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+void RGFW_RGB_to_BGR(RGFW_window* win, u8* data) {
+	#if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA)
+	u32 x, y;
+	for (y = 0; y < (u32)win->r.h; y++) {
+		for (x = 0; x < (u32)win->r.w; x++) {
+			u32 index = (y * 4 * win->bufferSize.w) + x * 4;
+
+			u8 red = data[index];
+			data[index] = win->buffer[index + 2];
+			data[index + 2] = red;
+		}
+	}
+    #elif defined(RGFW_OSMESA)
+	u32 y;
+	for(y = 0; y < (u32)win->r.h; y++){
+		u32 index_from = (y + (win->bufferSize.h - win->r.h)) * 4 * win->bufferSize.w;
+		u32 index_to = y * 4 * win->bufferSize.w;
+		memcpy(&data[index_to], &data[index_from], 4 * win->bufferSize.w);
+	}
+    #else
+	RGFW_UNUSED(win); RGFW_UNUSED(data);
+	#endif
+}
+#endif
+
+u32 RGFW_isPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) {
+	RGFW_UNUSED(win);
+	return RGFW_gamepadPressed[c][button].current;
+}
+u32 RGFW_wasPressedGamepad(RGFW_window* win, u8 c, RGFW_gamepadCodes button) {
+	RGFW_UNUSED(win);
+	return RGFW_gamepadPressed[c][button].prev;
+}
+u32 RGFW_isReleasedGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) {
+	RGFW_UNUSED(win);
+	return !RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button);
+}
+u32 RGFW_isHeldGamepad(RGFW_window* win, u8 controller, RGFW_gamepadCodes button) {
+	RGFW_UNUSED(win);
+	return RGFW_isPressedGamepad(win, controller, button) && RGFW_wasPressedGamepad(win, controller, button);
+}
+
+RGFW_point RGFW_getGamepadAxis(RGFW_window* win, u16 controller, u16 whichAxis) {
+	RGFW_UNUSED(win);
+	return RGFW_gamepadAxes[controller][whichAxis];
+}
+const char* RGFW_getGamepadName(RGFW_window* win, u16 controller) {
+	RGFW_UNUSED(win);
+	return (const char*)RGFW_gamepads_name[controller];
+}
+
+size_t RGFW_getGamepadCount(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	return RGFW_gamepadCount;
+}
+
+RGFW_gamepadType RGFW_getGamepadType(RGFW_window* win, u16 controller) {
+	RGFW_UNUSED(win);
+	return RGFW_gamepads_type[controller];
+}
+
+RGFWDEF void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value);
+void RGFW_updateKeyMod(RGFW_window* win, RGFW_keymod mod, RGFW_bool value) {
+	if (value) win->event.keyMod |= mod;
+	else win->event.keyMod &= ~mod;
+}
+
+RGFWDEF void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll);
+void RGFW_updateKeyModsPro(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+	RGFW_updateKeyMod(win, RGFW_modCapsLock, capital);
+	RGFW_updateKeyMod(win, RGFW_modNumLock, numlock);
+	RGFW_updateKeyMod(win, RGFW_modControl, control);
+	RGFW_updateKeyMod(win, RGFW_modAlt, alt);
+	RGFW_updateKeyMod(win, RGFW_modShift, shift);
+	RGFW_updateKeyMod(win, RGFW_modSuper, super);
+	RGFW_updateKeyMod(win, RGFW_modScrollLock, scroll);
+}
+
+RGFWDEF void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll);
+void RGFW_updateKeyMods(RGFW_window* win, RGFW_bool capital, RGFW_bool numlock, RGFW_bool scroll) {
+	RGFW_updateKeyModsPro(win, capital, numlock,
+					RGFW_isPressed(win, RGFW_controlL) || RGFW_isPressed(win, RGFW_controlR),
+					RGFW_isPressed(win, RGFW_altL) || RGFW_isPressed(win, RGFW_altR),
+					RGFW_isPressed(win, RGFW_shiftL) || RGFW_isPressed(win, RGFW_shiftR),
+					RGFW_isPressed(win, RGFW_superL) || RGFW_isPressed(win, RGFW_superR),
+					scroll);
+}
+
+RGFWDEF void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show);
+void RGFW_window_showMouseFlags(RGFW_window* win, RGFW_bool show) {
+	if (show && (win->_flags & RGFW_windowHideMouse))
+		win->_flags ^= RGFW_windowHideMouse;
+	else if (!show && !(win->_flags & RGFW_windowHideMouse))
+		win->_flags |= RGFW_windowHideMouse;
+}
+
+RGFW_bool RGFW_window_mouseHidden(RGFW_window* win) {
+	return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowHideMouse);
+}
+
+RGFW_bool RGFW_window_borderless(RGFW_window* win) {
+	return (RGFW_bool)RGFW_BOOL(win->_flags & RGFW_windowNoBorder);
+}
+
+RGFW_bool RGFW_window_isFullscreen(RGFW_window* win){ return RGFW_BOOL(win->_flags & RGFW_windowFullscreen); }
+RGFW_bool RGFW_window_allowsDND(RGFW_window* win) { return RGFW_BOOL(win->_flags & RGFW_windowAllowDND); }
+
+void RGFW_window_focusLost(RGFW_window* win) {
+    /* standard routines for when a window looses focus */
+	_RGFW.root->_flags &= ~(u32)RGFW_windowFocus;
+	if ((win->_flags & RGFW_windowFullscreen))
+			RGFW_window_minimize(win);
+
+    for (size_t key = 0; key < RGFW_keyLast; key++) {
+        if (RGFW_isPressed(NULL, (u8)key) == RGFW_FALSE) continue;
+	    RGFW_keyboard[key].current = RGFW_FALSE; 
+        u8 keyChar = RGFW_rgfwToKeyChar((u32)key);
+        RGFW_keyCallback(win, (u8)key, keyChar, win->event.keyMod, RGFW_FALSE);
+        RGFW_eventQueuePushEx(e.type = RGFW_keyReleased;
+                                e.key = (u8)key;
+                                e.keyChar = keyChar;
+                                e.repeat = RGFW_FALSE;
+                                e.keyMod = win->event.keyMod;
+                                e._win = win);
+    }
+    
+    RGFW_resetKey();
+}
+
+#ifndef RGFW_WINDOWS
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+	RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow);
+}
+#endif
+
+#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM) || defined(RGFW_WAYLAND)
+#ifndef __USE_POSIX199309
+	#define __USE_POSIX199309
+#endif
+#include <time.h>
+struct timespec;
+#endif
+
+#if defined(RGFW_WAYLAND) || defined(RGFW_X11) || defined(RGFW_WINDOWS)
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+	RGFW_window_showMouseFlags(win, show);
+	if (show == 0)
+		RGFW_window_setMouse(win, _RGFW.hiddenMouse);
+	else
+		RGFW_window_setMouseDefault(win);
+}
+#endif
+
+#ifndef RGFW_MACOS
+void RGFW_moveToMacOSResourceDir(void) { }
+#endif
+
+/*
+	graphics API specific code (end of generic code)
+	starts here
+*/
+
+
+/*
+	OpenGL defines start here   (Normal, EGL, OSMesa)
+*/
+
+#if defined(RGFW_OPENGL) || defined(RGFW_EGL)
+
+#ifdef RGFW_WINDOWS
+	#define WIN32_LEAN_AND_MEAN
+	#define OEMRESOURCE
+	#include <windows.h>
+#endif
+
+#if !defined(__APPLE__) && !defined(RGFW_NO_GL_HEADER)
+	#include <GL/gl.h>
+#elif defined(__APPLE__)
+	#ifndef GL_SILENCE_DEPRECATION
+		#define GL_SILENCE_DEPRECATION
+	#endif
+	#include <OpenGL/gl.h>
+	#include <OpenGL/OpenGL.h>
+#endif
+
+/* EGL, normal OpenGL only */
+#ifndef RGFW_EGL
+i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {8,
+#else
+i32 RGFW_GL_HINTS[RGFW_glFinalHint] = {0,
+#endif
+	0, 0, 0, 1, 8, 8, 8, 8, 24, 0, 0, 0, 0, 0, 0, 0, 0, RGFW_glReleaseNone, RGFW_glCore, 0, 0};
+
+void RGFW_setGLHint(RGFW_glHints hint, i32 value) {
+	if (hint < RGFW_glFinalHint && hint) RGFW_GL_HINTS[hint] = value;
+}
+
+RGFW_bool RGFW_extensionSupportedStr(const char* extensions, const char* ext, size_t len) {
+    const char *start = extensions;
+    const char *where; 
+    const char* terminator;
+
+    if (extensions == NULL || ext == NULL)
+        return RGFW_FALSE;
+
+    where = strstr(extensions, ext);
+    while (where) {
+        terminator = where + len;
+        if ((where == start || *(where - 1) == ' ') &&
+            (*terminator == ' ' || *terminator == '\0')) {
+            return RGFW_TRUE;
+        }
+        where = RGFW_STRSTR(terminator, ext);
+    }
+
+    return RGFW_FALSE;
+}
+
+RGFW_bool RGFW_extensionSupported(const char* extension, size_t len) {
+    #ifdef GL_NUM_EXTENSIONS
+    if (RGFW_GL_HINTS[RGFW_glMajor] >= 3) {
+        i32 i;
+        GLint count = 0;
+
+        RGFW_proc RGFW_glGetStringi = RGFW_getProcAddress("glGetStringi");
+        RGFW_proc RGFW_glGetIntegerv = RGFW_getProcAddress("RGFW_glGetIntegerv");
+        if (RGFW_glGetIntegerv) 
+            ((void(*)(GLenum, GLint*))RGFW_glGetIntegerv)(GL_NUM_EXTENSIONS, &count);
+
+        for (i = 0; RGFW_glGetStringi && i < count;  i++) {
+            const char* en = ((const char* (*)(u32, u32))RGFW_glGetStringi)(GL_EXTENSIONS, (u32)i);
+            if (en && RGFW_STRNCMP(en, extension, len) == 0)
+                return RGFW_TRUE;
+        }
+    } else 
+#endif
+    {
+        RGFW_proc RGFW_glGetString = RGFW_getProcAddress("glGetString");
+            
+        if (RGFW_glGetString) {
+            const char* extensions = ((const char*(*)(u32))RGFW_glGetString)(GL_EXTENSIONS);
+            if ((extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len))
+                return RGFW_TRUE;
+        }
+    }
+
+    return RGFW_extensionSupportedPlatform(extension, len);
+}
+
+/* OPENGL normal only (no EGL / OSMesa) */
+#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) && !defined(RGFW_CUSTOM_BACKEND) && !defined(RGFW_WASM)
+
+#define RGFW_GL_RENDER_TYPE 		RGFW_OS_BASED_VALUE(GLX_X_VISUAL_TYPE,    	0x2003,		73, 0)
+	#define RGFW_GL_ALPHA_SIZE 		RGFW_OS_BASED_VALUE(GLX_ALPHA_SIZE,       	0x201b,		11,     0)
+	#define RGFW_GL_DEPTH_SIZE 		RGFW_OS_BASED_VALUE(GLX_DEPTH_SIZE,       	0x2022,		12,     0)
+	#define RGFW_GL_DOUBLEBUFFER 		RGFW_OS_BASED_VALUE(GLX_DOUBLEBUFFER,     	0x2011, 	5,  0)
+	#define RGFW_GL_STENCIL_SIZE 		RGFW_OS_BASED_VALUE(GLX_STENCIL_SIZE,	 	0x2023,	13,     0)
+	#define RGFW_GL_SAMPLES			RGFW_OS_BASED_VALUE(GLX_SAMPLES, 		 	0x2042,	    55,     0)
+	#define RGFW_GL_STEREO 			RGFW_OS_BASED_VALUE(GLX_STEREO,	 		 	0x2012,			6,  0)
+	#define RGFW_GL_AUX_BUFFERS		RGFW_OS_BASED_VALUE(GLX_AUX_BUFFERS,	    0x2024,	7, 		    0)
+
+#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+	#define RGFW_GL_DRAW 			RGFW_OS_BASED_VALUE(GLX_X_RENDERABLE,	 	0x2001,					0, 0)
+	#define RGFW_GL_DRAW_TYPE 		RGFW_OS_BASED_VALUE(GLX_RENDER_TYPE,     	0x2013,						0, 0)
+	#define RGFW_GL_FULL_FORMAT		RGFW_OS_BASED_VALUE(GLX_TRUE_COLOR,   	 	0x2027,						0, 0)
+	#define RGFW_GL_RED_SIZE		RGFW_OS_BASED_VALUE(GLX_RED_SIZE,         	0x2015,						0, 0)
+	#define RGFW_GL_GREEN_SIZE		RGFW_OS_BASED_VALUE(GLX_GREEN_SIZE,       	0x2017,						0, 0)
+	#define RGFW_GL_BLUE_SIZE		RGFW_OS_BASED_VALUE(GLX_BLUE_SIZE, 	 		0x2019,						0, 0)
+	#define RGFW_GL_USE_RGBA		RGFW_OS_BASED_VALUE(GLX_RGBA_BIT,   	 	0x202B,						0, 0)
+	#define RGFW_GL_ACCUM_RED_SIZE 	RGFW_OS_BASED_VALUE(14,   	 	0x201E,						0, 0)
+	#define RGFW_GL_ACCUM_GREEN_SIZE RGFW_OS_BASED_VALUE(15,   	 	0x201F,						0, 0)
+	#define RGFW_GL_ACCUM_BLUE_SIZE	 RGFW_OS_BASED_VALUE(16,   	 	0x2020,						0, 0)
+	#define RGFW_GL_ACCUM_ALPHA_SIZE	 RGFW_OS_BASED_VALUE(17,   	 	0x2021,						0, 0)
+	#define RGFW_GL_SRGB	 RGFW_OS_BASED_VALUE(0x20b2,   	 	0x3089,						0, 0)
+	#define RGFW_GL_NOERROR	 RGFW_OS_BASED_VALUE(0x31b3,   	 	0x31b3,						0, 0)
+	#define RGFW_GL_FLAGS	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB,   	 	0x2094,						0, 0)
+	#define RGFW_GL_RELEASE_BEHAVIOR	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB,   	 	0x2097 ,						0, 0)
+	#define RGFW_GL_CONTEXT_RELEASE	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB,   	 	0x2098,						0, 0)
+	#define RGFW_GL_CONTEXT_NONE	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB,   	 	0x0000,						0, 0)
+	#define RGFW_GL_FLAGS	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB,   	 	0x2094,						0, 0)
+	#define RGFW_GL_DEBUG_BIT	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_FLAGS_ARB,   	 	0x2094,						0, 0)
+	#define RGFW_GL_ROBUST_BIT	 RGFW_OS_BASED_VALUE(GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB,   	 	0x00000004,						0, 0)
+#endif
+
+#ifdef RGFW_WINDOWS
+	#define WGL_SUPPORT_OPENGL_ARB                    0x2010
+	#define WGL_COLOR_BITS_ARB                        0x2014
+	#define WGL_NUMBER_PIXEL_FORMATS_ARB 			0x2000
+	#define WGL_CONTEXT_MAJOR_VERSION_ARB             0x2091
+	#define WGL_CONTEXT_MINOR_VERSION_ARB             0x2092
+	#define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
+	#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+	#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+	#define WGL_SAMPLE_BUFFERS_ARB               0x2041
+	#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9
+	#define WGL_PIXEL_TYPE_ARB                        0x2013
+	#define WGL_TYPE_RGBA_ARB                         0x202B
+
+	#define WGL_TRANSPARENT_ARB   					  0x200A
+#endif
+
+/*  The window'ing api needs to know how to render the data we (or opengl) give it
+	MacOS and Windows do this using a structure called a "pixel format"
+	X11 calls it a "Visual"
+	This function returns the attributes for the format we want */
+i32* RGFW_initFormatAttribs(void);
+i32* RGFW_initFormatAttribs(void) {
+	static i32 attribs[] = {
+							#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+							RGFW_GL_RENDER_TYPE,
+							RGFW_GL_FULL_FORMAT,
+							RGFW_GL_DRAW, 1,
+							RGFW_GL_DRAW_TYPE     , RGFW_GL_USE_RGBA,
+							#endif
+
+							#ifdef RGFW_X11
+							GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
+							#endif
+
+							#ifdef RGFW_MACOS
+							72,
+							8, 24,
+							#endif
+
+							#ifdef RGFW_WINDOWS
+							WGL_SUPPORT_OPENGL_ARB,		1,
+							WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+							WGL_COLOR_BITS_ARB,	 32,
+							#endif
+							0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+	};
+
+	size_t index = (sizeof(attribs) / sizeof(attribs[0])) - 27;
+
+	#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
+		if (attVal) { \
+			attribs[index] = attrib;\
+			attribs[index + 1] = attVal;\
+			index += 2;\
+		}
+
+		#if defined(RGFW_MACOS) && defined(RGFW_COCOA_GRAPHICS_SWITCHING)
+		RGFW_GL_ADD_ATTRIB(96, kCGLPFASupportsAutomaticGraphicsSwitching);
+		#endif
+
+        RGFW_GL_ADD_ATTRIB(RGFW_GL_DOUBLEBUFFER, 1);
+
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]);
+        RGFW_GL_ADD_ATTRIB(RGFW_GL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_STEREO, RGFW_GL_HINTS[RGFW_glStereo]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_AUX_BUFFERS, RGFW_GL_HINTS[RGFW_glAuxBuffers]);
+
+	#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]);
+	#endif
+
+	#if defined(RGFW_X11) || defined(RGFW_WINDOWS)
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_RED_SIZE, RGFW_GL_HINTS[RGFW_glAccumRed]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glAccumBlue]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glAccumGreen]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_ACCUM_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAccumAlpha]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_SRGB, RGFW_GL_HINTS[RGFW_glSRGB]);
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_NOERROR, RGFW_GL_HINTS[RGFW_glNoError]);
+
+		if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) {
+			RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_RELEASE);
+		} else if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_glReleaseNone) {
+			RGFW_GL_ADD_ATTRIB(RGFW_GL_RELEASE_BEHAVIOR, RGFW_GL_CONTEXT_NONE);
+		}
+
+		i32 flags = 0;
+		if (RGFW_GL_HINTS[RGFW_glDebug]) flags |= RGFW_GL_DEBUG_BIT;
+		if (RGFW_GL_HINTS[RGFW_glRobustness]) flags |= RGFW_GL_ROBUST_BIT;
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_FLAGS, flags);
+	#else
+		i32 accumSize = (i32)(RGFW_GL_HINTS[RGFW_glAccumRed] + RGFW_GL_HINTS[RGFW_glAccumGreen] +  RGFW_GL_HINTS[RGFW_glAccumBlue] + RGFW_GL_HINTS[RGFW_glAccumAlpha]) / 4;
+		RGFW_GL_ADD_ATTRIB(14, accumSize);
+	#endif
+
+	#ifndef RGFW_X11
+		RGFW_GL_ADD_ATTRIB(RGFW_GL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]);
+	#endif
+
+	#ifdef RGFW_MACOS
+		if (_RGFW.root->_flags & RGFW_windowOpenglSoftware) {
+			RGFW_GL_ADD_ATTRIB(70, kCGLRendererGenericFloatID);
+		} else {
+			attribs[index] = RGFW_GL_RENDER_TYPE;
+			index += 1;
+		}
+	#endif
+
+	#ifdef RGFW_MACOS
+		/* macOS has the surface attribs and the opengl attribs connected for some reason
+			maybe this is to give macOS more control to limit openGL/the opengl version? */
+
+		attribs[index] = 99;
+		attribs[index + 1] = 0x1000;
+
+
+		if (RGFW_GL_HINTS[RGFW_glMajor] >= 4 || RGFW_GL_HINTS[RGFW_glMajor] >= 3) {
+			attribs[index + 1] = (i32) ((RGFW_GL_HINTS[RGFW_glMajor] >= 4) ? 0x4100 : 0x3200);
+		}
+	#endif
+
+	RGFW_GL_ADD_ATTRIB(0, 0);
+
+	return attribs;
+}
+
+/* EGL only (no OSMesa nor normal OPENGL) */
+#elif defined(RGFW_EGL)
+
+#include <EGL/egl.h>
+
+#if defined(RGFW_LINK_EGL)
+	typedef EGLBoolean(EGLAPIENTRY* PFN_eglInitialize)(EGLDisplay, EGLint*, EGLint*);
+
+	PFNEGLINITIALIZEPROC eglInitializeSource;
+	PFNEGLGETCONFIGSPROC eglGetConfigsSource;
+	PFNEGLCHOOSECONFIgamepadROC eglChooseConfigSource;
+	PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceSource;
+	PFNEGLCREATECONTEXTPROC eglCreateContextSource;
+	PFNEGLMAKECURRENTPROC eglMakeCurrentSource;
+	PFNEGLGETDISPLAYPROC eglGetDisplaySource;
+	PFNEGLSWAPBUFFERSPROC eglSwapBuffersSource;
+	PFNEGLSWAPINTERVALPROC eglSwapIntervalSource;
+	PFNEGLBINDAPIPROC eglBindAPISource;
+	PFNEGLDESTROYCONTEXTPROC eglDestroyContextSource;
+	PFNEGLTERMINATEPROC eglTerminateSource;
+	PFNEGLDESTROYSURFACEPROC eglDestroySurfaceSource;
+
+	#define eglInitialize eglInitializeSource
+	#define eglGetConfigs eglGetConfigsSource
+	#define eglChooseConfig eglChooseConfigSource
+	#define eglCreateWindowSurface eglCreateWindowSurfaceSource
+	#define eglCreateContext eglCreateContextSource
+	#define eglMakeCurrent eglMakeCurrentSource
+	#define eglGetDisplay eglGetDisplaySource
+	#define eglSwapBuffers eglSwapBuffersSource
+	#define eglSwapInterval eglSwapIntervalSource
+	#define eglBindAPI eglBindAPISource
+	#define eglDestroyContext eglDestroyContextSource
+	#define eglTerminate eglTerminateSource
+	#define eglDestroySurface eglDestroySurfaceSource;
+#endif
+
+
+#define EGL_SURFACE_MAJOR_VERSION_KHR 0x3098
+#define EGL_SURFACE_MINOR_VERSION_KHR 0x30fb
+
+#ifndef RGFW_GL_ADD_ATTRIB
+#define RGFW_GL_ADD_ATTRIB(attrib, attVal) \
+	if (attVal) { \
+		attribs[index] = attrib;\
+		attribs[index + 1] = attVal;\
+		index += 2;\
+	}
+#endif
+
+
+void RGFW_window_initOpenGL(RGFW_window* win) {
+#if defined(RGFW_LINK_EGL)
+	eglInitializeSource = (PFNEGLINITIALIZEPROC) eglGetProcAddress("eglInitialize");
+	eglGetConfigsSource = (PFNEGLGETCONFIGSPROC) eglGetProcAddress("eglGetConfigs");
+	eglChooseConfigSource = (PFNEGLCHOOSECONFIgamepadROC) eglGetProcAddress("eglChooseConfig");
+	eglCreateWindowSurfaceSource = (PFNEGLCREATEWINDOWSURFACEPROC) eglGetProcAddress("eglCreateWindowSurface");
+	eglCreateContextSource = (PFNEGLCREATECONTEXTPROC) eglGetProcAddress("eglCreateContext");
+	eglMakeCurrentSource = (PFNEGLMAKECURRENTPROC) eglGetProcAddress("eglMakeCurrent");
+	eglGetDisplaySource = (PFNEGLGETDISPLAYPROC) eglGetProcAddress("eglGetDisplay");
+	eglSwapBuffersSource = (PFNEGLSWAPBUFFERSPROC) eglGetProcAddress("eglSwapBuffers");
+	eglSwapIntervalSource = (PFNEGLSWAPINTERVALPROC) eglGetProcAddress("eglSwapInterval");
+	eglBindAPISource = (PFNEGLBINDAPIPROC) eglGetProcAddress("eglBindAPI");
+	eglDestroyContextSource = (PFNEGLDESTROYCONTEXTPROC) eglGetProcAddress("eglDestroyContext");
+	eglTerminateSource = (PFNEGLTERMINATEPROC) eglGetProcAddress("eglTerminate");
+	eglDestroySurfaceSource = (PFNEGLDESTROYSURFACEPROC) eglGetProcAddress("eglDestroySurface");
+
+	RGFW_ASSERT(eglInitializeSource != NULL &&
+	            eglGetConfigsSource != NULL &&
+	            eglChooseConfigSource != NULL &&
+	            eglCreateWindowSurfaceSource != NULL &&
+	            eglCreateContextSource != NULL &&
+	            eglMakeCurrentSource != NULL &&
+	            eglGetDisplaySource != NULL &&
+	            eglSwapBuffersSource != NULL &&
+	            eglSwapIntervalsSource != NULL &&
+	            eglBindAPISource != NULL &&
+	            eglDestroyContextSource != NULL &&
+	            eglTerminateSource != NULL &&
+	            eglDestroySurfaceSource != NULL);
+#endif /* RGFW_LINK_EGL */
+
+#ifdef RGFW_WAYLAND
+    if (RGFW_useWaylandBool)
+        win->src.eglWindow = wl_egl_window_create(win->src.surface, win->r.w, win->r.h);
+#endif
+
+	#ifdef RGFW_WINDOWS
+	win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.hdc);
+	#elif defined(RGFW_MACOS)
+	win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0);
+	#elif defined(RGFW_WAYLAND)
+	if (RGFW_useWaylandBool)
+		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display);
+    else
+    #endif
+    #ifdef RGFW_X11
+		win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
+    #else 
+    {}
+    #endif
+    #if !defined(RGFW_WAYLAND) && !defined(RGFW_WINDOWS) && !defined(RGFW_X11) 
+        win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display);
+	#endif
+
+	EGLint major, minor;
+
+	eglInitialize(win->src.EGL_display, &major, &minor);
+
+	#ifndef EGL_OPENGL_ES1_BIT
+	#define EGL_OPENGL_ES1_BIT 0x1
+	#endif
+
+	EGLint egl_config[24] = {
+		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+		EGL_RENDERABLE_TYPE,
+		#ifdef RGFW_OPENGL_ES1
+		EGL_OPENGL_ES1_BIT,
+		#elif defined(RGFW_OPENGL_ES3)
+		EGL_OPENGL_ES3_BIT,
+		#elif defined(RGFW_OPENGL_ES2)
+		EGL_OPENGL_ES2_BIT,
+		#else
+		EGL_OPENGL_BIT,
+		#endif
+		EGL_NONE, EGL_NONE
+	};
+
+	{
+		size_t index = 7;
+		EGLint* attribs = egl_config;
+
+		RGFW_GL_ADD_ATTRIB(EGL_RED_SIZE, RGFW_GL_HINTS[RGFW_glRed]);
+		RGFW_GL_ADD_ATTRIB(EGL_GREEN_SIZE, RGFW_GL_HINTS[RGFW_glBlue]);
+		RGFW_GL_ADD_ATTRIB(EGL_BLUE_SIZE, RGFW_GL_HINTS[RGFW_glGreen]);
+		RGFW_GL_ADD_ATTRIB(EGL_ALPHA_SIZE, RGFW_GL_HINTS[RGFW_glAlpha]);
+		RGFW_GL_ADD_ATTRIB(EGL_DEPTH_SIZE, RGFW_GL_HINTS[RGFW_glDepth]);
+
+		if (RGFW_GL_HINTS[RGFW_glSRGB])
+			RGFW_GL_ADD_ATTRIB(0x3089, RGFW_GL_HINTS[RGFW_glSRGB]);
+
+		RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE);
+	}
+
+	EGLConfig config;
+	EGLint numConfigs;
+	eglChooseConfig(win->src.EGL_display, egl_config, &config, 1, &numConfigs);
+
+	#if defined(RGFW_MACOS)
+		void* layer = RGFW_cocoaGetLayer();
+
+		RGFW_window_cocoaSetLayer(win, layer);
+
+		win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL);
+	#elif defined(RGFW_WINDOWS)
+		win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
+	#elif defined(RGFW_WAYLAND)
+		if (RGFW_useWaylandBool)
+			win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL);
+		else
+    #endif
+    #ifdef RGFW_X11
+            win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
+    #else
+    {}
+    #endif
+    #if !defined(RGFW_X11) && !defined(RGFW_WAYLAND) && !defined(RGFW_MACOS)
+		win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL);
+	#endif
+
+	EGLint attribs[12];
+	size_t index = 0;
+
+#ifdef RGFW_OPENGL_ES1
+    RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 1);
+#elif defined(RGFW_OPENGL_ES2)
+    RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 2);
+#elif defined(RGFW_OPENGL_ES3)
+    RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_CLIENT_VERSION, 3);
+#endif
+
+    RGFW_GL_ADD_ATTRIB(EGL_STENCIL_SIZE, RGFW_GL_HINTS[RGFW_glStencil]);
+	RGFW_GL_ADD_ATTRIB(EGL_SAMPLES, RGFW_GL_HINTS[RGFW_glSamples]);
+
+    if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0)
+		RGFW_GL_ADD_ATTRIB(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER);
+
+	if (RGFW_GL_HINTS[RGFW_glMajor]) {
+		RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MAJOR_VERSION, RGFW_GL_HINTS[RGFW_glMajor]);
+		RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_MINOR_VERSION, RGFW_GL_HINTS[RGFW_glMinor]);
+
+		if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore) {
+			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
+		}
+		else {
+			RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
+		}
+	}
+
+	RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_ROBUST_ACCESS, RGFW_GL_HINTS[RGFW_glRobustness]);
+	RGFW_GL_ADD_ATTRIB(EGL_CONTEXT_OPENGL_DEBUG, RGFW_GL_HINTS[RGFW_glDebug]);
+	if (RGFW_GL_HINTS[RGFW_glReleaseBehavior] == RGFW_releaseFlush) {
+		RGFW_GL_ADD_ATTRIB(0x2097, 0x2098);
+	} else {
+		RGFW_GL_ADD_ATTRIB(0x2096, 0x0000);
+	}
+
+	RGFW_GL_ADD_ATTRIB(EGL_NONE, EGL_NONE);
+
+	#if defined(RGFW_OPENGL_ES1) || defined(RGFW_OPENGL_ES2) || defined(RGFW_OPENGL_ES3)
+	eglBindAPI(EGL_OPENGL_ES_API);
+	#else
+	eglBindAPI(EGL_OPENGL_API);
+	#endif
+
+	win->src.EGL_context = eglCreateContext(win->src.EGL_display, config, EGL_NO_CONTEXT, attribs);
+
+	if (win->src.EGL_context == NULL) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errEGLContext, RGFW_DEBUG_CTX(win, 0), "failed to create an EGL opengl context");
+		return;
+	}
+
+	eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
+	eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface);
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context initalized");
+}
+
+void RGFW_window_freeOpenGL(RGFW_window* win) {
+	if (win->src.EGL_display == NULL) return;
+
+	eglDestroySurface(win->src.EGL_display, win->src.EGL_surface);
+	eglDestroyContext(win->src.EGL_display, win->src.EGL_context);
+	eglTerminate(win->src.EGL_display);
+    win->src.EGL_display = NULL;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "EGL opengl context freed");
+}
+
+void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+    if (win == NULL)
+        eglMakeCurrent(_RGFW.root->src.EGL_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    else {
+        eglMakeCurrent(win->src.EGL_display, win->src.EGL_surface, win->src.EGL_surface, win->src.EGL_context);
+    }
+}
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); }
+
+void* RGFW_getCurrent_OpenGL(void) { return eglGetCurrentContext(); }
+
+#ifdef RGFW_APPLE
+void* RGFWnsglFramework = NULL;
+#elif defined(RGFW_WINDOWS)
+HMODULE RGFW_wgl_dll = NULL;
+#endif
+
+RGFW_proc RGFW_getProcAddress(const char* procname) {
+	#if defined(RGFW_WINDOWS)
+	RGFW_proc proc = (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+
+		if (proc)
+			return proc;
+	#endif
+
+	return (RGFW_proc) eglGetProcAddress(procname);
+}
+
+RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) {
+    const char* extensions = eglQueryString(_RGFW.root->src.EGL_display, EGL_EXTENSIONS);
+    return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); 
+}
+
+void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+	RGFW_ASSERT(win != NULL);
+
+	eglSwapInterval(win->src.EGL_display, swapInterval);
+
+}
+
+#endif /* RGFW_EGL */
+
+/*
+	end of RGFW_EGL defines
+*/
+#endif /* end of RGFW_GL (OpenGL, EGL, OSMesa )*/
+
+/*
+	RGFW_VULKAN defines
+*/
+#ifdef RGFW_VULKAN
+#ifdef RGFW_MACOS
+#include <objc/message.h>
+#endif
+
+const char** RGFW_getVKRequiredInstanceExtensions(size_t* count) {
+    static const char* arr[2] = {VK_KHR_SURFACE_EXTENSION_NAME};
+    arr[1] = RGFW_VK_SURFACE;
+    if (count != NULL) *count = 2;
+
+    return (const char**)arr;
+}
+
+VkResult RGFW_window_createVKSurface(RGFW_window* win, VkInstance instance, VkSurfaceKHR* surface) {
+    RGFW_ASSERT(win != NULL); RGFW_ASSERT(instance);
+	RGFW_ASSERT(surface != NULL);
+
+    *surface = VK_NULL_HANDLE;
+
+#ifdef RGFW_X11
+    RGFW_GOTO_WAYLAND(0);
+    VkXlibSurfaceCreateInfoKHR x11 = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 0, 0, (Display*) win->src.display, (Window) win->src.window };
+    return vkCreateXlibSurfaceKHR(instance, &x11, NULL, surface);
+#endif
+#if defined(RGFW_WAYLAND)
+RGFW_WAYLAND_LABEL
+    VkWaylandSurfaceCreateInfoKHR wayland = { VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, 0, 0, (struct wl_display*) win->src.wl_display, (struct wl_surface*) win->src.surface };
+    return vkCreateWaylandSurfaceKHR(instance, &wayland, NULL, surface);
+#elif defined(RGFW_WINDOWS)
+    VkWin32SurfaceCreateInfoKHR win32 = { VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 0, 0, GetModuleHandle(NULL), (HWND)win->src.window };
+
+    return vkCreateWin32SurfaceKHR(instance, &win32, NULL, surface);
+#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+    void* contentView = ((void* (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView"));
+    VkMacOSSurfaceCreateFlagsMVK macos = { VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 0, 0, win->src.display, (void*)contentView };
+
+    return vkCreateMacOSSurfaceMVK(instance, &macos, NULL, surface);
+#endif
+}
+
+
+RGFW_bool RGFW_getVKPresentationSupport(VkInstance instance, VkPhysicalDevice physicalDevice, u32 queueFamilyIndex) {
+    RGFW_ASSERT(instance);
+	if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) RGFW_init();
+#ifdef RGFW_X11
+    RGFW_GOTO_WAYLAND(0);
+	Visual* visual = DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display));
+    if (_RGFW.root)
+        visual = _RGFW.root->src.visual.visual;
+
+    RGFW_bool out = vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.display, XVisualIDFromVisual(visual));
+    return out;
+#endif
+#if defined(RGFW_WAYLAND)
+RGFW_WAYLAND_LABEL
+    RGFW_bool wlout = vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, _RGFW.wl_display);
+    return wlout;
+#elif defined(RGFW_WINDOWS)
+#elif defined(RGFW_MACOS) && !defined(RGFW_MACOS_X11)
+    return RGFW_FALSE; /* TODO */
+#endif
+}
+#endif /* end of RGFW_vulkan */
+
+/*
+This is where OS specific stuff starts
+*/
+
+
+#if (defined(RGFW_WAYLAND) || defined(RGFW_X11)) && !defined(RGFW_NO_LINUX)
+	int RGFW_eventWait_forceStop[] = {0, 0, 0}; /* for wait events */
+
+	#if defined(__linux__)
+		#include <linux/joystick.h>
+		#include <fcntl.h>
+		#include <unistd.h>
+		#include <errno.h>
+
+		u32 RGFW_linux_updateGamepad(RGFW_window* win);
+		u32 RGFW_linux_updateGamepad(RGFW_window* win) {
+			/* check for new gamepads */
+			static const char* str[] = {"/dev/input/js0", "/dev/input/js1", "/dev/input/js2", "/dev/input/js3", "/dev/input/js4", "/dev/input/js5"};
+			static u8 RGFW_rawGamepads[6];
+            {
+                u16 i;
+                for (i = 0; i < 6; i++) {
+                    u16 index = RGFW_gamepadCount;
+                    if (RGFW_rawGamepads[i]) {
+                        struct input_id device_info;
+                        if (ioctl(RGFW_rawGamepads[i], EVIOCGID, &device_info) == -2) {
+                            if (errno == ENODEV) {
+                                RGFW_rawGamepads[i] = 0;
+                            }
+                        }
+                        continue;
+                    }
+
+                    i32 js = open(str[i], O_RDONLY);
+
+                    if (js <= 0)
+                        break;
+
+                    if (RGFW_gamepadCount >= 4) {
+                        close(js);
+                        break;
+                    }
+
+                    RGFW_rawGamepads[i] = 1;
+
+                    int axes, buttons;
+                    if (ioctl(js, JSIOCGAXES, &axes) < 0 || ioctl(js, JSIOCGBUTTONS, &buttons) < 0) {
+                        close(js);
+                        continue;
+                    }
+
+                    if (buttons <= 5 || buttons >= 30) {
+                        close(js);
+                        continue;
+                    }
+
+                    RGFW_gamepadCount++;
+
+                    RGFW_gamepads[index] = js;
+
+                    ioctl(js, JSIOCGNAME(sizeof(RGFW_gamepads_name[index])), RGFW_gamepads_name[index]);
+                    RGFW_gamepads_name[index][sizeof(RGFW_gamepads_name[index]) - 1] = 0;
+
+                    u8 j;
+                    for (j = 0; j < 16; j++) {
+                        RGFW_gamepadPressed[index][j].prev = 0;
+                        RGFW_gamepadPressed[index][j].current = 0;
+                    }
+
+                    win->event.type = RGFW_gamepadConnected;
+
+                    RGFW_gamepads_type[index] = RGFW_gamepadUnknown;
+                    if (RGFW_STRSTR(RGFW_gamepads_name[index], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[index], "X-Box"))
+                        RGFW_gamepads_type[index] = RGFW_gamepadMicrosoft;
+                    else if (RGFW_STRSTR(RGFW_gamepads_name[index], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[index], "PS5"))
+                        RGFW_gamepads_type[index] = RGFW_gamepadSony;
+                    else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Nintendo"))
+                        RGFW_gamepads_type[index] = RGFW_gamepadNintendo;
+                    else if (RGFW_STRSTR(RGFW_gamepads_name[index], "Logitech"))
+                        RGFW_gamepads_type[index] = RGFW_gamepadLogitech;
+
+                    win->event.gamepad = index;
+                    RGFW_gamepadCallback(win, index, 1);
+                    return 1;
+                }
+            }
+			/* check gamepad events */
+			u8 i;
+
+			for (i = 0; i < RGFW_gamepadCount; i++) {
+				struct js_event e;
+				if (RGFW_gamepads[i] == 0)
+					continue;
+
+				i32 flags = fcntl(RGFW_gamepads[i], F_GETFL, 0);
+				fcntl(RGFW_gamepads[i], F_SETFL, flags | O_NONBLOCK);
+
+				ssize_t bytes;
+				while ((bytes = read(RGFW_gamepads[i], &e, sizeof(e))) > 0) {
+					switch (e.type) {
+						case JS_EVENT_BUTTON: {
+							size_t typeIndex = 0;
+							if (RGFW_gamepads_type[i] == RGFW_gamepadMicrosoft) typeIndex = 1;
+							else if (RGFW_gamepads_type[i] == RGFW_gamepadLogitech) typeIndex = 2;
+
+							win->event.type = e.value ? RGFW_gamepadButtonPressed : RGFW_gamepadButtonReleased;
+							u8 RGFW_linux2RGFW[3][RGFW_gamepadR3 + 8] = {{ /* ps */
+									RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadY, RGFW_gamepadX, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
+									RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight,
+								},{ /* xbox */
+									RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadSelect, RGFW_gamepadStart,
+									RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, 255, 255, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight
+							    },{ /* Logitech */
+									RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY, RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
+									RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight
+								}
+							};
+
+							win->event.button = RGFW_linux2RGFW[typeIndex][e.number];
+							win->event.gamepad = i;
+							if (win->event.button == 255) break;
+
+							RGFW_gamepadPressed[i][win->event.button].prev = RGFW_gamepadPressed[i][win->event.button].current;
+							RGFW_gamepadPressed[i][win->event.button].current = RGFW_BOOL(e.value);
+							RGFW_gamepadButtonCallback(win, i, win->event.button, RGFW_BOOL(e.value));
+
+							return 1;
+						}
+						case JS_EVENT_AXIS: {
+							size_t axis = e.number / 2;
+							if (axis == 2) axis = 1;
+
+							ioctl(RGFW_gamepads[i], JSIOCGAXES, &win->event.axisesCount);
+							win->event.axisesCount = 2;
+
+							if (axis < 3) {
+								if (e.number == 0 || e.number == 3)
+									RGFW_gamepadAxes[i][axis].x = (i32)((e.value / 32767.0f) * 100);
+								else if (e.number == 1 || e.number == 4) {
+									RGFW_gamepadAxes[i][axis].y = (i32)((e.value / 32767.0f) * 100);
+								}
+							}
+
+							win->event.axis[axis] = RGFW_gamepadAxes[i][axis];
+							win->event.type = RGFW_gamepadAxisMove;
+							win->event.gamepad = i;
+							win->event.whichAxis = (u8)axis;
+							RGFW_gamepadAxisCallback(win, i, win->event.axis, win->event.axisesCount, win->event.whichAxis);
+							return 1;
+						}
+						default: break;
+					}
+				}
+				if (bytes == -1 && errno == ENODEV) {
+					RGFW_gamepadCount--;
+					close(RGFW_gamepads[i]);
+					RGFW_gamepads[i] = 0;
+
+					win->event.type = RGFW_gamepadDisconnected;
+					win->event.gamepad = i;
+					RGFW_gamepadCallback(win, i, 0);
+					return 1;
+				}
+			}
+			return 0;
+		}
+
+	#endif
+#endif
+
+
+
+/*
+
+	Start of Wayland defines
+
+
+*/
+
+#ifdef RGFW_WAYLAND
+/*
+Wayland TODO: (out of date)
+- fix RGFW_keyPressed lock state
+
+	RGFW_windowMoved, 		the window was moved (by the user)
+	RGFW_windowResized  	the window was resized (by the user), [on WASM this means the browser was resized]
+	RGFW_windowRefresh	 	The window content needs to be refreshed
+
+	RGFW_DND 				a file has been dropped into the window
+	RGFW_DNDInit
+
+- window args:
+	#define RGFW_windowNoResize	 			the window cannot be resized  by the user
+	#define RGFW_windowAllowDND     			the window supports drag and drop
+	#define RGFW_scaleToMonitor 			scale the window to the screen
+
+- other missing functions functions ("TODO wayland") (~30 functions)
+- fix buffer rendering weird behavior
+*/
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon/xkbcommon-keysyms.h>
+#include <dirent.h>
+#include <linux/kd.h>
+#include <wayland-cursor.h>
+
+RGFW_window* RGFW_key_win = NULL;
+
+/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */
+#include "xdg-shell.h"
+#include "xdg-decoration-unstable-v1.h"
+
+struct xkb_context *xkb_context;
+struct xkb_keymap *keymap = NULL;
+struct xkb_state *xkb_state = NULL;
+enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode;
+struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
+
+struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL;
+struct wl_surface* RGFW_cursor_surface = NULL;
+struct wl_cursor_image* RGFW_cursor_image = NULL;
+
+void xdg_wm_base_ping_handler(void *data,
+        struct xdg_wm_base *wm_base, uint32_t serial)
+{
+	RGFW_UNUSED(data);
+    xdg_wm_base_pong(wm_base, serial);
+}
+
+const struct xdg_wm_base_listener xdg_wm_base_listener = {
+    .ping = xdg_wm_base_ping_handler,
+};
+
+RGFW_bool RGFW_wl_configured = 0;
+
+void xdg_surface_configure_handler(void *data,
+        struct xdg_surface *xdg_surface, uint32_t serial)
+{
+	RGFW_UNUSED(data);
+    xdg_surface_ack_configure(xdg_surface, serial);
+	RGFW_wl_configured = 1;
+}
+
+const struct xdg_surface_listener xdg_surface_listener = {
+    .configure = xdg_surface_configure_handler,
+};
+
+void xdg_toplevel_configure_handler(void *data,
+        struct xdg_toplevel *toplevel, int32_t width, int32_t height,
+        struct wl_array *states)
+{
+	RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states);
+    RGFW_UNUSED(width); RGFW_UNUSED(height);
+}
+
+void xdg_toplevel_close_handler(void *data,
+        struct xdg_toplevel *toplevel)
+{
+	RGFW_UNUSED(data);
+	RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel);
+	if (win == NULL)
+		win = RGFW_key_win;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
+	RGFW_windowQuitCallback(win);
+}
+
+void shm_format_handler(void *data,
+        struct wl_shm *shm, uint32_t format)
+{
+	RGFW_UNUSED(data); RGFW_UNUSED(shm); RGFW_UNUSED(format);
+}
+
+const struct wl_shm_listener shm_listener = {
+    .format = shm_format_handler,
+};
+
+const struct xdg_toplevel_listener xdg_toplevel_listener = {
+    .configure = xdg_toplevel_configure_handler,
+    .close = xdg_toplevel_close_handler,
+};
+
+RGFW_window* RGFW_mouse_win = NULL;
+
+void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y);
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	RGFW_mouse_win = win;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseEnter;
+									e.point = RGFW_POINT(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y));
+									e._win = win);
+
+	RGFW_mouseNotifyCallback(win, win->event.point, RGFW_TRUE);
+}
+void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface);
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	if (RGFW_mouse_win == win)
+		RGFW_mouse_win = NULL;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseLeave;
+									e.point = win->event.point;
+									e._win = win);
+
+	RGFW_mouseNotifyCallback(win,  win->event.point, RGFW_FALSE);
+}
+void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y);
+
+	RGFW_ASSERT(RGFW_mouse_win != NULL);
+	RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
+									e.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y));
+									e._win = RGFW_mouse_win);
+
+	RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)), RGFW_mouse_win->event.vector);
+}
+void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial);
+	RGFW_ASSERT(RGFW_mouse_win != NULL);
+
+	u32 b = (button - 0x110);
+
+	/* flip right and middle button codes */
+	if (b == 1) b = 2;
+	else if (b == 2) b = 1;
+
+	RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current;
+	RGFW_mouseButtons[b].current = RGFW_BOOL(state);
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased - RGFW_BOOL(state);
+									e.point = RGFW_mouse_win->event.point;
+									e.button = (u8)b;
+									e._win = RGFW_mouse_win);
+	RGFW_mouseButtonCallback(RGFW_mouse_win, (u8)b, 0, RGFW_BOOL(state));
+}
+void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
+	RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time);  RGFW_UNUSED(axis);
+	RGFW_ASSERT(RGFW_mouse_win != NULL);
+
+	double scroll = - wl_fixed_to_double(value);
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
+									e.point = RGFW_mouse_win->event.point;
+									e.button = RGFW_mouseScrollUp + (scroll < 0);
+									e.scroll = scroll;
+									e._win = RGFW_mouse_win);
+
+	RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1);
+}
+
+void RGFW_doNothing(void) { }
+
+void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format);
+
+	char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+	xkb_keymap_unref (keymap);
+	keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+	munmap (keymap_string, size);
+	close (fd);
+	xkb_state_unref (xkb_state);
+	xkb_state = xkb_state_new (keymap);
+}
+void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys);
+
+	RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface);
+
+	RGFW_key_win->_flags |= RGFW_windowFocus;
+	RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = RGFW_key_win);
+	RGFW_focusCallback(RGFW_key_win, RGFW_TRUE);
+
+	if ((RGFW_key_win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(RGFW_key_win, RGFW_AREA(RGFW_key_win->r.w, RGFW_key_win->r.h));
+}
+void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial);
+
+	RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface);
+	if (RGFW_key_win == win)
+		RGFW_key_win = NULL;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win);
+	RGFW_focusCallback(win, RGFW_FALSE);
+    RGFW_window_focusLost(win);
+}
+void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
+
+	if (RGFW_key_win == NULL) return;
+
+	xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
+
+	u32 RGFWkey = RGFW_apiKeyToRGFW(key + 8);
+	RGFW_keyboard[RGFWkey].prev = RGFW_keyboard[RGFWkey].current;
+	RGFW_keyboard[RGFWkey].current = RGFW_BOOL(state);
+
+	RGFW_eventQueuePushEx(e.type = (u8)(RGFW_keyPressed + state);
+									e.key = (u8)RGFWkey;
+									e.keyChar = (u8)keysym;
+									e.repeat = RGFW_isHeld(RGFW_key_win, (u8)RGFWkey);
+									e._win = RGFW_key_win);
+
+	RGFW_updateKeyMods(RGFW_key_win, RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Lock")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "Mod2")), RGFW_BOOL(xkb_keymap_mod_get_index(keymap, "ScrollLock")));
+	RGFW_keyCallback(RGFW_key_win, (u8)RGFWkey, (u8)keysym, RGFW_key_win->event.keyMod, RGFW_BOOL(state));
+}
+void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {
+	RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time);
+	xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+}
+struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void (*)(void *, struct wl_keyboard *, 
+int,  int))&RGFW_doNothing};
+
+void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) {
+	RGFW_UNUSED(data);
+    static struct wl_pointer_listener pointer_listener = {&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void (*)(void *, struct wl_pointer *))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, uint32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void *, struct wl_pointer *, uint32_t, int32_t))&RGFW_doNothing, (void (*)(void*, struct wl_pointer*, uint32_t, uint32_t))&RGFW_doNothing};
+
+    if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
+		struct wl_pointer *pointer = wl_seat_get_pointer (seat);
+		wl_pointer_add_listener (pointer, &pointer_listener, NULL);
+	}
+	if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
+		struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat);
+		wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL);
+	}
+}
+struct wl_seat_listener seat_listener = {&seat_capabilities, (void (*)(void *, struct wl_seat *, const char *))&RGFW_doNothing};
+
+void wl_global_registry_handler(void *data,
+		struct wl_registry *registry, uint32_t id, const char *interface,
+		uint32_t version)
+{
+	RGFW_window* win = (RGFW_window*)data;
+	RGFW_UNUSED(version);
+    if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) {
+		win->src.compositor = wl_registry_bind(registry,
+			id, &wl_compositor_interface, 4);
+	} else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) {
+		win->src.xdg_wm_base = wl_registry_bind(registry,
+		id, &xdg_wm_base_interface, 1);
+	} else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) {
+		decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1);
+    } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) {
+        win->src.shm = wl_registry_bind(registry,
+            id, &wl_shm_interface, 1);
+        wl_shm_add_listener(win->src.shm, &shm_listener, NULL);
+	} else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) {
+		win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1);
+		wl_seat_add_listener(win->src.seat, &seat_listener, NULL);
+	}
+}
+
+void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); }
+const struct wl_registry_listener registry_listener = {
+	.global = wl_global_registry_handler,
+	.global_remove = wl_global_registry_remove,
+};
+
+void decoration_handle_configure(void *data,
+		struct zxdg_toplevel_decoration_v1 *decoration,
+		enum zxdg_toplevel_decoration_v1_mode mode) {
+	RGFW_UNUSED(data); RGFW_UNUSED(decoration);
+	RGFW_current_mode = mode;
+}
+
+const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
+	.configure = decoration_handle_configure,
+};
+
+void randname(char *buf) {
+	struct timespec ts;
+	clock_gettime(CLOCK_REALTIME, &ts);
+	long r = ts.tv_nsec;
+
+    int i;
+    for (i = 0; i < 6; ++i) {
+		buf[i] = (char)('A'+(r&15)+(r&16)*2);
+		r >>= 5;
+	}
+}
+
+size_t wl_stringlen(char* name) {
+	size_t i = 0;
+    while (name[i]) { i++; }
+	return i;
+}
+
+int anonymous_shm_open(void) {
+	char name[] = "/RGFW-wayland-XXXXXX";
+	int retries = 100;
+
+	do {
+		randname(name + wl_stringlen(name) - 6);
+
+		--retries;
+		/* shm_open guarantees that O_CLOEXEC is set */
+		int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+		if (fd >= 0) {
+			shm_unlink(name);
+			return fd;
+		}
+	} while (retries > 0 && errno == EEXIST);
+
+	return -1;
+}
+
+int create_shm_file(off_t size) {
+	int fd = anonymous_shm_open();
+	if (fd < 0) {
+		return fd;
+	}
+
+	if (ftruncate(fd, size) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
+void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
+	RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time);
+
+	#ifdef RGFW_BUFFER
+		RGFW_window* win = (RGFW_window*)data;
+		wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
+		wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h);
+		wl_surface_commit(win->src.surface);
+	#endif
+}
+
+const struct wl_callback_listener wl_surface_frame_listener = {
+	.done = wl_surface_frame_done,
+};
+#endif /* RGFW_WAYLAND */
+/*
+	End of Wayland defines
+*/
+
+/*
+
+
+Start of Linux / Unix defines
+
+
+*/
+
+#ifdef RGFW_UNIX
+#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11)
+#include <X11/Xcursor/Xcursor.h>
+#endif
+
+#include <dlfcn.h>
+
+#ifndef RGFW_NO_DPI
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xresource.h>
+#endif
+
+#include <X11/Xatom.h>
+#include <X11/keysymdef.h>
+#include <X11/extensions/sync.h>
+#include <unistd.h>
+
+#include <X11/XKBlib.h> /* for converting keycode to string */
+#include <X11/cursorfont.h> /* for hiding */
+#include <X11/extensions/shapeconst.h>
+#include <X11/extensions/shape.h>
+#include <X11/extensions/XInput2.h>
+
+#include <limits.h> /* for data limits (mainly used in drag and drop functions) */
+#include <poll.h>
+
+/* atoms needed for drag and drop */
+Atom XdndAware, XtextPlain, XtextUriList;
+Atom RGFW_XUTF8_STRING = 0;
+
+Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0;
+
+#if defined(RGFW_X11) && !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+	typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int);
+	typedef void (*PFN_XcursorImageDestroy)(XcursorImage*);
+	typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*);
+#endif
+#if defined(RGFW_OPENGL) && defined(RGFW_X11)
+	typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
+#endif
+
+#if !defined(RGFW_NO_X11_XI_PRELOAD) && defined(RGFW_X11)
+	typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int);
+	PFN_XISelectEvents XISelectEventsSRC = NULL;
+	#define XISelectEvents XISelectEventsSRC
+
+	void* X11Xihandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_EXT_PRELOAD) && defined(RGFW_X11)
+	typedef void (* PFN_XSyncIntToValue)(XSyncValue*, int);
+	PFN_XSyncIntToValue XSyncIntToValueSRC = NULL;
+    #define XSyncIntToValue XSyncIntToValueSRC
+
+	typedef Status (* PFN_XSyncSetCounter)(Display*, XSyncCounter, XSyncValue);
+	PFN_XSyncSetCounter XSyncSetCounterSRC = NULL;
+    #define XSyncSetCounter XSyncSetCounterSRC
+
+	typedef XSyncCounter (* PFN_XSyncCreateCounter)(Display*, XSyncValue);
+	PFN_XSyncCreateCounter XSyncCreateCounterSRC = NULL;
+    #define XSyncCreateCounter XSyncCreateCounterSRC
+
+	typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int);
+	PFN_XShapeCombineMask XShapeCombineMaskSRC;
+    #define XShapeCombineMask XShapeCombineMaskSRC
+
+	typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int);
+    PFN_XShapeCombineRegion XShapeCombineRegionSRC;
+    #define XShapeCombineRegion XShapeCombineRegionSRC
+	void* X11XEXThandle = NULL;
+#endif
+
+#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) && defined(RGFW_X11)
+	PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL;
+	PFN_XcursorImageCreate XcursorImageCreateSRC = NULL;
+	PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL;
+
+	#define XcursorImageLoadCursor XcursorImageLoadCursorSRC
+	#define XcursorImageCreate XcursorImageCreateSRC
+	#define XcursorImageDestroy XcursorImageDestroySRC
+
+	void* X11Cursorhandle = NULL;
+#endif
+
+#ifdef RGFW_X11
+const char* RGFW_instName = NULL;
+void RGFW_setXInstName(const char* name) { RGFW_instName = name; }
+#endif
+
+#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
+RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) {
+    const char* extensions = glXQueryExtensionsString(_RGFW.display, XDefaultScreen(_RGFW.display));
+    return (extensions != NULL) && RGFW_extensionSupportedStr(extensions, extension, len); 
+}
+RGFW_proc RGFW_getProcAddress(const char* procname) { return (RGFW_proc) glXGetProcAddress((GLubyte*) procname); }
+#endif
+
+void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) {
+	RGFW_GOTO_WAYLAND(0);
+
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	win->buffer = (u8*)buffer;
+	win->bufferSize = area;
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoBuffer, RGFW_DEBUG_CTX(win, 0), "createing a 4 channel buffer");
+	#ifdef RGFW_X11
+		#ifdef RGFW_OSMESA
+				win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
+				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
+				OSMesaPixelStore(OSMESA_Y_UP, 0);
+		#endif
+
+		win->src.bitmap = XCreateImage(
+			win->src.display, win->src.visual.visual, (u32)win->src.visual.depth,
+			ZPixmap, 0, NULL, area.w, area.h, 32, 0
+		);
+	#endif
+	#ifdef RGFW_WAYLAND
+		RGFW_WAYLAND_LABEL {}
+		u32 size = (u32)(win->r.w * win->r.h * 4);
+		int fd = create_shm_file(size);
+		if (fd < 0) {
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, (u32)fd),"Failed to create a buffer.");
+			exit(1);
+        }
+
+		win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+		if (win->src.buffer == MAP_FAILED) {
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errBuffer, RGFW_DEBUG_CTX(win, 0), "mmap failed!");
+			close(fd);
+			exit(1);
+		}
+
+		win->_flags |= RGFW_BUFFER_ALLOC;
+
+		struct wl_shm_pool* pool = wl_shm_create_pool(win->src.shm, fd, (i32)size);
+		win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4,
+			WL_SHM_FORMAT_ARGB8888);
+		wl_shm_pool_destroy(pool);
+
+		close(fd);
+
+		wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0);
+		wl_surface_commit(win->src.surface);
+
+		u8 color[] = {0x00, 0x00, 0x00, 0xFF};
+
+		size_t i;
+		for (i = 0; i < area.w * area.h * 4; i += 4) {
+			RGFW_MEMCPY(&win->buffer[i], color, 4);
+		}
+
+		RGFW_MEMCPY(win->src.buffer, win->buffer, (size_t)(win->r.w * win->r.h * 4));
+
+		#if defined(RGFW_OSMESA)
+				win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
+				OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
+				OSMesaPixelStore(OSMESA_Y_UP, 0);
+		#endif
+	#endif
+#else
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL{}
+	#endif
+
+	RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area);
+#endif
+}
+
+#define RGFW_LOAD_ATOM(name) \
+	static Atom name = 0; \
+	if (name == 0) name = XInternAtom(_RGFW.display, #name, False);
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+	RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
+
+	RGFW_GOTO_WAYLAND(0);
+	#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(_MOTIF_WM_HINTS);
+
+	struct __x11WindowHints {
+		unsigned long flags, functions, decorations, status;
+		long input_mode;
+	} hints;
+	hints.flags = 2;
+	hints.decorations = border;
+
+	XChangeProperty(win->src.display, win->src.window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32,
+				PropModeReplace, (u8*)&hints, 5
+	);
+
+	if (RGFW_window_isHidden(win) == 0) {
+		RGFW_window_hide(win);
+		RGFW_window_show(win);
+	}
+
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(win); RGFW_UNUSED(border);
+	#endif
+}
+
+void RGFW_releaseCursor(RGFW_window* win) {
+RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	XUngrabPointer(win->src.display, CurrentTime);
+
+	/* disable raw input */
+	unsigned char mask[] = { 0 };
+	XIEventMask em;
+	em.deviceid = XIAllMasterDevices;
+	em.mask_len = sizeof(mask);
+	em.mask = mask;
+
+	XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
+RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	/* enable raw input */
+	unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
+	XISetMask(mask, XI_RawMotion);
+
+	XIEventMask em;
+	em.deviceid = XIAllMasterDevices;
+	em.mask_len = sizeof(mask);
+	em.mask = mask;
+
+	XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1);
+
+	XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
+	RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2)));
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(win); RGFW_UNUSED(r);
+#endif
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+	void* ptr = dlsym(proc, #name); \
+	if (ptr != NULL) memcpy(&name##SRC, &ptr, sizeof(PFN_##name)); \
+}
+
+#ifdef RGFW_X11
+void RGFW_window_getVisual(RGFW_window* win) {
+#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
+		i32* visual_attribs = RGFW_initFormatAttribs();
+		i32 fbcount;
+		GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), visual_attribs, &fbcount);
+
+		i32 best_fbc = -1;
+		i32 best_depth = 0;
+		i32 best_samples = 0;
+
+		if (fbcount == 0) {
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to find any valid GLX visual configs");
+			return;
+		}
+
+        i32 i;
+		for (i = 0; i < fbcount; i++) {
+			XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]);
+			if (vi == NULL)
+				continue;
+
+			i32 samp_buf, samples;
+			glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+			glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples);
+
+			if (best_fbc == -1) best_fbc = i;
+			if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32)  && best_depth == 0) {
+				best_fbc = i;
+				best_depth = vi->depth;
+			}
+			if ((!(win->_flags & RGFW_windowTransparent) || vi->depth == 32) && samples <= RGFW_GL_HINTS[RGFW_glSamples] && samples > best_samples) {
+				best_fbc = i;
+				best_depth = vi->depth;
+				best_samples = samples;
+			}
+			XFree(vi);
+		}
+
+		if (best_fbc == -1) {
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to get a valid GLX visual");
+			return;
+        }		
+
+		win->src.bestFbc = fbc[best_fbc];
+		XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, win->src.bestFbc);
+		if (vi->depth != 32 && (win->_flags & RGFW_windowTransparent))
+			RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to to find a matching visual with a 32-bit depth");
+
+		if (best_samples < RGFW_GL_HINTS[RGFW_glSamples])
+			RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load matching sampiling");
+
+        int configCaveat;
+        if (glXGetFBConfigAttrib(win->src.display, win->src.bestFbc, GLX_CONFIG_CAVEAT, &configCaveat) == Success &&
+            configCaveat == GLX_SLOW_CONFIG) {
+                win->_flags |= RGFW_windowOpenglSoftware;
+        }
+
+		XFree(fbc);
+        win->src.visual = *vi;
+		XFree(vi);
+#else
+	win->src.visual.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display));
+	win->src.visual.depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display));
+	if (win->_flags & RGFW_windowTransparent) {
+		XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, &win->src.visual); /*!< for RGBA backgrounds */
+		if (win->src.visual.depth != 32)
+		RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Failed to load a 32-bit depth");
+	}
+#endif
+}
+#endif
+#ifndef RGFW_EGL
+void RGFW_window_initOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+        i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 };
+		context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB;
+		if (RGFW_GL_HINTS[RGFW_glProfile] == RGFW_glCore)
+			context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
+		else
+			context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+
+		if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) {
+			context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB;
+			context_attribs[3] = RGFW_GL_HINTS[RGFW_glMajor];
+			context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB;
+			context_attribs[5] = RGFW_GL_HINTS[RGFW_glMinor];
+		}
+
+		glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+		glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
+			glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB");
+
+		GLXContext ctx = NULL;
+		if (_RGFW.root != NULL && _RGFW.root != win) {
+			ctx = _RGFW.root->src.ctx;
+            RGFW_window_makeCurrent_OpenGL(_RGFW.root);
+        }
+
+		if (glXCreateContextAttribsARB == NULL) {
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to load proc address 'glXCreateContextAttribsARB', loading a generic opengl context");
+			win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True);
+		}
+		else {
+				win->src.ctx = glXCreateContextAttribsARB(win->src.display, win->src.bestFbc, ctx, True, context_attribs);
+				XSync(win->src.display, False);
+				if (win->src.ctx == NULL) {
+					RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "failed to create an opengl context with AttribsARB, loading a generic opengl context");
+					win->src.ctx = glXCreateContext(win->src.display, &win->src.visual, ctx, True);
+            }
+		}
+        
+        glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);    
+        RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_window_freeOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+	if (win->src.ctx == NULL) return;
+	glXDestroyContext(win->src.display, win->src.ctx);
+	win->src.ctx = NULL;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
+#else
+RGFW_UNUSED(win);
+#endif
+}
+#endif
+
+
+i32 RGFW_init(void) {
+    RGFW_GOTO_WAYLAND(1);
+#if defined(RGFW_C89) || defined(__cplusplus)
+    if (_RGFW_init) return 0; 
+    _RGFW_init = RGFW_TRUE;    
+    _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
+#endif
+
+#ifdef RGFW_X11
+    if (_RGFW.windowCount != -1) return 0;
+    #ifdef RGFW_USE_XDL
+		XDL_init();
+	#endif
+
+	#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD)
+	#if defined(__CYGWIN__)
+				RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so");
+	#elif defined(__OpenBSD__) || defined(__NetBSD__)
+				RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so");
+	#else
+				RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1");
+	#endif
+		RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate);
+		RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy);
+		RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor);
+	#endif
+
+	#if !defined(RGFW_NO_X11_XI_PRELOAD)
+	#if defined(__CYGWIN__)
+			RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so");
+	#elif defined(__OpenBSD__) || defined(__NetBSD__)
+			RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so");
+	#else
+			RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6");
+	#endif
+			RGFW_PROC_DEF(X11Xihandle, XISelectEvents);
+	#endif
+
+	#if !defined(RGFW_NO_X11_EXT_PRELOAD)
+	#if defined(__CYGWIN__)
+			RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext-6.so");
+	#elif defined(__OpenBSD__) || defined(__NetBSD__)
+	        RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so");
+	#else
+			RGFW_LOAD_LIBRARY(X11XEXThandle, "libXext.so.6");
+	#endif
+			RGFW_PROC_DEF(X11XEXThandle, XSyncCreateCounter);
+			RGFW_PROC_DEF(X11XEXThandle, XSyncIntToValue);
+			RGFW_PROC_DEF(X11XEXThandle, XSyncSetCounter);
+    	    RGFW_PROC_DEF(X11XEXThandle, XShapeCombineRegion);
+	        RGFW_PROC_DEF(X11XEXThandle, XShapeCombineMask);
+    #endif
+
+	XInitThreads(); /*!< init X11 threading */
+    _RGFW.display = XOpenDisplay(0);
+    XSetWindowAttributes wa;
+    RGFW_MEMSET(&wa, 0, sizeof(wa));
+    wa.event_mask = PropertyChangeMask;
+    _RGFW.helperWindow = XCreateWindow(_RGFW.display, XDefaultRootWindow(_RGFW.display), 0, 0, 1, 1, 0, 0,
+                                        InputOnly, DefaultVisual(_RGFW.display, DefaultScreen(_RGFW.display)), CWEventMask, &wa);
+
+    _RGFW.windowCount = 0;
+    u8 RGFW_blk[] = { 0, 0, 0, 0 };
+	_RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4);
+	_RGFW.clipboard = NULL;
+
+    XkbComponentNamesRec rec;
+    XkbDescPtr desc = XkbGetMap(_RGFW.display, 0, XkbUseCoreKbd);
+    XkbDescPtr evdesc;
+    u8 old[sizeof(RGFW_keycodes) / sizeof(RGFW_keycodes[0])];
+
+    XkbGetNames(_RGFW.display, XkbKeyNamesMask, desc);
+
+    RGFW_MEMSET(&rec, 0, sizeof(rec));
+    rec.keycodes = (char*)"evdev";
+    evdesc = XkbGetKeyboardByName(_RGFW.display, XkbUseCoreKbd, &rec, XkbGBN_KeyNamesMask, XkbGBN_KeyNamesMask, False);
+    /* memo: RGFW_keycodes[x11 keycode] = rgfw keycode */
+    if(evdesc != NULL && desc != NULL){
+        for(int i = 0; i < (int)sizeof(RGFW_keycodes) / (int)sizeof(RGFW_keycodes[0]); i++){
+    	    old[i] = RGFW_keycodes[i];
+    	    RGFW_keycodes[i] = 0;
+        }
+        for(int i = evdesc->min_key_code; i <= evdesc->max_key_code; i++){
+    	    for(int j = desc->min_key_code; j <= desc->max_key_code; j++){
+    		if(strncmp(evdesc->names->keys[i].name, desc->names->keys[j].name, XkbKeyNameLength) == 0){
+    			RGFW_keycodes[j] = old[i];
+    			break;
+    		}
+    	    }
+        }
+	XkbFreeKeyboard(desc, 0, True);
+	XkbFreeKeyboard(evdesc, 0, True);
+    }
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL
+    _RGFW.wl_display = wl_display_connect(NULL);
+#endif
+    _RGFW.windowCount = 0;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
+    return 0;
+}
+
+
+RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
+	RGFW_window_basic_init(win, rect, flags);
+
+#ifdef RGFW_WAYLAND
+	win->src.compositor = NULL;
+#endif
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	i64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted */
+
+    win->src.display = XOpenDisplay(NULL);
+    RGFW_window_getVisual(win);
+
+    /* make X window attrubutes */
+	XSetWindowAttributes swa;
+    RGFW_MEMSET(&swa, 0, sizeof(swa));
+
+	Colormap cmap;
+	swa.colormap = cmap = XCreateColormap(win->src.display,
+		DefaultRootWindow(win->src.display),
+		win->src.visual.visual, AllocNone);
+	swa.event_mask = event_mask;
+
+	/* create the window */
+    win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, (u32)win->r.w, (u32)win->r.h,
+		0, win->src.visual.depth, InputOutput, win->src.visual.visual,
+		CWColormap | CWBorderPixel | CWEventMask, &swa);
+
+	XFreeColors(win->src.display, cmap, NULL, 0, 0);
+
+	win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL);
+
+	/* In your .desktop app, if you set the property
+	    StartupWMClass=RGFW that will assoicate the launcher icon
+	     with your application - robrohan */
+	if (RGFW_className == NULL)
+		RGFW_className = (char*)name;
+
+	XClassHint hint;
+	hint.res_class = (char*)RGFW_className;
+	if (RGFW_instName == NULL)	hint.res_name = (char*)name;
+	else 						hint.res_name = (char*)RGFW_instName;
+	XSetClassHint(win->src.display, win->src.window, &hint);
+
+	#ifndef RGFW_NO_MONITOR
+	if (flags & RGFW_windowScaleToMonitor)
+		RGFW_window_scaleToMonitor(win);
+	#endif
+	XSelectInput(win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want */
+
+	/* make it so the user can't close the window until the program does */
+	if (wm_delete_window == 0) {
+		wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False);
+		RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False);
+		RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False);
+	}
+
+	XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1);
+	/* set the background */
+	RGFW_window_setName(win, name);
+
+	XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords */
+
+	if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */
+		win->_flags |= RGFW_windowAllowDND;
+
+		/* actions */
+		XtextUriList = XInternAtom(win->src.display, "text/uri-list", False);
+		XtextPlain = XInternAtom(win->src.display, "text/plain", False);
+		XdndAware = XInternAtom(win->src.display, "XdndAware", False);
+		const u8 version = 5;
+
+		XChangeProperty(win->src.display, win->src.window,
+			XdndAware, 4, 32,
+			PropModeReplace, &version, 1); /*!< turns on drag and drop */
+	}
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+    RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST_COUNTER)
+    RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST)
+    Atom protcols[2] = {_NET_WM_SYNC_REQUEST, wm_delete_window};
+    XSetWMProtocols(win->src.display, win->src.window, protcols, 2);
+
+    XSyncValue initial_value;
+    XSyncIntToValue(&initial_value, 0);
+    win->src.counter = XSyncCreateCounter(win->src.display, initial_value);
+
+    XChangeProperty(win->src.display, win->src.window, _NET_WM_SYNC_REQUEST_COUNTER, XA_CARDINAL, 32, PropModeReplace, (uint8_t*)&win->src.counter, 1);
+#endif
+
+	if ((flags & RGFW_windowNoInitAPI) == 0) {
+		RGFW_window_initOpenGL(win);
+        RGFW_window_initBuffer(win);
+    }
+
+    RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
+	RGFW_window_setMouseDefault(win);
+	RGFW_window_setFlags(win, flags);
+    
+    win->src.r = win->r;
+
+	RGFW_window_show(win);
+    return win; /*return newly created window */
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "RGFW Wayland support is experimental");
+
+	win->src.wl_display = _RGFW.wl_display;
+	if (win->src.wl_display == NULL) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Failed to load Wayland display");
+		#ifdef RGFW_X11
+			RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningWayland, RGFW_DEBUG_CTX(win, 0), "Falling back to X11");
+			RGFW_useWayland(0);
+			return RGFW_createWindowPtr(name, rect, flags, win);
+		#endif
+		return NULL;
+	}
+
+
+	#ifdef RGFW_X11
+        win->src.display = _RGFW.display;
+		win->src.window = _RGFW.helperWindow;
+        XMapWindow(_RGFW.display, win->src.window);
+		XFlush(win->src.display);
+		if (wm_delete_window == 0) {
+			wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False);
+			RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False);
+			RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False);
+		}
+	#endif
+
+	struct wl_registry *registry = wl_display_get_registry(win->src.wl_display);
+	wl_registry_add_listener(registry, &registry_listener, win);
+
+	wl_display_roundtrip(win->src.wl_display);
+	wl_display_dispatch(win->src.wl_display);
+
+	if (win->src.compositor == NULL) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errWayland, RGFW_DEBUG_CTX(win, 0), "Can't find compositor.");
+		return NULL;
+	}
+
+	if (RGFW_wl_cursor_theme == NULL) {
+		RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm);
+		RGFW_cursor_surface = wl_compositor_create_surface(win->src.compositor);
+
+		struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr");
+		RGFW_cursor_image = cursor->images[0];
+		struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
+
+		wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
+		wl_surface_commit(RGFW_cursor_surface);
+	}
+
+	xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL);
+
+	xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+	win->src.surface = wl_compositor_create_surface(win->src.compositor);
+	wl_surface_set_user_data(win->src.surface, win);
+
+	win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface);
+	xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL);
+
+	xdg_wm_base_set_user_data(win->src.xdg_wm_base, win);
+
+	win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface);
+	xdg_toplevel_set_user_data(win->src.xdg_toplevel, win);
+	xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+	xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
+
+	if (!(flags & RGFW_windowNoBorder)) {
+		win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
+					decoration_manager, win->src.xdg_toplevel);
+	}
+
+	wl_display_roundtrip(win->src.wl_display);
+
+	wl_surface_commit(win->src.surface);
+	RGFW_window_show(win);
+
+	/* wait for the surface to be configured */
+	while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { }
+
+    if ((flags & RGFW_windowNoInitAPI) == 0) {
+        RGFW_window_initOpenGL(win);
+        RGFW_window_initBuffer(win);
+    }
+	struct wl_callback* callback = wl_surface_frame(win->src.surface);
+	wl_callback_add_listener(callback, &wl_surface_frame_listener, win);
+	wl_surface_commit(win->src.surface);
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
+
+	#ifndef RGFW_NO_MONITOR
+	if (flags & RGFW_windowScaleToMonitor)
+		RGFW_window_scaleToMonitor(win);
+	#endif
+    
+    RGFW_window_setName(win, name);
+	RGFW_window_setMouseDefault(win);
+	RGFW_window_setFlags(win, flags);
+	return win; /* return newly created window */
+#endif
+}
+
+RGFW_area RGFW_getScreenSize(void) {
+	RGFW_GOTO_WAYLAND(1);
+	RGFW_init();
+
+	#ifdef RGFW_X11
+	Screen* scrn = DefaultScreenOfDisplay(_RGFW.display);
+	return RGFW_AREA(scrn->width, scrn->height);
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL return RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h); /* TODO */
+	#endif
+}
+
+RGFW_point RGFW_getGlobalMousePoint(void) {
+	RGFW_init();
+	RGFW_point RGFWMouse = RGFW_POINT(0, 0);
+    RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+	i32 x, y;
+	u32 z;
+	Window window1, window2;
+	XQueryPointer(_RGFW.display, XDefaultRootWindow(_RGFW.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z);
+	return RGFWMouse;
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+    return RGFWMouse;
+#endif
+}
+
+RGFWDEF void RGFW_XHandleClipboardSelection(XEvent* event);
+void RGFW_XHandleClipboardSelection(XEvent* event) { RGFW_UNUSED(event);
+#ifdef RGFW_X11
+    RGFW_LOAD_ATOM(ATOM_PAIR);
+	RGFW_LOAD_ATOM(MULTIPLE);
+	RGFW_LOAD_ATOM(TARGETS);
+	RGFW_LOAD_ATOM(SAVE_TARGETS);
+
+    const XSelectionRequestEvent* request = &event->xselectionrequest;
+    const Atom formats[] = { RGFW_XUTF8_STRING, XA_STRING };
+    const int formatCount = sizeof(formats) / sizeof(formats[0]);
+
+    if (request->target == TARGETS) {
+        const Atom targets[] = { TARGETS, MULTIPLE, RGFW_XUTF8_STRING, XA_STRING };
+
+        XChangeProperty(_RGFW.display, request->requestor, request->property,
+                        XA_ATOM, 32, PropModeReplace, (u8*) targets, sizeof(targets) / sizeof(Atom));
+    }  else if (request->target == MULTIPLE) {
+		Atom* targets = NULL;
+
+		Atom actualType = 0;
+		int actualFormat = 0;
+		unsigned long count = 0, bytesAfter = 0;
+
+		XGetWindowProperty(_RGFW.display, request->requestor, request->property, 0, LONG_MAX,
+							False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets);
+
+		unsigned long i;
+		for (i = 0; i < (u32)count; i += 2) {
+			if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING)
+				XChangeProperty(_RGFW.display, request->requestor, targets[i + 1], targets[i],
+					8, PropModeReplace, (const unsigned char *)_RGFW.clipboard, (i32)_RGFW.clipboard_len);
+			else
+				targets[i + 1] = None;
+		}
+
+		XChangeProperty(_RGFW.display,
+			request->requestor, request->property, ATOM_PAIR, 32,
+			PropModeReplace, (u8*) targets, (i32)count);
+
+		XFlush(_RGFW.display);
+		XFree(targets);
+	} else if (request->target == SAVE_TARGETS)
+        XChangeProperty(_RGFW.display, request->requestor, request->property, 0, 32, PropModeReplace, NULL, 0);
+    else {
+        int i;
+        for (i = 0;  i < formatCount;  i++) {
+			if (request->target != formats[i])
+				continue;
+			XChangeProperty(_RGFW.display, request->requestor, request->property, request->target,
+								8, PropModeReplace, (u8*) _RGFW.clipboard, (i32)_RGFW.clipboard_len);
+		}
+	}
+
+    XEvent reply = { SelectionNotify };
+    reply.xselection.property = request->property;
+    reply.xselection.display = request->display;
+    reply.xselection.requestor = request->requestor;
+    reply.xselection.selection = request->selection;
+    reply.xselection.target = request->target;
+    reply.xselection.time = request->time;
+
+    XSendEvent(_RGFW.display, request->requestor, False, 0, &reply);
+#endif
+}
+
+char* RGFW_strtok(char* str, const char* delimStr);
+char* RGFW_strtok(char* str, const char* delimStr) {
+    static char* static_str = NULL;
+
+    if (str != NULL)
+        static_str = str;
+
+    if (static_str == NULL) {
+        return NULL;
+    }
+
+    while (*static_str != '\0') {
+        RGFW_bool delim = 0;
+        const char* d;
+        for (d = delimStr; *d != '\0'; d++) {
+            if (*static_str == *d) {
+                delim = 1;
+                break;
+            }
+        }
+        if (!delim)
+            break;
+        static_str++;
+    }
+
+    if (*static_str == '\0')
+        return NULL;
+
+    char* token_start = static_str;
+    while (*static_str != '\0') {
+        int delim = 0;
+        const char* d;
+        for (d = delimStr; *d != '\0'; d++) {
+            if (*static_str == *d) {
+                delim = 1;
+                break;
+            }
+        }
+
+        if (delim) {
+            *static_str = '\0';
+            static_str++;
+            break;
+        }
+        static_str++;
+    }
+
+    return token_start;
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void);
+
+
+u8 RGFW_rgfwToKeyChar(u32 key) {
+    u32 keycode = RGFW_rgfwToApiKey(key);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    Window root = DefaultRootWindow(_RGFW.display);
+    Window ret_root, ret_child;
+    int root_x, root_y, win_x, win_y;
+    unsigned int mask;
+    XQueryPointer(_RGFW.display, root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
+    KeySym sym = (KeySym)XkbKeycodeToKeysym(_RGFW.display, (KeyCode)keycode, 0, (KeyCode)mask & ShiftMask ? 1 : 0);
+
+    if ((mask & LockMask) && sym >= XK_a && sym <= XK_z)
+        sym = (mask & ShiftMask) ? sym + 32 : sym - 32;
+    if ((u8)sym != (u32)sym)
+        sym = 0;
+
+    return (u8)sym;
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL RGFW_UNUSED(keycode);
+    return (u8)key;
+#endif
+}
+
+RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
+    RGFW_XHandleClipboardSelectionHelper();
+
+    if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
+    RGFW_event* ev = RGFW_window_checkEventCore(win);
+	if (ev) return ev;
+
+	#if defined(__linux__) && !defined(RGFW_NO_LINUX)
+		if (RGFW_linux_updateGamepad(win)) return &win->event;
+	#endif
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(XdndTypeList);
+	RGFW_LOAD_ATOM(XdndSelection);
+	RGFW_LOAD_ATOM(XdndEnter);
+	RGFW_LOAD_ATOM(XdndPosition);
+	RGFW_LOAD_ATOM(XdndStatus);
+	RGFW_LOAD_ATOM(XdndLeave);
+	RGFW_LOAD_ATOM(XdndDrop);
+	RGFW_LOAD_ATOM(XdndFinished);
+	RGFW_LOAD_ATOM(XdndActionCopy);
+    RGFW_LOAD_ATOM(_NET_WM_SYNC_REQUEST);
+    RGFW_LOAD_ATOM(WM_PROTOCOLS);
+	XPending(win->src.display);
+
+	XEvent E; /*!< raw X11 event */
+
+    /* if there is no unread qued events, get a new one */
+	if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading))
+		&& win->event.type != RGFW_quit
+	)
+		XNextEvent(win->src.display, &E);
+	else {
+		return NULL;
+	}
+
+	win->event.type = 0;
+
+	/* xdnd data */
+	static Window source = 0;
+	static long version = 0;
+	static i32 format = 0;
+
+	XEvent reply = { ClientMessage };
+
+	switch (E.type) {
+	case KeyPress:
+	case KeyRelease: {
+		win->event.repeat = RGFW_FALSE;
+		/* check if it's a real key release */
+		if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one */
+			XEvent NE;
+			XPeekEvent(win->src.display, &NE);
+
+			if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same */
+				win->event.repeat = RGFW_TRUE;
+		}
+
+		/* set event key data */
+		win->event.key = (u8)RGFW_apiKeyToRGFW(E.xkey.keycode);
+		win->event.keyChar = (u8)RGFW_rgfwToKeyChar(win->event.key);
+
+		RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
+
+		/* get keystate data */
+		win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased;
+
+        XKeyboardState keystate;
+		XGetKeyboardControl(win->src.display, &keystate);
+
+		RGFW_keyboard[win->event.key].current = (E.type == KeyPress);
+
+		XkbStateRec state;
+		XkbGetState(win->src.display, XkbUseCoreKbd, &state);
+		RGFW_updateKeyMods(win, (state.locked_mods & LockMask), (state.locked_mods & Mod2Mask), (state.locked_mods & Mod3Mask));
+
+		RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress));
+		break;
+	}
+	case ButtonPress:
+	case ButtonRelease:
+		if (E.xbutton.button > RGFW_mouseFinal) { /* skip this event */
+			XFlush(win->src.display);
+			return RGFW_window_checkEvent(win);
+		}
+
+		win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); /* the events match */
+		win->event.button = (u8)(E.xbutton.button - 1);
+		switch(win->event.button) {
+			case RGFW_mouseScrollUp:
+				win->event.scroll = 1;
+				break;
+			case RGFW_mouseScrollDown:
+				win->event.scroll = -1;
+				break;
+			default: break;
+		}
+
+		RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+
+		if (win->event.repeat == RGFW_FALSE)
+			win->event.repeat = RGFW_isPressed(win, win->event.key);
+
+		RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress);
+		RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress));
+		break;
+
+	case MotionNotify:
+		win->event.point.x = E.xmotion.x;
+		win->event.point.y = E.xmotion.y;
+
+		win->event.vector.x = win->event.point.x - win->_lastMousePoint.x;
+		win->event.vector.y = win->event.point.y - win->_lastMousePoint.y;
+		win->_lastMousePoint = win->event.point;
+
+		win->event.type = RGFW_mousePosChanged;
+		RGFW_mousePosCallback(win, win->event.point, win->event.vector);
+		break;
+
+	case GenericEvent: {
+		/* MotionNotify is used for mouse events if the mouse isn't held */
+		if (!(win->_flags & RGFW_HOLD_MOUSE)) {
+			XFreeEventData(win->src.display, &E.xcookie);
+			break;
+		}
+
+		XGetEventData(win->src.display, &E.xcookie);
+		if (E.xcookie.evtype == XI_RawMotion) {
+			XIRawEvent *raw = (XIRawEvent *)E.xcookie.data;
+			if (raw->valuators.mask_len == 0) {
+				XFreeEventData(win->src.display, &E.xcookie);
+				break;
+			}
+
+			double deltaX = 0.0f;
+			double deltaY = 0.0f;
+
+			/* check if relative motion data exists where we think it does */
+			if (XIMaskIsSet(raw->valuators.mask, 0) != 0)
+				deltaX += raw->raw_values[0];
+			if (XIMaskIsSet(raw->valuators.mask, 1) != 0)
+				deltaY += raw->raw_values[1];
+
+			win->event.vector = RGFW_POINT((i32)deltaX, (i32)deltaY);
+			win->event.point.x = win->_lastMousePoint.x + win->event.vector.x;
+			win->event.point.y = win->_lastMousePoint.y + win->event.vector.y;
+			win->_lastMousePoint = win->event.point;
+
+			RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2)));
+
+			win->event.type = RGFW_mousePosChanged;
+			RGFW_mousePosCallback(win, win->event.point, win->event.vector);
+		}
+
+		XFreeEventData(win->src.display, &E.xcookie);
+		break;
+	}
+
+	case Expose: {
+		win->event.type = RGFW_windowRefresh;
+		RGFW_windowRefreshCallback(win);
+
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+        XSyncValue value;
+        XSyncIntToValue(&value, (i32)win->src.counter_value);
+        XSyncSetCounter(win->src.display, win->src.counter, value);
+#endif
+        break;
+    }
+	case MapNotify: case UnmapNotify: 		RGFW_window_checkMode(win); break;
+	case ClientMessage: {
+		/* if the client closed the window */
+		if (E.xclient.data.l[0] == (long)wm_delete_window) {
+			win->event.type = RGFW_quit;
+			RGFW_window_setShouldClose(win, RGFW_TRUE);
+			RGFW_windowQuitCallback(win);
+			break;
+		}
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+        if (E.xclient.message_type == WM_PROTOCOLS && (Atom)E.xclient.data.l[0] == _NET_WM_SYNC_REQUEST) {
+		    RGFW_windowRefreshCallback(win);
+            win->src.counter_value = 0;
+            win->src.counter_value |= E.xclient.data.l[2];
+            win->src.counter_value |= (E.xclient.data.l[3] << 32);
+
+            XSyncValue value;
+            XSyncIntToValue(&value, (i32)win->src.counter_value);
+            XSyncSetCounter(win->src.display, win->src.counter, value);
+            break;
+        }
+#endif
+        if ((win->_flags & RGFW_windowAllowDND) == 0)
+			break;
+
+        reply.xclient.window = source;
+		reply.xclient.format = 32;
+		reply.xclient.data.l[0] = (long)win->src.window;
+		reply.xclient.data.l[1] = 0;
+		reply.xclient.data.l[2] = None;
+
+		if (E.xclient.message_type == XdndEnter) {
+            if (version > 5)
+				break;
+
+			unsigned long count;
+			Atom* formats;
+			Atom real_formats[6];
+			Bool list = E.xclient.data.l[1] & 1;
+
+			source = (unsigned long int)E.xclient.data.l[0];
+			version = E.xclient.data.l[1] >> 24;
+			format = None;
+			if (list) {
+				Atom actualType;
+				i32 actualFormat;
+				unsigned long bytesAfter;
+
+				XGetWindowProperty(
+					win->src.display, source, XdndTypeList,
+					0, LONG_MAX, False, 4,
+					&actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats
+				);
+			} else {
+				count = 0;
+
+                size_t i;
+				for (i = 2; i < 5; i++) {
+					if (E.xclient.data.l[i] != None) {
+						real_formats[count] = (unsigned long int)E.xclient.data.l[i];
+						count += 1;
+					}
+				}
+
+				formats = real_formats;
+			}
+
+            size_t i;
+			for (i = 0; i < count; i++) {
+				if (formats[i] == XtextUriList || formats[i] == XtextPlain) {
+					format = (int)formats[i];
+					break;
+				}
+			}
+
+			if (list) {
+				XFree(formats);
+			}
+
+			break;
+		}
+
+		if (E.xclient.message_type == XdndPosition) {
+            const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff;
+			const i32 yabs = (E.xclient.data.l[2]) & 0xffff;
+			Window dummy;
+			i32 xpos, ypos;
+
+			if (version > 5)
+				break;
+
+			XTranslateCoordinates(
+				win->src.display, XDefaultRootWindow(win->src.display), win->src.window,
+				xabs, yabs, &xpos, &ypos, &dummy
+			);
+
+			win->event.point.x = xpos;
+			win->event.point.y = ypos;
+
+			reply.xclient.window = source;
+			reply.xclient.message_type = XdndStatus;
+
+			if (format) {
+				reply.xclient.data.l[1] = 1;
+				if (version >= 2)
+					reply.xclient.data.l[4] = (long)XdndActionCopy;
+			}
+
+			XSendEvent(win->src.display, source, False, NoEventMask, &reply);
+			XFlush(win->src.display);
+			break;
+		}
+		if (E.xclient.message_type != XdndDrop)
+			break;
+
+		if (version > 5)
+			break;
+
+	        size_t i;
+		for (i = 0; i < win->event.droppedFilesCount; i++)
+			win->event.droppedFiles[i][0] = '\0';
+
+		win->event.droppedFilesCount = 0;
+
+
+		win->event.type = RGFW_DNDInit;
+
+		if (format) {
+			Time time = (version >= 1)
+				? (Time)E.xclient.data.l[2]
+				: CurrentTime;
+
+			XConvertSelection(
+				win->src.display, XdndSelection, (Atom)format,
+				XdndSelection, win->src.window, time
+			);
+		} else if (version >= 2) {
+			XEvent new_reply = { ClientMessage };
+
+			XSendEvent(win->src.display, source, False, NoEventMask, &new_reply);
+			XFlush(win->src.display);
+		}
+
+		RGFW_dndInitCallback(win, win->event.point);
+	} break;
+	case SelectionRequest:
+		RGFW_XHandleClipboardSelection(&E);
+		XFlush(win->src.display);
+		return RGFW_window_checkEvent(win);
+	case SelectionNotify: {
+		/* this is only for checking for xdnd drops */
+        if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND))
+			break;
+		char* data;
+		unsigned long result;
+
+		Atom actualType;
+		i32 actualFormat;
+		unsigned long bytesAfter;
+
+		XGetWindowProperty(win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data);
+
+		if (result == 0)
+			break;
+
+		const char* prefix = (const char*)"file://";
+
+		char* line;
+
+		win->event.droppedFilesCount = 0;
+		win->event.type = RGFW_DND;
+
+		while ((line = (char*)RGFW_strtok(data, "\r\n"))) {
+			char path[RGFW_MAX_PATH];
+
+			data = NULL;
+
+			if (line[0] == '#')
+				continue;
+
+			char* l;
+			for (l = line; 1; l++) {
+				if ((l - line) > 7)
+					break;
+				else if (*l != prefix[(l - line)])
+					break;
+				else if (*l == '\0' && prefix[(l - line)] == '\0') {
+					line += 7;
+					while (*line != '/')
+						line++;
+					break;
+				} else if (*l == '\0')
+					break;
+			}
+
+			win->event.droppedFilesCount++;
+
+			size_t index = 0;
+			while (*line) {
+				if (line[0] == '%' && line[1] && line[2]) {
+					const char digits[3] = { line[1], line[2], '\0' };
+					path[index] = (char) RGFW_STRTOL(digits, NULL, 16);
+					line += 2;
+				} else
+					path[index] = *line;
+
+				index++;
+				line++;
+			}
+			path[index] = '\0';
+			RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1);
+		}
+
+		RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+		if (data)
+			XFree(data);
+
+		if (version >= 2) {
+            XEvent new_reply = { ClientMessage };
+            new_reply.xclient.window = source;
+            new_reply.xclient.message_type = XdndFinished;
+            new_reply.xclient.format = 32;
+			new_reply.xclient.data.l[1] = (long int)result;
+			new_reply.xclient.data.l[2] = (long int)XdndActionCopy;
+            XSendEvent(win->src.display, source, False, NoEventMask, &new_reply);
+			XFlush(win->src.display);
+		}
+		break;
+	}
+	case FocusIn:
+		if ((win->_flags & RGFW_windowFullscreen))
+			XMapRaised(win->src.display, win->src.window);
+
+		win->_flags |= RGFW_windowFocus;
+		win->event.type = RGFW_focusIn;
+		RGFW_focusCallback(win, 1);
+
+
+	    if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h));
+        break;
+	case FocusOut:
+        win->event.type = RGFW_focusOut;
+		RGFW_focusCallback(win, 0);
+        RGFW_window_focusLost(win);
+		break;
+	case PropertyNotify: RGFW_window_checkMode(win); break;
+	case EnterNotify: {
+		win->event.type = RGFW_mouseEnter;
+		win->event.point.x = E.xcrossing.x;
+		win->event.point.y = E.xcrossing.y;
+		RGFW_mouseNotifyCallback(win, win->event.point, 1);
+		break;
+	}
+
+	case LeaveNotify: {
+		win->event.type = RGFW_mouseLeave;
+		RGFW_mouseNotifyCallback(win, win->event.point, 0);
+		break;
+	}
+
+	case ConfigureNotify: {
+		/* detect resize */
+		RGFW_window_checkMode(win);
+		if (E.xconfigure.width != win->src.r.w || E.xconfigure.height != win->src.r.h) {
+			win->event.type = RGFW_windowResized;
+			win->src.r = win->r = RGFW_RECT(win->src.r.x, win->src.r.y, E.xconfigure.width, E.xconfigure.height);
+			RGFW_windowResizedCallback(win, win->r);
+			break;
+		}
+
+		/* detect move */
+		if (E.xconfigure.x != win->src.r.x || E.xconfigure.y != win->src.r.y) {
+			win->event.type = RGFW_windowMoved;
+            win->src.r = win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->src.r.w, win->src.r.h);
+			RGFW_windowMovedCallback(win, win->r);
+			break;
+		}
+
+		break;
+	}
+	default:
+		XFlush(win->src.display);
+		return RGFW_window_checkEvent(win);
+	}
+	XFlush(win->src.display);
+	if (win->event.type) return &win->event;
+	else return NULL;
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	if ((win->_flags & RGFW_windowHide) == 0)
+        wl_display_roundtrip(win->src.wl_display);
+	return NULL;
+#endif
+}
+
+void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+	RGFW_ASSERT(win != NULL);
+	win->r.x = v.x;
+	win->r.y = v.y;
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	XMoveWindow(win->src.display, win->src.window, v.x, v.y);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	RGFW_ASSERT(win != NULL);
+
+	if (win->src.compositor) {
+		struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat);
+			if (!pointer) {
+				return;
+			}
+
+		wl_display_flush(win->src.wl_display);
+	}
+#endif
+}
+
+
+void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+	win->r.w = (i32)a.w;
+	win->r.h = (i32)a.h;
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	XResizeWindow(win->src.display, win->src.window, a.w, a.h);
+
+	if ((win->_flags & RGFW_windowNoResize)) {
+		XSizeHints sh;
+		sh.flags = (1L << 4) | (1L << 5);
+		sh.min_width = sh.max_width = (i32)a.w;
+		sh.min_height = sh.max_height = (i32)a.h;
+
+		XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS);
+	}
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	if (win->src.compositor) {
+		xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h);
+		#ifdef RGFW_OPENGL
+		wl_egl_window_resize(win->src.eglWindow, (i32)a.w, (i32)a.h, 0, 0);
+		#endif
+	}
+#endif
+}
+
+void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+
+	if (a.w == 0 && a.h == 0)
+		return;
+#ifdef RGFW_X11
+	XSizeHints hints;
+	long flags;
+
+	XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
+
+	hints.flags |= PAspect;
+
+	hints.min_aspect.x = hints.max_aspect.x = (i32)a.w;
+	hints.min_aspect.y = hints.max_aspect.y = (i32)a.h;
+
+	XSetWMNormalHints(win->src.display, win->src.window, &hints);
+    return;
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL 
+#endif
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    long flags;
+	XSizeHints hints;
+    RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
+
+	XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
+
+	hints.flags |= PMinSize;
+
+	hints.min_width = (i32)a.w;
+	hints.min_height = (i32)a.h;
+
+	XSetWMNormalHints(win->src.display, win->src.window, &hints);
+    return;
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL RGFW_UNUSED(a);
+#endif
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    long flags;
+	XSizeHints hints;
+    RGFW_MEMSET(&hints, 0, sizeof(XSizeHints));
+
+	XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags);
+
+	hints.flags |= PMaxSize;
+
+	hints.max_width = (i32)a.w;
+	hints.max_height = (i32)a.h;
+
+	XSetWMNormalHints(win->src.display, win->src.window, &hints);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL RGFW_UNUSED(a);
+#endif
+}
+
+#ifdef RGFW_X11
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized);
+void RGFW_toggleXMaximized(RGFW_window* win, RGFW_bool maximized) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_LOAD_ATOM(_NET_WM_STATE);
+	RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+	RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+	XEvent xev = {0};
+	xev.type = ClientMessage;
+	xev.xclient.window = win->src.window;
+	xev.xclient.message_type = _NET_WM_STATE;
+	xev.xclient.format = 32;
+	xev.xclient.data.l[0] = maximized;
+	xev.xclient.data.l[1] = (long int)_NET_WM_STATE_MAXIMIZED_HORZ;
+	xev.xclient.data.l[2] = (long int)_NET_WM_STATE_MAXIMIZED_VERT;
+	xev.xclient.data.l[3] = 0;
+	xev.xclient.data.l[4] = 0;
+
+	XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+}
+#endif
+
+void RGFW_window_maximize(RGFW_window* win) {
+	win->_oldRect = win->r;
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11 
+    RGFW_toggleXMaximized(win, 1);
+    return;
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+    return;
+#endif
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+	RGFW_ASSERT(win);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    XWindowAttributes attr;
+    XGetWindowAttributes(win->src.display, win->src.window, &attr);
+    if (attr.map_state != IsViewable) return;
+
+	XSetInputFocus(win->src.display, win->src.window, RevertToPointerRoot, CurrentTime);
+	XFlush(win->src.display);
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL;
+#endif
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+	RGFW_ASSERT(win);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    XRaiseWindow(win->src.display, win->src.window);
+	XMapRaised(win->src.display, win->src.window);
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL;
+#endif
+}
+
+#ifdef RGFW_X11
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen);
+void RGFW_window_setXAtom(RGFW_window* win, Atom netAtom, RGFW_bool fullscreen) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_LOAD_ATOM(_NET_WM_STATE);
+
+	XEvent xev = {0};
+    xev.xclient.type = ClientMessage;
+    xev.xclient.serial = 0;
+    xev.xclient.send_event = True;
+    xev.xclient.message_type = _NET_WM_STATE;
+    xev.xclient.window = win->src.window;
+    xev.xclient.format = 32;
+    xev.xclient.data.l[0] = fullscreen;
+    xev.xclient.data.l[1] = (long int)netAtom;
+    xev.xclient.data.l[2] = 0;
+
+    XSendEvent(win->src.display, DefaultRootWindow(win->src.display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev);
+}
+#endif
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+    if (fullscreen) {
+		win->_flags |= RGFW_windowFullscreen;
+		win->_oldRect = win->r;
+	}
+	else win->_flags &= ~(u32)RGFW_windowFullscreen;
+#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(_NET_WM_STATE_FULLSCREEN);
+
+	RGFW_window_setXAtom(win, _NET_WM_STATE_FULLSCREEN, fullscreen);
+
+	XRaiseWindow(win->src.display, win->src.window);
+	XMapRaised(win->src.display, win->src.window);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL;
+#endif
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+    RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+	RGFW_window_setXAtom(win, _NET_WM_STATE_ABOVE, floating);
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL RGFW_UNUSED(floating);
+#endif
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    const u32 value = (u32) (0xffffffffu * (double) opacity);
+	RGFW_LOAD_ATOM(NET_WM_WINDOW_OPACITY);
+    XChangeProperty(win->src.display, win->src.window,
+					NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1);
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL RGFW_UNUSED(opacity);
+#endif
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+	if (RGFW_window_isMaximized(win)) return;
+
+	win->_oldRect = win->r;
+#ifdef RGFW_X11
+    XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display));
+	XFlush(win->src.display);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL;
+#endif
+}
+
+void RGFW_window_restore(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    RGFW_toggleXMaximized(win, 0);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+#endif
+	win->r = win->_oldRect;
+	RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y));
+	RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h));
+	
+    RGFW_window_show(win);
+#ifdef RGFW_X11	
+    XFlush(win->src.display);
+#endif
+}
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    RGFW_LOAD_ATOM(_NET_WM_STATE);
+	RGFW_LOAD_ATOM(_NET_WM_STATE_ABOVE);
+
+	Atom actual_type;
+	int actual_format;
+	unsigned long nitems, bytes_after;
+	Atom* prop_return = NULL;
+
+	int status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, (~0L), False, XA_ATOM,
+									&actual_type, &actual_format, &nitems, &bytes_after,
+									(unsigned char **)&prop_return);
+
+	if (status != Success || actual_type != XA_ATOM)
+		return RGFW_FALSE;
+
+    unsigned long i;
+	for (i = 0; i < nitems; i++)
+		if (prop_return[i] == _NET_WM_STATE_ABOVE) return RGFW_TRUE;
+
+	if (prop_return)
+		XFree(prop_return);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL RGFW_UNUSED(win);
+#endif
+	return RGFW_FALSE;
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+	#ifdef RGFW_X11
+	XStoreName(win->src.display, win->src.window, name);
+
+	RGFW_LOAD_ATOM(_NET_WM_NAME);
+
+    char buf[256];
+    RGFW_MEMSET(buf, 0, sizeof(buf));
+    RGFW_STRNCPY(buf, name, sizeof(buf) - 1);
+
+    XChangeProperty(
+		win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING,
+		8, PropModeReplace, (u8*)buf, sizeof(buf)
+	);
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	if (win->src.compositor)
+		xdg_toplevel_set_title(win->src.xdg_toplevel, name);
+	#endif
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    if (passthrough) {
+		Region region = XCreateRegion();
+		XShapeCombineRegion(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet);
+		XDestroyRegion(region);
+
+		return;
+	}
+
+	XShapeCombineMask(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet);
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL RGFW_UNUSED(passthrough);
+#endif
+}
+#endif /* RGFW_NO_PASSTHROUGH */
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(_NET_WM_ICON);
+	if (icon == NULL || (channels != 3 && channels != 4)) {
+		RGFW_bool res = (RGFW_bool)XChangeProperty(
+			win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+			PropModeReplace, (u8*)NULL, 0
+		);
+		return res;
+	}
+
+	i32 count = (i32)(2 + (a.w * a.h));
+
+	unsigned long* data = (unsigned long*) RGFW_ALLOC((u32)count * sizeof(unsigned long));
+    RGFW_ASSERT(data != NULL);
+
+    data[0] = (unsigned long)a.w;
+	data[1] = (unsigned long)a.h;
+
+	unsigned long* target = &data[2];
+	u32 x, y;
+
+	for (x = 0; x < a.w; x++) {
+		for (y = 0; y < a.h; y++) {
+			size_t i = y * a.w + x;
+			u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF;
+
+			target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) |
+						(unsigned long)((icon[i * 4 + 1]) <<  8) |
+						(unsigned long)((icon[i * 4 + 2]) <<  0) |
+						(unsigned long)(alpha << 24);
+		}
+	}
+
+	RGFW_bool res = RGFW_TRUE;
+	if (type & RGFW_iconTaskbar) {
+		res = (RGFW_bool)XChangeProperty(
+			win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32,
+			PropModeReplace, (u8*)data, count
+		);
+	}
+
+	if (type & RGFW_iconWindow) {
+		XWMHints wm_hints;
+		wm_hints.flags = IconPixmapHint;
+
+		i32 depth = DefaultDepth(win->src.display, DefaultScreen(win->src.display));
+		XImage *image = XCreateImage(win->src.display, DefaultVisual(win->src.display, DefaultScreen(win->src.display)),
+									(u32)depth, ZPixmap, 0, (char *)target, a.w, a.h, 32, 0);
+
+		wm_hints.icon_pixmap = XCreatePixmap(win->src.display, win->src.window, a.w, a.h, (u32)depth);
+		XPutImage(win->src.display, wm_hints.icon_pixmap, DefaultGC(win->src.display, DefaultScreen(win->src.display)), image, 0, 0, 0, 0, a.w, a.h);
+		image->data = NULL;
+		XDestroyImage(image);
+
+		XSetWMHints(win->src.display, win->src.window, &wm_hints);
+	}
+
+	RGFW_FREE(data);
+	XFlush(win->src.display);
+	return RGFW_BOOL(res);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL  RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type);
+	return RGFW_FALSE;
+#endif
+}
+
+RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
+    RGFW_ASSERT(icon);
+	RGFW_ASSERT(channels == 3 || channels == 4);
+	RGFW_GOTO_WAYLAND(0);
+
+#ifdef RGFW_X11
+#ifndef RGFW_NO_X11_CURSOR
+	RGFW_init();
+    XcursorImage* native = XcursorImageCreate((i32)a.w, (i32)a.h);
+	native->xhot = 0;
+	native->yhot = 0;
+
+	XcursorPixel* target = native->pixels;
+    size_t x, y;
+    for (x = 0; x < a.w; x++) {
+		for (y = 0; y < a.h; y++) {
+			size_t i = y * a.w + x;
+			u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF;
+
+			target[i] = (u32)((icon[i * 4 + 0]) << 16)
+				| (u32)((icon[i * 4 + 1]) << 8)
+				| (u32)((icon[i * 4 + 2]) << 0)
+				| (u32)(alpha << 24);
+		}
+	}
+
+	Cursor cursor = XcursorImageLoadCursor(_RGFW.display, native);
+	XcursorImageDestroy(native);
+
+	return (void*)cursor;
+#else
+	RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels);
+	return NULL;
+#endif
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels);
+	return NULL; /* TODO */
+#endif
+}
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	RGFW_ASSERT(win && mouse);
+	XDefineCursor(win->src.display, win->src.window, (Cursor)mouse);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(win); RGFW_UNUSED(mouse);
+#endif
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	RGFW_ASSERT(mouse);
+	XFreeCursor(_RGFW.display, (Cursor)mouse);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(mouse);
+#endif
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
+RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+	RGFW_ASSERT(win != NULL);
+
+	XEvent event;
+	XQueryPointer(win->src.display, DefaultRootWindow(win->src.display),
+		&event.xbutton.root, &event.xbutton.window,
+		&event.xbutton.x_root, &event.xbutton.y_root,
+		&event.xbutton.x, &event.xbutton.y,
+		&event.xbutton.state);
+
+	win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y);
+	if (event.xbutton.x == p.x && event.xbutton.y == p.y)
+		return;
+
+	XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(win); RGFW_UNUSED(p);
+#endif
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+	return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	static const u8 mouseIconSrc[16] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor};
+
+	if (mouse > (sizeof(mouseIconSrc) / sizeof(u8)))
+		return RGFW_FALSE;
+
+	mouse = mouseIconSrc[mouse];
+
+	Cursor cursor = XCreateFontCursor(win->src.display, mouse);
+	XDefineCursor(win->src.display, win->src.window, (Cursor) cursor);
+
+	XFreeCursor(win->src.display, (Cursor) cursor);
+	return RGFW_TRUE;
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL { }
+	static const char* iconStrings[16] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" };
+
+	struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]);
+	RGFW_cursor_image = wlcursor->images[0];
+	struct wl_buffer* cursor_buffer	= wl_cursor_image_get_buffer(RGFW_cursor_image);
+
+	wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0);
+	wl_surface_commit(RGFW_cursor_surface);
+	return RGFW_TRUE;
+    
+#endif
+}
+
+void RGFW_window_hide(RGFW_window* win) {
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	XUnmapWindow(win->src.display, win->src.window);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	wl_surface_attach(win->src.surface, NULL, 0, 0);
+	wl_surface_commit(win->src.surface);
+	win->_flags |= RGFW_windowHide;
+#endif
+}
+
+void RGFW_window_show(RGFW_window* win) {
+	win->_flags &= ~(u32)RGFW_windowHide;
+	if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+	RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+	XMapWindow(win->src.display, win->src.window);
+#endif
+#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+	/* wl_surface_attach(win->src.surface, win->rc., 0, 0); */
+	wl_surface_commit(win->src.surface);
+#endif
+}
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+	RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+	RGFW_init();
+	if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) {
+		if (str != NULL)
+			RGFW_STRNCPY(str, _RGFW.clipboard, _RGFW.clipboard_len - 1);
+		_RGFW.clipboard[_RGFW.clipboard_len - 1] = '\0';
+		return (RGFW_ssize_t)_RGFW.clipboard_len - 1;
+	}
+
+	XEvent event;
+	int format;
+	unsigned long N, sizeN;
+	char* data;
+	Atom target;
+
+	RGFW_LOAD_ATOM(XSEL_DATA);
+
+	XConvertSelection(_RGFW.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, _RGFW.helperWindow, CurrentTime);
+	XSync(_RGFW.display, 0);
+	while (1) {
+		XNextEvent(_RGFW.display, &event);
+		if (event.type != SelectionNotify) continue;
+
+		if (event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0)
+			return -1;
+		break;
+	}
+
+	XGetWindowProperty(event.xselection.display, event.xselection.requestor,
+			event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target,
+			&format, &sizeN, &N, (u8**) &data);
+
+	RGFW_ssize_t size;
+	if (sizeN > strCapacity && str != NULL)
+		size = -1;
+
+	if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) {
+		RGFW_MEMCPY(str, data, sizeN);
+		str[sizeN] = '\0';
+		XFree(data);
+	} else if (str != NULL) size = -1;
+
+	XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property);
+	size = (RGFW_ssize_t)sizeN;
+
+    return size;
+	#endif
+	#if defined(RGFW_WAYLAND) 
+	RGFW_WAYLAND_LABEL RGFW_UNUSED(str); RGFW_UNUSED(strCapacity); 
+	return 0;
+	#endif
+}
+
+i32 RGFW_XHandleClipboardSelectionHelper(void) {
+#ifdef RGFW_X11
+    RGFW_LOAD_ATOM(SAVE_TARGETS);
+
+    XEvent event;
+    XPending(_RGFW.display);
+
+    if (QLength(_RGFW.display) || XEventsQueued(_RGFW.display, QueuedAlready) + XEventsQueued(_RGFW.display, QueuedAfterReading))
+        XNextEvent(_RGFW.display, &event);
+    else
+        return 0;
+
+    switch (event.type) {
+        case SelectionRequest:
+            RGFW_XHandleClipboardSelection(&event);
+            return 0;
+        case SelectionNotify:
+            if (event.xselection.target == SAVE_TARGETS)
+                return 0;
+            break;
+        default: break;
+    }
+
+    return 0;
+#else
+    return 1;
+#endif
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+	RGFW_GOTO_WAYLAND(1);
+	#ifdef RGFW_X11
+	RGFW_LOAD_ATOM(SAVE_TARGETS);
+    RGFW_init();
+
+    /* request ownership of the clipboard section and request to convert it, this means its our job to convert it */
+	XSetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD, _RGFW.helperWindow, CurrentTime);
+	if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) != _RGFW.helperWindow) {
+    	RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(_RGFW.root, 0), "X11 failed to become owner of clipboard selection");
+		return;
+	}
+
+	if (_RGFW.clipboard)
+		RGFW_FREE(_RGFW.clipboard);
+
+	_RGFW.clipboard = (char*)RGFW_ALLOC(textLen);
+	RGFW_ASSERT(_RGFW.clipboard != NULL);
+
+	RGFW_STRNCPY(_RGFW.clipboard, text, textLen - 1);
+	_RGFW.clipboard[textLen - 1] = '\0';
+	_RGFW.clipboard_len = textLen;
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+    RGFW_UNUSED(text); RGFW_UNUSED(textLen);
+	#endif
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11     
+
+	XWindowAttributes windowAttributes;
+	XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes);
+
+	return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win));
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+    return RGFW_FALSE;
+#endif
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11     
+    RGFW_LOAD_ATOM(WM_STATE);
+
+	Atom actual_type;
+	i32 actual_format;
+	unsigned long nitems, bytes_after;
+	unsigned char* prop_data;
+
+	i32 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False,
+		AnyPropertyType, &actual_type, &actual_format,
+		&nitems, &bytes_after, &prop_data);
+
+	if (status == Success && nitems >= 1 && prop_data == (unsigned char*)IconicState) {
+		XFree(prop_data);
+		return RGFW_TRUE;
+	}
+
+	if (prop_data != NULL)
+		XFree(prop_data);
+
+	XWindowAttributes windowAttributes;
+	XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes);
+    return windowAttributes.map_state != IsViewable;
+#endif
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+    return RGFW_FALSE;
+#endif
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+    RGFW_GOTO_WAYLAND(0);
+#ifdef RGFW_X11
+    RGFW_LOAD_ATOM(_NET_WM_STATE);
+	RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
+	RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
+
+	Atom actual_type;
+	i32 actual_format;
+	unsigned long nitems, bytes_after;
+	unsigned char* prop_data;
+
+	i32 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False,
+		XA_ATOM, &actual_type, &actual_format,
+		&nitems, &bytes_after, &prop_data);
+
+	if (status != Success) {
+		if (prop_data != NULL)
+			XFree(prop_data);
+
+		return RGFW_FALSE;
+	}
+
+	u64 i;
+	for (i = 0; i < nitems; ++i) {
+		if (prop_data[i] == _NET_WM_STATE_MAXIMIZED_VERT ||
+			prop_data[i] == _NET_WM_STATE_MAXIMIZED_HORZ) {
+			XFree(prop_data);
+			return RGFW_TRUE;
+		}
+	}
+
+	if (prop_data != NULL)
+		XFree(prop_data);
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL;
+#endif
+	return RGFW_FALSE;
+}
+
+#ifndef RGFW_NO_DPI
+u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi);
+u32 RGFW_XCalculateRefreshRate(XRRModeInfo mi) {
+    if (mi.hTotal == 0 || mi.vTotal == 0) return 0;
+	return (u32) RGFW_ROUND((double) mi.dotClock / ((double) mi.hTotal * (double) mi.vTotal));
+}
+#endif
+
+
+#ifdef RGFW_X11
+static float XGetSystemContentDPI(Display* display, i32 screen) {
+	float dpi = 96.0f;
+
+	#ifndef RGFW_NO_DPI
+		RGFW_UNUSED(screen);
+		char* rms = XResourceManagerString(display);
+		XrmDatabase db = NULL;
+		if (rms) db = XrmGetStringDatabase(rms);
+
+		if (rms && db) {
+			XrmValue value;
+			char* type = NULL;
+
+			if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0)
+				dpi = (float)RGFW_ATOF(value.addr);
+			XrmDestroyDatabase(db);
+		}
+	#else
+		dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4));
+	#endif
+
+	return dpi;
+}
+#endif
+
+RGFW_monitor RGFW_XCreateMonitor(i32 screen);
+RGFW_monitor RGFW_XCreateMonitor(i32 screen) {
+	RGFW_monitor monitor;
+    RGFW_init();
+
+	RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+    Display* display = _RGFW.display;
+
+	if (screen == -1) screen = DefaultScreen(display);
+
+	Screen* scrn = DefaultScreenOfDisplay(display);
+	RGFW_area size = RGFW_AREA(scrn->width, scrn->height);
+
+	monitor.x = 0;
+	monitor.y = 0;
+	monitor.mode.area = RGFW_AREA(size.w, size.h);
+	monitor.physW = (float)DisplayWidthMM(display, screen) / 25.4f;
+	monitor.physH = (float)DisplayHeightMM(display, screen) / 25.4f;
+
+	RGFW_splitBPP((u32)DefaultDepth(display, DefaultScreen(display)), &monitor.mode);
+
+	char* name = XDisplayName((const char*)display);
+	RGFW_STRNCPY(monitor.name, name, sizeof(monitor.name) - 1);
+	monitor.name[sizeof(monitor.name) - 1] = '\0';
+
+	float dpi = XGetSystemContentDPI(display, screen);
+	monitor.pixelRatio = dpi >= 192.0f ? 2 : 1;
+	monitor.scaleX = (float) (dpi) / 96.0f;
+	monitor.scaleY = (float) (dpi) / 96.0f;
+
+	#ifndef RGFW_NO_DPI
+		XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen));
+		monitor.mode.refreshRate = RGFW_XCalculateRefreshRate(sr->modes[screen]);
+
+		XRRCrtcInfo* ci = NULL;
+		int crtc = screen;
+
+		if (sr->ncrtc > crtc) {
+			ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]);
+		}
+	#endif
+
+	#ifndef RGFW_NO_DPI
+		XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]);
+
+		if (info == NULL || ci == NULL) {
+			XRRFreeScreenResources(sr);
+			RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
+			return monitor;
+		}
+
+
+		float physW = (float)info->mm_width / 25.4f;
+		float physH = (float)info->mm_height / 25.4f;
+
+		RGFW_STRNCPY(monitor.name, info->name, sizeof(monitor.name) - 1);
+		monitor.name[sizeof(monitor.name) - 1] = '\0';
+
+	if ((u8)physW && (u8)physH) {
+		monitor.physW = physW;
+		monitor.physH = physH;
+	}
+
+	monitor.x = ci->x;
+	monitor.y = ci->y;
+
+	if (ci->width && ci->height) {
+		monitor.mode.area.w = (u32)ci->width;
+		monitor.mode.area.h = (u32)ci->height;
+	}
+	#endif
+
+	#ifndef RGFW_NO_DPI
+		XRRFreeCrtcInfo(ci);
+		XRRFreeScreenResources(sr);
+	#endif
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
+    return monitor;
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL  RGFW_UNUSED(screen);
+    RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
+    return monitor;
+#endif
+}
+
+RGFW_monitor* RGFW_getMonitors(size_t* len) {
+	static RGFW_monitor monitors[7];
+
+	RGFW_GOTO_WAYLAND(1);
+	#ifdef RGFW_X11
+    RGFW_init();
+
+	Display* display = _RGFW.display;
+	i32 max = ScreenCount(display);
+
+	i32 i;
+	for (i = 0; i < max && i < 6; i++)
+		monitors[i] = RGFW_XCreateMonitor(i);
+
+	if (len != NULL) *len = (size_t)((max <= 6) ? (max) : (6));
+
+	return monitors;
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL RGFW_UNUSED(len);
+    return monitors; /* TODO WAYLAND */
+	#endif
+}
+
+RGFW_monitor RGFW_getPrimaryMonitor(void) {
+	RGFW_GOTO_WAYLAND(1);
+	#ifdef RGFW_X11
+	return RGFW_XCreateMonitor(-1);
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL return (RGFW_monitor){ 0 }; /* TODO WAYLAND */
+	#endif
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
+	RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+	#ifndef RGFW_NO_DPI
+    RGFW_init();
+    XRRScreenResources* screenRes = XRRGetScreenResources(_RGFW.display, DefaultRootWindow(_RGFW.display));
+	if (screenRes == NULL) return RGFW_FALSE;
+
+    int i;
+    for (i = 0; i < screenRes->ncrtc; i++) {
+		XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(_RGFW.display, screenRes, screenRes->crtcs[i]);
+		if (!crtcInfo) continue;
+
+		if (mon.x == crtcInfo->x && mon.y == crtcInfo->y && (u32)mon.mode.area.w == crtcInfo->width && (u32)mon.mode.area.h == crtcInfo->height) {
+			RRMode rmode = None;
+            int index;
+            for (index = 0; index < screenRes->nmode; index++) {
+				RGFW_monitorMode foundMode;
+				foundMode.area = RGFW_AREA(screenRes->modes[index].width, screenRes->modes[index].height);
+				foundMode.refreshRate =  RGFW_XCalculateRefreshRate(screenRes->modes[index]);
+				RGFW_splitBPP((u32)DefaultDepth(_RGFW.display, DefaultScreen(_RGFW.display)), &foundMode);
+
+				if (RGFW_monitorModeCompare(mode, foundMode, request)) {
+					rmode = screenRes->modes[index].id;
+
+					RROutput output = screenRes->outputs[i];
+					XRROutputInfo* info = XRRGetOutputInfo(_RGFW.display, screenRes, output);
+					if (info) {
+						XRRSetCrtcConfig(_RGFW.display, screenRes, screenRes->crtcs[i],
+										CurrentTime, 0, 0, rmode, RR_Rotate_0, &output, 1);
+						XRRFreeOutputInfo(info);
+						XRRFreeCrtcInfo(crtcInfo);
+						XRRFreeScreenResources(screenRes);
+						return RGFW_TRUE;
+					}
+				}
+			}
+
+			XRRFreeCrtcInfo(crtcInfo);
+			XRRFreeScreenResources(screenRes);
+			return RGFW_FALSE;
+		}
+
+		XRRFreeCrtcInfo(crtcInfo);
+	}
+
+    XRRFreeScreenResources(screenRes);
+	return RGFW_FALSE;
+	#endif
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request);
+#endif
+	return RGFW_FALSE;
+}
+
+RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+    RGFW_monitor mon;
+    RGFW_MEMSET(&mon, 0, sizeof(mon));
+
+    RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(1);
+#ifdef RGFW_X11
+	XWindowAttributes attrs;
+    if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) {
+        return mon;
+    }
+
+	i32 i;
+	for (i = 0; i < ScreenCount(win->src.display) && i < 6; i++) {
+		Screen* screen = ScreenOfDisplay(win->src.display, i);
+        if (attrs.x >= 0 && attrs.x < XWidthOfScreen(screen) &&
+            attrs.y >= 0 && attrs.y < XHeightOfScreen(screen))
+            	return RGFW_XCreateMonitor(i);
+	}
+#endif
+#ifdef RGFW_WAYLAND
+RGFW_WAYLAND_LABEL
+#endif
+    return mon;
+
+}
+
+#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
+void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+	if (win == NULL)
+		glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL);
+	else
+		glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx);
+}
+void* RGFW_getCurrent_OpenGL(void) { return glXGetCurrentContext();  }
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) { glXSwapBuffers(win->src.display, win->src.window); }
+#endif
+
+void RGFW_window_swapBuffers_software(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_GOTO_WAYLAND(0);
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	#ifdef RGFW_X11
+		win->src.bitmap->data = (char*) win->buffer;
+		RGFW_RGB_to_BGR(win, (u8*)win->src.bitmap->data);
+		XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, win->bufferSize.w, win->bufferSize.h);
+		win->src.bitmap->data = NULL;
+		return;
+	#endif
+	#ifdef RGFW_WAYLAND
+	RGFW_WAYLAND_LABEL
+        #if !defined(RGFW_BUFFER_BGR) && !defined(RGFW_OSMESA)
+		RGFW_RGB_to_BGR(win, win->src.buffer);
+		#else
+        size_t y;
+		for (y = 0; y < win->r.h; y++) {
+			u32 index = (y * 4 * win->r.w);
+			u32 index2 = (y * 4 * win->bufferSize.w);
+			RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4);
+		}
+		#endif
+
+		wl_surface_frame_done(win, NULL, 0);
+		wl_surface_commit(win->src.surface);
+	#endif
+#else
+#ifdef RGFW_WAYLAND
+    RGFW_WAYLAND_LABEL
+#endif
+    RGFW_UNUSED(win);
+#endif
+}
+
+#if !defined(RGFW_EGL)
+
+void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+	RGFW_ASSERT(win != NULL);
+
+	#if defined(RGFW_OPENGL)
+	// cached pfn to avoid calling glXGetProcAddress more than once
+	static PFNGLXSWAPINTERVALEXTPROC pfn = (PFNGLXSWAPINTERVALEXTPROC)123;
+    static int (*pfn2)(int) = NULL;
+
+	if (pfn == (PFNGLXSWAPINTERVALEXTPROC)123) {
+		pfn = ((PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"));
+		if (pfn == NULL)  {
+            const char* array[] = {"GLX_MESA_swap_control", "GLX_SGI_swap_control"};
+            u32 i;
+            for (i = 0; i < sizeof(array) / sizeof(char*) && pfn2 == NULL; i++)
+    		    pfn2 = ((int(*)(int))glXGetProcAddress((GLubyte*) array[i]));
+
+            if (pfn2 != NULL) {
+        		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function, fallingback to the native swapinterval function");
+            } else {
+        		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function");
+            }
+        }
+    }
+	if (pfn != NULL)
+		pfn(win->src.display, win->src.window, swapInterval);
+    else if (pfn2 != NULL) {
+        pfn2(swapInterval);
+    }
+	#else
+	RGFW_UNUSED(swapInterval);
+	#endif
+}
+#endif
+
+void RGFW_deinit(void) {
+    if (_RGFW.windowCount == -1 || _RGFW_init == RGFW_FALSE) return;
+    #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL;
+#ifdef RGFW_X11
+	/* to save the clipboard on the x server after the window is closed */
+	RGFW_LOAD_ATOM(CLIPBOARD_MANAGER);
+	RGFW_LOAD_ATOM(SAVE_TARGETS);
+	if (XGetSelectionOwner(_RGFW.display, RGFW_XCLIPBOARD) == _RGFW.helperWindow) {
+		XConvertSelection(_RGFW.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, _RGFW.helperWindow, CurrentTime);
+        while (RGFW_XHandleClipboardSelectionHelper());
+	}
+	if (_RGFW.clipboard) {
+		RGFW_FREE(_RGFW.clipboard);
+		_RGFW.clipboard = NULL;
+	}
+
+    RGFW_freeMouse(_RGFW.hiddenMouse);
+
+    XDestroyWindow(_RGFW.display, (Drawable) _RGFW.helperWindow); /*!< close the window */
+    XCloseDisplay(_RGFW.display); /*!< kill connection to the x server */
+
+    #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR)
+        RGFW_FREE_LIBRARY(X11Cursorhandle);
+    #endif
+    #if !defined(RGFW_NO_X11_XI_PRELOAD)
+        RGFW_FREE_LIBRARY(X11Xihandle);
+    #endif
+
+    #ifdef RGFW_USE_XDL
+        XDL_close();
+    #endif
+
+    #if !defined(RGFW_NO_X11_EXT_PRELOAD)
+        RGFW_FREE_LIBRARY(X11XEXThandle);
+    #endif
+#endif
+#ifdef RGFW_WAYLAND
+	wl_display_disconnect(_RGFW.wl_display);
+#endif
+    #ifndef RGFW_NO_LINUX
+    if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){
+        close(RGFW_eventWait_forceStop[0]);
+        close(RGFW_eventWait_forceStop[1]);
+    }
+
+    u8 i;
+    for (i = 0; i < RGFW_gamepadCount; i++) {
+        if(RGFW_gamepads[i])
+            close(RGFW_gamepads[i]);
+    }
+    #endif
+
+    _RGFW.root = NULL;
+    _RGFW.windowCount = -1;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
+}
+
+void RGFW_window_close(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
+
+	RGFW_GOTO_WAYLAND(0);
+	#ifdef RGFW_X11
+    /* ungrab pointer if it was grabbed */
+	if (win->_flags & RGFW_HOLD_MOUSE)
+		XUngrabPointer(win->src.display, CurrentTime);
+
+    #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		if (win->buffer != NULL) {
+			if ((win->_flags & RGFW_BUFFER_ALLOC))
+				RGFW_FREE(win->buffer);
+			XDestroyImage((XImage*) win->src.bitmap);
+		}
+	#endif
+
+    XFreeGC(win->src.display, win->src.gc);
+    XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window */
+    win->src.window = 0;
+    XCloseDisplay(win->src.display);
+
+    RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
+    _RGFW.windowCount--;
+    if (_RGFW.windowCount == 0) RGFW_deinit();
+
+	RGFW_clipboard_switch(NULL);
+	RGFW_FREE(win->event.droppedFiles);
+    if ((win->_flags & RGFW_WINDOW_ALLOC)) {
+		RGFW_FREE(win);
+        win = NULL;
+    }
+	return;
+	#endif
+
+	#ifdef RGFW_WAYLAND
+		RGFW_WAYLAND_LABEL
+
+	    RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
+
+        xdg_toplevel_destroy(win->src.xdg_toplevel);
+        xdg_surface_destroy(win->src.xdg_surface);
+		wl_surface_destroy(win->src.surface);
+
+		_RGFW.windowCount--;
+        if (_RGFW.windowCount == 0) RGFW_deinit();
+
+		#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+			wl_buffer_destroy(win->src.wl_buffer);
+			if ((win->_flags & RGFW_BUFFER_ALLOC))
+				RGFW_FREE(win->buffer);
+
+			munmap(win->src.buffer, (size_t)(win->r.w * win->r.h * 4));
+		#endif
+
+		RGFW_clipboard_switch(NULL);
+		RGFW_FREE(win->event.droppedFiles);
+        if ((win->_flags & RGFW_WINDOW_ALLOC)) {
+			RGFW_FREE(win);
+            win = NULL;
+        }
+	#endif
+}
+
+
+/*
+	End of X11 linux / wayland / unix defines
+*/
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+void RGFW_stopCheckEvents(void) {
+
+	RGFW_eventWait_forceStop[2] = 1;
+	while (1) {
+		const char byte = 0;
+		const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1);
+		if (result == 1 || result == -1)
+			break;
+	}
+}
+
+void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+	if (waitMS == 0) return;
+
+	u8 i;
+	if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) {
+		if (pipe(RGFW_eventWait_forceStop) != -1) {
+			fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0);
+			fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0);
+			fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0);
+			fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0);
+		}
+	}
+
+	struct pollfd fds[] = {
+		#ifdef RGFW_WAYLAND
+		{ wl_display_get_fd(win->src.wl_display), POLLIN, 0 },
+		#else
+		{ ConnectionNumber(win->src.display), POLLIN, 0 },
+        #endif
+        #ifdef RGFW_X11
+		{ ConnectionNumber(_RGFW.display), POLLIN, 0 },
+        #endif
+        { RGFW_eventWait_forceStop[0], POLLIN, 0 },
+		#if defined(__linux__)
+		{ -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 },  {-1, POLLIN, 0}
+		#endif
+	};
+
+	u8 index = 2;
+#ifdef RGFW_X11
+    index++;
+#endif
+
+	#if defined(__linux__) || defined(__NetBSD__)
+		for (i = 0; i < RGFW_gamepadCount; i++) {
+			if (RGFW_gamepads[i] == 0)
+				continue;
+
+			fds[index].fd = RGFW_gamepads[i];
+			index++;
+		}
+	#endif
+
+
+	u64 start = RGFW_getTimeNS();
+
+
+	#ifdef RGFW_WAYLAND
+		while (wl_display_dispatch(win->src.wl_display) <= 0
+	#else
+		while (XPending(win->src.display) == 0
+	#endif
+    #ifdef RGFW_X11
+        && XPending(_RGFW.display) == 0
+    #endif
+    ) {
+		if (poll(fds, index, waitMS) <= 0)
+			break;
+
+		if (waitMS != RGFW_eventWaitNext)
+			waitMS -= (i32)(RGFW_getTimeNS() - start) / (i32)1e+6;
+	}
+
+	/* drain any data in the stop request */
+	if (RGFW_eventWait_forceStop[2]) {
+		char data[64];
+		(void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data));
+
+		RGFW_eventWait_forceStop[2] = 0;
+	}
+}
+
+i32 RGFW_getClock(void);
+i32 RGFW_getClock(void) {
+	static i32 clock = -1;
+	if (clock != -1) return clock;
+
+	#if defined(_POSIX_MONOTONIC_CLOCK)
+	struct timespec ts;
+	if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+		clock = CLOCK_MONOTONIC;
+	#else
+		clock = CLOCK_REALTIME;
+	#endif
+
+	return clock;
+}
+
+u64 RGFW_getTimerFreq(void) { return 1000000000LLU; }
+u64 RGFW_getTimerValue(void) {
+	struct timespec ts;
+	clock_gettime(CLOCK_REALTIME, &ts);
+    return (u64)ts.tv_sec * RGFW_getTimerFreq() + (u64)ts.tv_nsec;
+}
+#endif /* end of wayland or X11 defines */
+
+
+
+/*
+
+	Start of Windows defines
+
+
+*/
+
+#ifdef RGFW_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#define OEMRESOURCE
+#include <windows.h>
+
+#include <processthreadsapi.h>
+#include <windowsx.h>
+#include <shellapi.h>
+#include <shellscalingapi.h>
+#include <wchar.h>
+#include <locale.h>
+#include <winuser.h>
+
+#ifndef WM_DPICHANGED
+#define WM_DPICHANGED       0x02E0
+#endif
+
+#ifndef RGFW_NO_XINPUT
+	typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*);
+	PFN_XInputGetState XInputGetStateSRC = NULL;
+	#define XInputGetState XInputGetStateSRC
+
+	typedef DWORD (WINAPI * PFN_XInputGetKeystroke)(DWORD, DWORD, PXINPUT_KEYSTROKE);
+	PFN_XInputGetKeystroke XInputGetKeystrokeSRC = NULL;
+	#define XInputGetKeystroke XInputGetKeystrokeSRC
+
+	HMODULE RGFW_XInput_dll = NULL;
+#endif
+
+char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source);
+
+#define GL_FRONT				0x0404
+#define GL_BACK					0x0405
+#define GL_LEFT					0x0406
+#define GL_RIGHT				0x0407
+
+typedef int (*PFN_wglGetSwapIntervalEXT)(void);
+PFN_wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc = NULL;
+#define wglGetSwapIntervalEXT wglGetSwapIntervalEXTSrc
+
+
+void* RGFWgamepadApi = NULL;
+
+/* these two wgl functions need to be preloaded */
+typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hdc, HGLRC hglrc, const int *attribList);
+PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
+
+#ifndef RGFW_EGL
+	HMODULE RGFW_wgl_dll = NULL;
+#endif
+
+#ifndef RGFW_NO_LOAD_WGL
+	typedef HGLRC(WINAPI* PFN_wglCreateContext)(HDC);
+	typedef BOOL(WINAPI* PFN_wglDeleteContext)(HGLRC);
+	typedef PROC(WINAPI* PFN_wglGetProcAddress)(LPCSTR);
+	typedef BOOL(WINAPI* PFN_wglMakeCurrent)(HDC, HGLRC);
+	typedef HDC(WINAPI* PFN_wglGetCurrentDC)(void);
+	typedef HGLRC(WINAPI* PFN_wglGetCurrentContext)(void);
+	typedef BOOL(WINAPI* PFN_wglShareLists)(HGLRC, HGLRC);
+
+	PFN_wglCreateContext wglCreateContextSRC;
+	PFN_wglDeleteContext wglDeleteContextSRC;
+	PFN_wglGetProcAddress wglGetProcAddressSRC;
+	PFN_wglMakeCurrent wglMakeCurrentSRC;
+	PFN_wglGetCurrentDC wglGetCurrentDCSRC;
+	PFN_wglGetCurrentContext wglGetCurrentContextSRC;
+	PFN_wglShareLists wglShareListsSRC;
+
+	#define wglCreateContext wglCreateContextSRC
+	#define wglDeleteContext wglDeleteContextSRC
+	#define wglGetProcAddress wglGetProcAddressSRC
+	#define wglMakeCurrent wglMakeCurrentSRC
+	#define wglGetCurrentDC wglGetCurrentDCSRC
+	#define wglGetCurrentContext wglGetCurrentContextSRC
+	#define wglShareLists wglShareListsSRC
+#endif
+
+#if defined(RGFW_OPENGL) && !defined(RGFW_EGL)
+RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) {
+    const char* extensions = NULL;
+
+    RGFW_proc proc = RGFW_getProcAddress("wglGetExtensionsStringARB");
+    RGFW_proc proc2 = RGFW_getProcAddress("wglGetExtensionsStringEXT");
+
+    if (proc)
+        extensions = ((const char* (*)(HDC))proc)(wglGetCurrentDC());
+    else if (proc2)
+        extensions = ((const char*(*)(void))proc2)();
+
+    return extensions != NULL && RGFW_extensionSupportedStr(extensions, extension, len); 
+}
+
+RGFW_proc RGFW_getProcAddress(const char* procname) {
+    RGFW_proc proc = (RGFW_proc)wglGetProcAddress(procname);
+    if (proc)
+        return proc;
+
+    return (RGFW_proc) GetProcAddress(RGFW_wgl_dll, procname);
+}
+
+typedef HRESULT (APIENTRY* PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
+PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
+
+typedef BOOL(APIENTRY* PFNWGLSWAPINTERVALEXTPROC)(int interval);
+PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+#endif
+
+#ifndef RGFW_NO_DWM
+HMODULE RGFW_dwm_dll = NULL;
+typedef struct { DWORD dwFlags; int fEnable; HRGN hRgnBlur; int fTransitionOnMaximized;} DWM_BLURBEHIND;
+typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND, const DWM_BLURBEHIND*);
+PFN_DwmEnableBlurBehindWindow DwmEnableBlurBehindWindowSRC = NULL;
+#endif
+void RGFW_win32_makeWindowTransparent(RGFW_window* win);
+void RGFW_win32_makeWindowTransparent(RGFW_window* win) {
+	if (!(win->_flags & RGFW_windowTransparent)) return;
+
+	#ifndef RGFW_NO_DWM
+	if (DwmEnableBlurBehindWindowSRC != NULL) {
+			DWM_BLURBEHIND bb = {0, 0, 0, 0};
+			bb.dwFlags = 0x1;
+			bb.fEnable = TRUE;
+			bb.hRgnBlur = NULL;
+			DwmEnableBlurBehindWindowSRC(win->src.window, &bb);
+
+	} else
+	#endif
+	{
+		SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+		SetLayeredWindowAttributes(win->src.window, 0, 128,  LWA_ALPHA);
+	}
+}
+
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK WndProcW(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+    RGFW_window* win = (RGFW_window*)GetPropW(hWnd, L"RGFW");
+	if (win == NULL) return DefWindowProcW(hWnd, message, wParam, lParam);
+
+	RECT windowRect;
+	GetWindowRect(hWnd, &windowRect);
+
+	switch (message) {
+		case WM_CLOSE:
+		case WM_QUIT:
+			RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
+			RGFW_windowQuitCallback(win);
+			return 0;
+		case WM_ACTIVATE: {
+			RGFW_bool inFocus = RGFW_BOOL(LOWORD(wParam) != WA_INACTIVE);
+			if (inFocus) win->_flags |= RGFW_windowFocus;
+			else 		win->_flags &= ~ (u32)RGFW_windowFocus;
+			RGFW_eventQueuePushEx(e.type = (RGFW_eventType)((u8)RGFW_focusOut - inFocus); e._win = win);
+
+			RGFW_focusCallback(win, inFocus);
+            RGFW_window_focusLost(win);
+
+			if ((win->_flags & RGFW_windowFullscreen) == 0)
+				return DefWindowProcW(hWnd, message, wParam, lParam);
+
+			win->_flags &= ~(u32)RGFW_EVENT_PASSED;
+			if (inFocus == RGFW_FALSE) RGFW_window_minimize(win);
+			else RGFW_window_setFullscreen(win, 1);
+			return DefWindowProcW(hWnd, message, wParam, lParam);
+		}
+		case WM_MOVE:
+			win->r.x = windowRect.left;
+			win->r.y = windowRect.top;
+			RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win);
+			RGFW_windowMovedCallback(win, win->r);
+			return DefWindowProcW(hWnd, message, wParam, lParam);
+		case WM_SIZE: {
+			if (win->src.aspectRatio.w != 0 && win->src.aspectRatio.h != 0) {
+				double aspectRatio = (double)win->src.aspectRatio.w / win->src.aspectRatio.h;
+
+				int width = (windowRect.right - windowRect.left) - win->src.wOffset;
+				int height = (windowRect.bottom - windowRect.top) - win->src.hOffset;
+				int newHeight = (int)(width / aspectRatio);
+				int newWidth = (int)(height * aspectRatio);
+
+				if (win->r.w > windowRect.right - windowRect.left ||
+					win->r.h > (i32)((u32)(windowRect.bottom - windowRect.top) - win->src.hOffset))
+				{
+					if (newHeight > height) windowRect.right = windowRect.left + newWidth;
+					else windowRect.bottom = windowRect.top + newHeight;
+				} else {
+					if (newHeight < height) windowRect.right = windowRect.left + newWidth;
+					else windowRect.bottom = windowRect.top + newHeight;
+				}
+
+				RGFW_window_resize(win, RGFW_AREA((u32)(windowRect.right - windowRect.left) - (u32)win->src.wOffset,
+												(u32)(windowRect.bottom - windowRect.top) - (u32)win->src.hOffset));
+			}
+
+			win->r.w = (windowRect.right -  windowRect.left) - (i32)win->src.wOffset;
+			win->r.h = (windowRect.bottom - windowRect.top) - (i32)win->src.hOffset;
+			RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win);
+			RGFW_windowResizedCallback(win, win->r);
+			RGFW_window_checkMode(win);
+			return DefWindowProcW(hWnd, message, wParam, lParam);
+		}
+		#ifndef RGFW_NO_MONITOR
+		case WM_DPICHANGED: {
+			if (win->_flags & RGFW_windowScaleToMonitor) RGFW_window_scaleToMonitor(win);
+
+			const float scaleX = HIWORD(wParam) / (float) 96;
+            const float scaleY = LOWORD(wParam) / (float) 96;
+			RGFW_scaleUpdatedCallback(win, scaleX, scaleY);
+			RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = scaleX; e.scaleY = scaleY; e._win = win);
+			return DefWindowProcW(hWnd, message, wParam, lParam);
+		}
+		#endif
+		case WM_GETMINMAXINFO: {
+			MINMAXINFO* mmi = (MINMAXINFO*) lParam;
+			mmi->ptMinTrackSize.x = (LONG)(win->src.minSize.w + win->src.wOffset);
+			mmi->ptMinTrackSize.y = (LONG)(win->src.minSize.h + win->src.hOffset);
+			if (win->src.maxSize.w == 0 && win->src.maxSize.h == 0)
+				return DefWindowProcW(hWnd, message, wParam, lParam);
+
+			mmi->ptMaxTrackSize.x = (LONG)(win->src.maxSize.w + win->src.wOffset);
+			mmi->ptMaxTrackSize.y = (LONG)(win->src.maxSize.h + win->src.hOffset);
+			return DefWindowProcW(hWnd, message, wParam, lParam);
+		}
+		case WM_PAINT: {
+            PAINTSTRUCT ps;
+            BeginPaint(hWnd, &ps);
+            RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win);
+            RGFW_windowRefreshCallback(win);
+            EndPaint(hWnd, &ps);
+
+            return DefWindowProcW(hWnd, message, wParam, lParam);
+		}
+		#if(_WIN32_WINNT >= 0x0600)
+		case WM_DWMCOMPOSITIONCHANGED:
+		case WM_DWMCOLORIZATIONCOLORCHANGED:
+			RGFW_win32_makeWindowTransparent(win);
+			break;
+		#endif
+/* based on sokol_app.h */
+#ifdef RGFW_ADVANCED_SMOOTH_RESIZE
+        case WM_ENTERSIZEMOVE: SetTimer(win->src.window, 1, USER_TIMER_MINIMUM, NULL); break;
+        case WM_EXITSIZEMOVE: KillTimer(win->src.window, 1); break;
+        case WM_TIMER: RGFW_windowRefreshCallback(win); break;
+#endif
+        case WM_NCLBUTTONDOWN: {
+            /* workaround for half-second pause when starting to move window
+                see: https://gamedev.net/forums/topic/672094-keeping-things-moving-during-win32-moveresize-events/5254386/
+            */
+            POINT point = { 0, 0 };
+            if (SendMessage(win->src.window, WM_NCHITTEST, wParam, lParam) != HTCAPTION || GetCursorPos(&point) == FALSE)
+                break;
+
+            ScreenToClient(win->src.window, &point);
+            PostMessage(win->src.window, WM_MOUSEMOVE, 0, ((uint32_t)point.x)|(((uint32_t)point.y) << 16));
+            break;
+        }
+		default: break;
+	}
+	return DefWindowProcW(hWnd, message, wParam, lParam);
+}
+
+#ifndef RGFW_NO_DPI
+	HMODULE RGFW_Shcore_dll = NULL;
+	typedef HRESULT (WINAPI *PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
+	PFN_GetDpiForMonitor GetDpiForMonitorSRC = NULL;
+	#define GetDpiForMonitor GetDpiForMonitorSRC
+#endif
+
+#if !defined(RGFW_NO_LOAD_WINMM) && !defined(RGFW_NO_WINMM)
+	HMODULE RGFW_winmm_dll = NULL;
+	typedef u32 (WINAPI * PFN_timeBeginPeriod)(u32);
+	typedef PFN_timeBeginPeriod PFN_timeEndPeriod;
+	PFN_timeBeginPeriod timeBeginPeriodSRC, timeEndPeriodSRC;
+	#define timeBeginPeriod timeBeginPeriodSRC
+	#define timeEndPeriod timeEndPeriodSRC
+#elif !defined(RGFW_NO_WINMM)
+	__declspec(dllimport) u32 __stdcall timeBeginPeriod(u32 uPeriod);
+	__declspec(dllimport) u32 __stdcall timeEndPeriod(u32 uPeriod);
+#endif
+#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) { \
+                                        name##SRC = (PFN_##name)(RGFW_proc)GetProcAddress((proc), (#name)); \
+                                        RGFW_ASSERT(name##SRC != NULL); \
+                                    }
+
+#ifndef RGFW_NO_XINPUT
+void RGFW_loadXInput(void);
+void RGFW_loadXInput(void) {
+	u32 i;
+	static const char* names[] = {"xinput1_4.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll"};
+
+	for (i = 0; i < sizeof(names) / sizeof(const char*) && (XInputGetStateSRC == NULL || XInputGetKeystrokeSRC != NULL);  i++) {
+		RGFW_XInput_dll = LoadLibraryA(names[i]);
+		RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetState);
+		RGFW_PROC_DEF(RGFW_XInput_dll, XInputGetKeystroke);
+	}
+
+	if (XInputGetStateSRC == NULL)
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetState");
+	if (XInputGetKeystrokeSRC == NULL)
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errFailedFuncLoad, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load XInputGetKeystroke");
+}
+#endif
+
+void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	win->buffer = buffer;
+	win->bufferSize = area;
+
+	BITMAPV5HEADER bi;
+	ZeroMemory(&bi, sizeof(bi));
+	bi.bV5Size = sizeof(bi);
+	bi.bV5Width = (i32)area.w;
+	bi.bV5Height = -((LONG) area.h);
+	bi.bV5Planes = 1;
+	bi.bV5BitCount = 32;
+	bi.bV5Compression = BI_RGB;
+
+	win->src.bitmap = CreateDIBSection(win->src.hdc,
+		(BITMAPINFO*) &bi, DIB_RGB_COLORS,
+		(void**) &win->src.bitmapBits,
+		NULL, (DWORD) 0);
+
+	if (win->buffer == NULL)
+		win->buffer = win->src.bitmapBits;
+
+	win->src.hdcMem = CreateCompatibleDC(win->src.hdc);
+	SelectObject(win->src.hdcMem, win->src.bitmap);
+
+	#if defined(RGFW_OSMESA)
+	win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL);
+	OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
+	OSMesaPixelStore(OSMESA_Y_UP, 0);
+	#endif
+	#else
+	RGFW_UNUSED(win); RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
+	#endif
+}
+
+void RGFW_releaseCursor(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	ClipCursor(NULL);
+	const RAWINPUTDEVICE id = { 0x01, 0x02, RIDEV_REMOVE, NULL };
+	RegisterRawInputDevices(&id, 1, sizeof(id));
+}
+
+void RGFW_captureCursor(RGFW_window* win, RGFW_rect rect) {
+	RGFW_UNUSED(win); RGFW_UNUSED(rect);
+
+	RECT clipRect;
+	GetClientRect(win->src.window, &clipRect);
+	ClientToScreen(win->src.window, (POINT*) &clipRect.left);
+	ClientToScreen(win->src.window, (POINT*) &clipRect.right);
+	ClipCursor(&clipRect);
+
+	const RAWINPUTDEVICE id = { 0x01, 0x02, 0, win->src.window };
+	RegisterRawInputDevices(&id, 1, sizeof(id));
+}
+
+#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) { x = LoadLibraryA(lib); RGFW_ASSERT(x != NULL); }
+
+#ifdef RGFW_DIRECTX
+int RGFW_window_createDXSwapChain(RGFW_window* win, IDXGIFactory* pFactory, IUnknown* pDevice, IDXGISwapChain** swapchain) {
+    RGFW_ASSERT(win && pFactory && pDevice && swapchain);
+
+    static DXGI_SWAP_CHAIN_DESC swapChainDesc = { 0 };
+    swapChainDesc.BufferCount = 2;
+    swapChainDesc.BufferDesc.Width = win->r.w;
+    swapChainDesc.BufferDesc.Height = win->r.h;
+    swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    swapChainDesc.OutputWindow = (HWND)win->src.window;
+    swapChainDesc.SampleDesc.Count = 1;
+    swapChainDesc.SampleDesc.Quality = 0;
+    swapChainDesc.Windowed = TRUE;
+    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+    HRESULT hr = pFactory->lpVtbl->CreateSwapChain(pFactory, (IUnknown*)pDevice, &swapChainDesc, swapchain);
+    if (FAILED(hr)) {
+        RGFW_sendDebugInfo(RGFW_typeError, RGFW_errDirectXContext, RGFW_DEBUG_CTX(win, hr), "Failed to create DirectX swap chain!");
+        return -2;
+    }
+
+    return 0;
+}
+#endif
+
+void RGFW_win32_loadOpenGLFuncs(HWND dummyWin);
+void RGFW_win32_loadOpenGLFuncs(HWND dummyWin) {
+#ifdef RGFW_OPENGL
+     if (wglSwapIntervalEXT != NULL && wglChoosePixelFormatARB != NULL && wglChoosePixelFormatARB != NULL)
+		return;
+
+	HDC dummy_dc = GetDC(dummyWin);
+	u32 pfd_flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+
+	PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd), 1, pfd_flags, PFD_TYPE_RGBA, 32, 8, PFD_MAIN_PLANE, 32, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 32, 8, 0, PFD_MAIN_PLANE, 0, 0, 0, 0};
+
+	int dummy_pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
+	SetPixelFormat(dummy_dc, dummy_pixel_format, &pfd);
+
+	HGLRC dummy_context = wglCreateContext(dummy_dc);
+	wglMakeCurrent(dummy_dc, dummy_context);
+
+	wglCreateContextAttribsARB = ((PFNWGLCREATECONTEXTATTRIBSARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglCreateContextAttribsARB");
+	wglChoosePixelFormatARB = ((PFNWGLCHOOSEPIXELFORMATARBPROC(WINAPI *)(const char*)) wglGetProcAddress)("wglChoosePixelFormatARB");
+
+    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(RGFW_proc)wglGetProcAddress("wglSwapIntervalEXT");
+    if (wglSwapIntervalEXT == NULL) {
+        RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to load swap interval function");
+    }
+
+	wglMakeCurrent(dummy_dc, 0);
+	wglDeleteContext(dummy_context);
+	ReleaseDC(dummyWin, dummy_dc);
+#else
+	RGFW_UNUSED(dummyWin);
+#endif
+}
+
+#ifndef RGFW_EGL
+void RGFW_window_initOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+	PIXELFORMATDESCRIPTOR pfd;
+	pfd.nSize        = sizeof(PIXELFORMATDESCRIPTOR);
+	pfd.nVersion     = 1;
+	pfd.dwFlags      = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
+	pfd.iPixelType   = PFD_TYPE_RGBA;
+	pfd.iLayerType   = PFD_MAIN_PLANE;
+	pfd.cColorBits   = 32;
+	pfd.cAlphaBits   = 8;
+	pfd.cDepthBits   = 24;
+	pfd.cStencilBits = (BYTE)RGFW_GL_HINTS[RGFW_glStencil];
+	pfd.cAuxBuffers  = (BYTE)RGFW_GL_HINTS[RGFW_glAuxBuffers];
+	if (RGFW_GL_HINTS[RGFW_glStereo]) pfd.dwFlags |= PFD_STEREO;
+
+	/* try to create the pixel format we want for opengl and then try to create an opengl context for the specified version */
+	if (win->_flags & RGFW_windowOpenglSoftware)
+		pfd.dwFlags |= PFD_GENERIC_FORMAT | PFD_GENERIC_ACCELERATED;
+
+	/* get pixel format, default to a basic pixel format */
+	int pixel_format = ChoosePixelFormat(win->src.hdc, &pfd);
+	if (wglChoosePixelFormatARB != NULL) {
+		i32* pixel_format_attribs = (i32*)RGFW_initFormatAttribs();
+
+		int new_pixel_format;
+		UINT num_formats;
+		wglChoosePixelFormatARB(win->src.hdc, pixel_format_attribs, 0, 1, &new_pixel_format, &num_formats);
+		if (!num_formats)
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create a pixel format for WGL");
+		else pixel_format = new_pixel_format;
+	}
+
+	PIXELFORMATDESCRIPTOR suggested;
+	if (!DescribePixelFormat(win->src.hdc, pixel_format, sizeof(suggested), &suggested) ||
+		!SetPixelFormat(win->src.hdc, pixel_format, &pfd))
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set the WGL pixel format");
+    
+    if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED)) {
+        win->_flags |= RGFW_windowOpenglSoftware;
+    }
+
+	if (wglCreateContextAttribsARB != NULL) {
+		/* create opengl/WGL context for the specified version */
+		u32 index = 0;
+		i32 attribs[40];
+
+		if (RGFW_GL_HINTS[RGFW_glProfile]== RGFW_glCore) {
+			SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB);
+		}
+		else {
+			SET_ATTRIB(WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB);
+		}
+
+		if (RGFW_GL_HINTS[RGFW_glMinor] || RGFW_GL_HINTS[RGFW_glMajor]) {
+			SET_ATTRIB(WGL_CONTEXT_MAJOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMajor]);
+			SET_ATTRIB(WGL_CONTEXT_MINOR_VERSION_ARB, RGFW_GL_HINTS[RGFW_glMinor]);
+		}
+
+		SET_ATTRIB(0, 0);
+
+		win->src.ctx = (HGLRC)wglCreateContextAttribsARB(win->src.hdc, NULL, attribs);
+	} else { /* fall back to a default context (probably opengl 2 or something) */
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to create an accelerated OpenGL Context");
+		win->src.ctx = wglCreateContext(win->src.hdc);
+	}
+
+	ReleaseDC(win->src.window, win->src.hdc);
+	win->src.hdc = GetDC(win->src.window);
+	wglMakeCurrent(win->src.hdc, win->src.ctx);
+
+	if (_RGFW.root != win)
+		wglShareLists(_RGFW.root->src.ctx, win->src.ctx);
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_window_freeOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+	if (win->src.ctx == NULL) return;
+	wglDeleteContext((HGLRC) win->src.ctx); /*!< delete opengl context */
+	win->src.ctx = NULL;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+#endif
+
+
+i32 RGFW_init(void) {
+#if defined(RGFW_C89) || defined(__cplusplus)
+    if (_RGFW_init) return 0; 
+    _RGFW_init = RGFW_TRUE;
+    _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
+#endif
+
+    #ifndef RGFW_NO_XINPUT
+		if (RGFW_XInput_dll == NULL)
+			RGFW_loadXInput();
+	#endif
+
+#ifndef RGFW_NO_DPI
+	#if (_WIN32_WINNT >= 0x0600)
+		SetProcessDPIAware();
+	#endif
+#endif
+
+	#ifndef RGFW_NO_WINMM
+		#ifndef RGFW_NO_LOAD_WINMM
+			RGFW_LOAD_LIBRARY(RGFW_winmm_dll, "winmm.dll");
+			RGFW_PROC_DEF(RGFW_winmm_dll, timeBeginPeriod);
+			RGFW_PROC_DEF(RGFW_winmm_dll, timeEndPeriod);
+		#endif
+		timeBeginPeriod(1);
+	#endif
+
+	#ifndef RGFW_NO_DWM
+	RGFW_LOAD_LIBRARY(RGFW_dwm_dll, "dwmapi.dll");
+	RGFW_PROC_DEF(RGFW_dwm_dll, DwmEnableBlurBehindWindow);
+	#endif
+
+	RGFW_LOAD_LIBRARY(RGFW_wgl_dll, "opengl32.dll");
+	#ifndef RGFW_NO_LOAD_WGL
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglCreateContext);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglDeleteContext);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglGetProcAddress);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglMakeCurrent);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentDC);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglGetCurrentContext);
+		RGFW_PROC_DEF(RGFW_wgl_dll, wglShareLists);
+	#endif
+
+	u8 RGFW_blk[] = { 0, 0, 0, 0 };
+	_RGFW.hiddenMouse = RGFW_loadMouse(RGFW_blk, RGFW_AREA(1, 1), 4);
+
+    _RGFW.windowCount = 0;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
+    return 1;
+}
+
+RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
+	if (name[0] == 0) name = (char*) " ";
+
+	RGFW_window_basic_init(win, rect, flags);
+
+	win->src.hIconSmall = win->src.hIconBig = NULL;
+	win->src.maxSize = RGFW_AREA(0, 0);
+	win->src.minSize = RGFW_AREA(0, 0);
+	win->src.aspectRatio = RGFW_AREA(0, 0);
+
+	HINSTANCE inh = GetModuleHandleA(NULL);
+
+	#ifndef __cplusplus
+	WNDCLASSW Class = { 0 }; /*!< Setup the Window class. */
+	#else
+	WNDCLASSW Class = { };
+	#endif
+
+	if (RGFW_className == NULL)
+		RGFW_className = (char*)name;
+
+	wchar_t wide_class[256];
+	MultiByteToWideChar(CP_UTF8, 0, RGFW_className, -1, wide_class, 255);
+
+	Class.lpszClassName = wide_class;
+	Class.hInstance = inh;
+	Class.hCursor = LoadCursor(NULL, IDC_ARROW);
+	Class.lpfnWndProc = WndProcW;
+	Class.cbClsExtra = sizeof(RGFW_window*);
+
+	Class.hIcon = (HICON)LoadImageA(GetModuleHandleW(NULL), "RGFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+	if (Class.hIcon == NULL)
+		Class.hIcon = (HICON)LoadImageA(NULL, (LPCSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+	RegisterClassW(&Class);
+
+	DWORD window_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
+
+	RECT windowRect, clientRect;
+
+	if (!(flags & RGFW_windowNoBorder)) {
+		window_style |= WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX | WS_THICKFRAME;
+
+		if (!(flags & RGFW_windowNoResize))
+			window_style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
+	} else
+		window_style |= WS_POPUP | WS_VISIBLE | WS_SYSMENU;
+
+	wchar_t wide_name[256];
+	MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 255);
+	HWND dummyWin = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w, win->r.h, 0, 0, inh, 0);
+
+	GetWindowRect(dummyWin, &windowRect);
+	GetClientRect(dummyWin, &clientRect);
+
+	RGFW_win32_loadOpenGLFuncs(dummyWin);
+	DestroyWindow(dummyWin);
+
+	win->src.hOffset = (u32)(windowRect.bottom - windowRect.top) - (u32)(clientRect.bottom - clientRect.top);
+	win->src.wOffset = (u32)(windowRect.right - windowRect.left) - (u32)(clientRect.right - clientRect.left);
+	win->src.window = CreateWindowW(Class.lpszClassName, (wchar_t*)wide_name, window_style, win->r.x, win->r.y, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, 0, 0, inh, 0);
+	SetPropW(win->src.window, L"RGFW", win);
+	RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h)); /* so WM_GETMINMAXINFO gets called again */
+
+	if (flags & RGFW_windowAllowDND) {
+		win->_flags |= RGFW_windowAllowDND;
+		RGFW_window_setDND(win, 1);
+	}
+	win->src.hdc = GetDC(win->src.window);
+
+	if ((flags & RGFW_windowNoInitAPI) == 0) {
+        RGFW_window_initOpenGL(win);
+        RGFW_window_initBuffer(win);
+    }
+
+	RGFW_window_setFlags(win, flags);
+	RGFW_win32_makeWindowTransparent(win);
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new window was created");
+    RGFW_window_show(win);
+
+    return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+	RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
+	LONG style = GetWindowLong(win->src.window, GWL_STYLE);
+
+
+	if (border == 0) {
+		SetWindowLong(win->src.window, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW);
+		SetWindowPos(
+			win->src.window, HWND_TOP, 0, 0, 0, 0,
+			SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+		);
+	}
+	else {
+		style |= WS_OVERLAPPEDWINDOW;
+		if (win->_flags & RGFW_windowNoResize) style &= ~WS_MAXIMIZEBOX;
+		SetWindowPos(
+			win->src.window, HWND_TOP, 0, 0, 0, 0,
+			SWP_NOZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE
+		);
+	}
+}
+
+void RGFW_window_setDND(RGFW_window* win, RGFW_bool allow) {
+	RGFW_setBit(&win->_flags, RGFW_windowAllowDND, allow);
+	DragAcceptFiles(win->src.window, allow);
+}
+
+RGFW_area RGFW_getScreenSize(void) {
+	HDC dc = GetDC(NULL);
+	RGFW_area area = RGFW_AREA(GetDeviceCaps(dc, HORZRES), GetDeviceCaps(dc, VERTRES));
+	ReleaseDC(NULL, dc);
+	return area;
+}
+
+RGFW_point RGFW_getGlobalMousePoint(void) {
+	POINT p;
+	GetCursorPos(&p);
+
+	return RGFW_POINT(p.x, p.y);
+}
+
+void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+	win->src.aspectRatio = a;
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+	win->src.minSize = a;
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+	win->src.maxSize = a;
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+	RGFW_ASSERT(win);
+    SetForegroundWindow(win->src.window);
+    SetFocus(win->src.window);
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+	RGFW_ASSERT(win);
+	BringWindowToTop(win->src.window);
+	SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, win->r.w, win->r.h, SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+	RGFW_ASSERT(win != NULL);
+
+	if (fullscreen == RGFW_FALSE) {
+		RGFW_window_setBorder(win, 1);
+		SetWindowPos(win->src.window, HWND_NOTOPMOST, win->_oldRect.x, win->_oldRect.y, win->_oldRect.w + (i32)win->src.wOffset, win->_oldRect.h + (i32)win->src.hOffset,
+			 SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
+
+		win->_flags &= ~(u32)RGFW_windowFullscreen;
+		win->r = win->_oldRect;
+		return;
+	}
+
+	win->_oldRect = win->r;
+	win->_flags |= RGFW_windowFullscreen;
+
+	RGFW_monitor mon  = RGFW_window_getMonitor(win);
+	RGFW_window_setBorder(win, 0);
+
+    SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, (i32)mon.mode.area.w, (i32)mon.mode.area.h, SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+    RGFW_monitor_scaleToWindow(mon, win);
+
+	win->r = RGFW_RECT(0, 0, mon.mode.area.w, mon.mode.area.h);
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_window_hide(win);
+	ShowWindow(win->src.window, SW_MAXIMIZE);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	ShowWindow(win->src.window, SW_MINIMIZE);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+    RGFW_ASSERT(win != NULL);
+    if (floating) SetWindowPos(win->src.window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+    else SetWindowPos(win->src.window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+	SetWindowLong(win->src.window, GWL_EXSTYLE, WS_EX_LAYERED);
+	SetLayeredWindowAttributes(win->src.window, 0, opacity, LWA_ALPHA);
+}
+
+void RGFW_window_restore(RGFW_window* win) { RGFW_window_show(win); }
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+	return (GetWindowLongPtr(win->src.window, GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
+}
+
+u8 RGFW_xinput2RGFW[] = {
+	RGFW_gamepadA, /* or PS X button */
+	RGFW_gamepadB, /* or PS circle button */
+	RGFW_gamepadX, /* or PS square button */
+	RGFW_gamepadY, /* or PS triangle button */
+	RGFW_gamepadR1, /* right bumper */
+	RGFW_gamepadL1, /* left bump */
+	RGFW_gamepadL2, /* left trigger */
+	RGFW_gamepadR2, /* right trigger */
+	0, 0, 0, 0, 0, 0, 0, 0,
+	RGFW_gamepadUp, /* dpad up */
+	RGFW_gamepadDown, /* dpad down */
+	RGFW_gamepadLeft, /* dpad left */
+	RGFW_gamepadRight, /* dpad right */
+	RGFW_gamepadStart, /* start button */
+	RGFW_gamepadSelect,/* select button */
+	RGFW_gamepadL3,
+	RGFW_gamepadR3,
+};
+i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e);
+i32 RGFW_checkXInput(RGFW_window* win, RGFW_event* e) {
+	#ifndef RGFW_NO_XINPUT
+
+	RGFW_UNUSED(win);
+    u16 i;
+	for (i = 0; i < 4; i++) {
+		XINPUT_KEYSTROKE keystroke;
+
+		if (XInputGetKeystroke == NULL)
+			return 0;
+
+		DWORD result = XInputGetKeystroke((DWORD)i, 0, &keystroke);
+
+		if ((keystroke.Flags & XINPUT_KEYSTROKE_REPEAT) == 0 && result != ERROR_EMPTY) {
+			if (result != ERROR_SUCCESS)
+				return 0;
+
+			if (keystroke.VirtualKey > VK_PAD_RTHUMB_PRESS)
+				continue;
+
+			/* gamepad + 1 = RGFW_gamepadButtonReleased */
+			e->type = RGFW_gamepadButtonPressed + !(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
+			e->button = RGFW_xinput2RGFW[keystroke.VirtualKey - 0x5800];
+			RGFW_gamepadPressed[i][e->button].prev = RGFW_gamepadPressed[i][e->button].current;
+			RGFW_gamepadPressed[i][e->button].current = RGFW_BOOL(keystroke.Flags & XINPUT_KEYSTROKE_KEYDOWN);
+
+			RGFW_gamepadButtonCallback(win, i, e->button, e->type == RGFW_gamepadButtonPressed);
+			return 1;
+		}
+
+		XINPUT_STATE state;
+		if (XInputGetState == NULL ||
+			XInputGetState((DWORD) i, &state) == ERROR_DEVICE_NOT_CONNECTED
+		) {
+			if (RGFW_gamepads[i] == 0)
+				continue;
+
+			RGFW_gamepads[i] = 0;
+			RGFW_gamepadCount--;
+
+			win->event.type = RGFW_gamepadDisconnected;
+			win->event.gamepad = (u16)i;
+			RGFW_gamepadCallback(win, i, 0);
+			return 1;
+		}
+
+		if (RGFW_gamepads[i] == 0) {
+			RGFW_gamepads[i] = 1;
+			RGFW_gamepadCount++;
+
+			char str[] = "Microsoft X-Box (XInput device)";
+			RGFW_MEMCPY(RGFW_gamepads_name[i], str, sizeof(str));
+			RGFW_gamepads_name[i][sizeof(RGFW_gamepads_name[i]) - 1] = '\0';
+			win->event.type = RGFW_gamepadConnected;
+			win->event.gamepad = i;
+			RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
+
+			RGFW_gamepadCallback(win, i, 1);
+			return 1;
+		}
+
+#define INPUT_DEADZONE  ( 0.24f * (float)(0x7FFF) )  /* Default to 24% of the +/- 32767 range.   This is a reasonable default value but can be altered if needed. */
+
+		if ((state.Gamepad.sThumbLX < INPUT_DEADZONE &&
+			state.Gamepad.sThumbLX > -INPUT_DEADZONE) &&
+			(state.Gamepad.sThumbLY < INPUT_DEADZONE &&
+				state.Gamepad.sThumbLY > -INPUT_DEADZONE))
+		{
+			state.Gamepad.sThumbLX = 0;
+			state.Gamepad.sThumbLY = 0;
+		}
+
+		if ((state.Gamepad.sThumbRX < INPUT_DEADZONE &&
+			state.Gamepad.sThumbRX > -INPUT_DEADZONE) &&
+			(state.Gamepad.sThumbRY < INPUT_DEADZONE &&
+				state.Gamepad.sThumbRY > -INPUT_DEADZONE))
+		{
+			state.Gamepad.sThumbRX = 0;
+			state.Gamepad.sThumbRY = 0;
+		}
+
+		e->axisesCount = 2;
+		RGFW_point axis1 = RGFW_POINT(((float)state.Gamepad.sThumbLX / 32768.0f) * 100, ((float)state.Gamepad.sThumbLY / -32768.0f) * 100);
+		RGFW_point axis2 = RGFW_POINT(((float)state.Gamepad.sThumbRX / 32768.0f) * 100, ((float)state.Gamepad.sThumbRY / -32768.0f) * 100);
+
+		if (axis1.x != e->axis[0].x || axis1.y != e->axis[0].y){
+			win->event.whichAxis = 0;
+
+			e->type = RGFW_gamepadAxisMove;
+			e->axis[0] = axis1;
+			RGFW_gamepadAxes[i][0] = e->axis[0];
+
+			RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis);
+			return 1;
+		}
+
+		if (axis2.x != e->axis[1].x || axis2.y != e->axis[1].y) {
+			win->event.whichAxis = 1;
+			e->type = RGFW_gamepadAxisMove;
+			e->axis[1] = axis2;
+			RGFW_gamepadAxes[i][1] = e->axis[1];
+
+			RGFW_gamepadAxisCallback(win, e->gamepad, e->axis, e->axisesCount, e->whichAxis);
+			return 1;
+		}
+	}
+
+	#endif
+
+	return 0;
+}
+
+void RGFW_stopCheckEvents(void) {
+	PostMessageW(_RGFW.root->src.window, WM_NULL, 0, 0);
+}
+
+void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+	RGFW_UNUSED(win);
+	MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD)waitMS, QS_ALLINPUT);
+}
+
+u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
+    UINT vsc = RGFW_rgfwToApiKey(rgfw_keycode);  // Should return a Windows VK_* code
+    BYTE keyboardState[256] = {0};
+
+    if (!GetKeyboardState(keyboardState))
+        return (u8)rgfw_keycode;
+
+    UINT vk = MapVirtualKeyW(vsc, MAPVK_VSC_TO_VK);
+    HKL layout = GetKeyboardLayout(0);
+
+    wchar_t charBuffer[2] = {0};
+    int result = ToUnicodeEx(vk, vsc, keyboardState, charBuffer, 1, 0, layout);
+
+    if (result <= 0)
+        return (u8)rgfw_keycode;
+
+    return (u8)charBuffer[0];
+}
+
+RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
+    if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
+    RGFW_event* ev = RGFW_window_checkEventCore(win);
+	if (ev) {
+        return ev;
+    }
+
+    static HDROP drop;
+	if (win->event.type == RGFW_DNDInit) {
+		if (win->event.droppedFilesCount) {
+			u32 i;
+			for (i = 0; i < win->event.droppedFilesCount; i++)
+				win->event.droppedFiles[i][0] = '\0';
+		}
+
+		win->event.droppedFilesCount = 0;
+		win->event.droppedFilesCount = DragQueryFileW(drop, 0xffffffff, NULL, 0);
+
+		u32 i;
+		for (i = 0; i < win->event.droppedFilesCount; i++) {
+			UINT length = DragQueryFileW(drop, i, NULL, 0);
+			if (length == 0)
+				continue;
+
+			WCHAR buffer[RGFW_MAX_PATH * 2];
+			if (length > (RGFW_MAX_PATH * 2) - 1)
+				length = RGFW_MAX_PATH * 2;
+
+			DragQueryFileW(drop, i, buffer, length + 1);
+
+			char* str = RGFW_createUTF8FromWideStringWin32(buffer);
+			if (str != NULL)
+				RGFW_MEMCPY(win->event.droppedFiles[i], str, length + 1);
+
+			win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
+		}
+
+		DragFinish(drop);
+		RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+
+		win->event.type = RGFW_DND;
+		return &win->event;
+	}
+
+	if (RGFW_checkXInput(win, &win->event))
+		return &win->event;
+
+	static BYTE keyboardState[256];
+	GetKeyboardState(keyboardState);
+
+    MSG msg;
+    if (PeekMessageA(&msg, NULL, 0u, 0u, PM_REMOVE)) {
+        if (msg.hwnd != win->src.window && msg.hwnd != NULL) {
+            TranslateMessage(&msg);
+            DispatchMessageA(&msg);
+            return RGFW_window_checkEvent(win);
+        }
+    } else {
+        return NULL;
+    }
+
+    switch (msg.message) {
+		case WM_MOUSELEAVE:
+			win->event.type = RGFW_mouseLeave;
+			win->_flags |= RGFW_MOUSE_LEFT;
+			RGFW_mouseNotifyCallback(win, win->event.point, 0);
+			break;
+		case WM_SYSKEYUP: case WM_KEYUP: {
+			i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff));
+			if (scancode == 0)
+				scancode = (i32)MapVirtualKeyW((UINT)msg.wParam, MAPVK_VK_TO_VSC);
+
+			switch (scancode) {
+				case 0x54: scancode = 0x137; break; /*  Alt+PrtS */
+				case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+				case 0x136: scancode = 0x36; break; /*  CJK IME sets the extended bit for right Shift */
+				default: break;
+			}
+
+			win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+
+			if (msg.wParam == VK_CONTROL) {
+				if (HIWORD(msg.lParam) & KF_EXTENDED)
+					win->event.key = RGFW_controlR;
+				else win->event.key = RGFW_controlL;
+			}
+
+			wchar_t charBuffer;
+			ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, (wchar_t*)&charBuffer, 1, 0, NULL);
+
+			win->event.keyChar = (u8)charBuffer;
+
+			RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
+			win->event.type = RGFW_keyReleased;
+			RGFW_keyboard[win->event.key].current = 0;
+
+			RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+
+			RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0);
+			break;
+		}
+		case WM_SYSKEYDOWN: case WM_KEYDOWN: {
+            i32 scancode = (HIWORD(msg.lParam) & (KF_EXTENDED | 0xff));
+			if (scancode == 0)
+				scancode = (i32)MapVirtualKeyW((u32)msg.wParam, MAPVK_VK_TO_VSC);
+
+			switch (scancode) {
+				case 0x54: scancode = 0x137; break; /*  Alt+PrtS */
+				case 0x146: scancode = 0x45; break; /* Ctrl+Pause */
+				case 0x136: scancode = 0x36; break; /*  CJK IME sets the extended bit for right Shift */
+				default: break;
+			}
+
+			win->event.key = (u8)RGFW_apiKeyToRGFW((u32) scancode);
+			if (msg.wParam == VK_CONTROL) {
+				if (HIWORD(msg.lParam) & KF_EXTENDED)
+					win->event.key = RGFW_controlR;
+				else win->event.key = RGFW_controlL;
+			}
+
+			wchar_t charBuffer;
+			ToUnicodeEx((UINT)msg.wParam, (UINT)scancode, keyboardState, &charBuffer, 1, 0, NULL);
+			win->event.keyChar = (u8)charBuffer;
+
+			RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
+
+			win->event.type = RGFW_keyPressed;
+			win->event.repeat = RGFW_isPressed(win, win->event.key);
+			RGFW_keyboard[win->event.key].current = 1;
+			RGFW_updateKeyMods(win, (GetKeyState(VK_CAPITAL) & 0x0001), (GetKeyState(VK_NUMLOCK) & 0x0001), (GetKeyState(VK_SCROLL) & 0x0001));
+
+			RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1);
+			break;
+		}
+		case WM_MOUSEMOVE: {
+			if ((win->_flags & RGFW_HOLD_MOUSE))
+				break;
+
+			win->event.type = RGFW_mousePosChanged;
+
+			i32 x = GET_X_LPARAM(msg.lParam);
+			i32 y = GET_Y_LPARAM(msg.lParam);
+
+			RGFW_mousePosCallback(win, win->event.point, win->event.vector);
+
+			if (win->_flags & RGFW_MOUSE_LEFT) {
+				win->_flags &= ~(u32)RGFW_MOUSE_LEFT;
+				win->event.type = RGFW_mouseEnter;
+				RGFW_mouseNotifyCallback(win, win->event.point, 1);
+			}
+
+			win->event.point.x = x;
+			win->event.point.y = y;
+			win->_lastMousePoint = RGFW_POINT(x, y);
+
+			break;
+		}
+		case WM_INPUT: {
+			if (!(win->_flags & RGFW_HOLD_MOUSE))
+				break;
+
+			unsigned size = sizeof(RAWINPUT);
+			static RAWINPUT raw;
+
+			GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, &raw, &size, sizeof(RAWINPUTHEADER));
+
+			if (raw.header.dwType != RIM_TYPEMOUSE || (raw.data.mouse.lLastX == 0 && raw.data.mouse.lLastY == 0) )
+				break;
+
+			if (raw.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
+				POINT pos = {0, 0};
+				int width, height;
+
+				if (raw.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) {
+					pos.x += GetSystemMetrics(SM_XVIRTUALSCREEN);
+					pos.y += GetSystemMetrics(SM_YVIRTUALSCREEN);
+					width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+					height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+				}
+				else {
+					width = GetSystemMetrics(SM_CXSCREEN);
+					height = GetSystemMetrics(SM_CYSCREEN);
+				}
+
+				pos.x += (int) (((float)raw.data.mouse.lLastX / 65535.f) * (float)width);
+				pos.y += (int) (((float)raw.data.mouse.lLastY / 65535.f) * (float)height);
+				ScreenToClient(win->src.window, &pos);
+
+				win->event.vector.x = pos.x - win->_lastMousePoint.x;
+				win->event.vector.y = pos.y - win->_lastMousePoint.y;
+			} else {
+				win->event.vector.x = raw.data.mouse.lLastX;
+				win->event.vector.y = raw.data.mouse.lLastY;
+			}
+
+			win->event.type = RGFW_mousePosChanged;
+			win->_lastMousePoint.x += win->event.vector.x;
+			win->_lastMousePoint.y += win->event.vector.y;
+			win->event.point = win->_lastMousePoint;
+			RGFW_mousePosCallback(win, win->event.point, win->event.vector);
+			break;
+		}
+		case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN:
+			if (msg.message == WM_XBUTTONDOWN)
+				win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2);
+			else win->event.button = (msg.message == WM_LBUTTONDOWN) ? RGFW_mouseLeft :
+									 (msg.message == WM_RBUTTONDOWN) ? RGFW_mouseRight : RGFW_mouseMiddle;
+
+			win->event.type = RGFW_mouseButtonPressed;
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 1;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+			break;
+		case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP:
+			if (msg.message == WM_XBUTTONUP)
+				win->event.button = RGFW_mouseMisc1 + (GET_XBUTTON_WPARAM(msg.wParam) == XBUTTON2);
+			else win->event.button = (msg.message == WM_LBUTTONUP) ? RGFW_mouseLeft :
+									 (msg.message == WM_RBUTTONUP) ? RGFW_mouseRight : RGFW_mouseMiddle;
+			win->event.type = RGFW_mouseButtonReleased;
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 0;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+			break;
+		case WM_MOUSEWHEEL:
+			if (msg.wParam > 0)
+				win->event.button = RGFW_mouseScrollUp;
+			else
+				win->event.button = RGFW_mouseScrollDown;
+
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 1;
+
+			win->event.scroll = (SHORT) HIWORD(msg.wParam) / (double) WHEEL_DELTA;
+
+			win->event.type = RGFW_mouseButtonPressed;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+			break;
+		case WM_DROPFILES: {
+			win->event.type = RGFW_DNDInit;
+
+			drop = (HDROP) msg.wParam;
+			POINT pt;
+
+			/* Move the mouse to the position of the drop */
+			DragQueryPoint(drop, &pt);
+
+			win->event.point.x = pt.x;
+			win->event.point.y = pt.y;
+
+			RGFW_dndInitCallback(win, win->event.point);
+		}
+			break;
+		default:
+			TranslateMessage(&msg);
+			DispatchMessageA(&msg);
+			return RGFW_window_checkEvent(win);
+	}
+
+    TranslateMessage(&msg);
+	DispatchMessageA(&msg);
+
+	return &win->event;
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	return IsWindowVisible(win->src.window) == 0 && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	#ifndef __cplusplus
+	WINDOWPLACEMENT placement = { 0 };
+	#else
+	WINDOWPLACEMENT placement = {  };
+	#endif
+	GetWindowPlacement(win->src.window, &placement);
+	return placement.showCmd == SW_SHOWMINIMIZED;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	#ifndef __cplusplus
+	WINDOWPLACEMENT placement = { 0 };
+	#else
+	WINDOWPLACEMENT placement = {  };
+	#endif
+	GetWindowPlacement(win->src.window, &placement);
+	return placement.showCmd == SW_SHOWMAXIMIZED || IsZoomed(win->src.window);
+}
+
+typedef struct { int iIndex; HMONITOR hMonitor; RGFW_monitor* monitors; } RGFW_mInfo;
+#ifndef RGFW_NO_MONITOR
+RGFW_monitor win32CreateMonitor(HMONITOR src);
+RGFW_monitor win32CreateMonitor(HMONITOR src) {
+	RGFW_monitor monitor;
+	MONITORINFOEX  monitorInfo;
+
+	monitorInfo.cbSize = sizeof(MONITORINFOEX);
+	GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
+
+	/* get the monitor's index */
+	DISPLAY_DEVICEA dd;
+	dd.cb = sizeof(dd);
+
+    DWORD deviceNum;
+	for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) {
+		if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
+			continue;
+
+		DEVMODEA dm;
+		ZeroMemory(&dm, sizeof(dm));
+		dm.dmSize = sizeof(dm);
+
+		if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
+			monitor.mode.refreshRate = dm.dmDisplayFrequency;
+			RGFW_splitBPP(dm.dmBitsPerPel, &monitor.mode);
+		}
+
+		DISPLAY_DEVICEA mdd;
+		mdd.cb = sizeof(mdd);
+
+		if (EnumDisplayDevicesA(dd.DeviceName, (DWORD)deviceNum, &mdd, 0)) {
+			RGFW_STRNCPY(monitor.name, mdd.DeviceString, sizeof(monitor.name) - 1);
+			monitor.name[sizeof(monitor.name) - 1] = '\0';
+			break;
+		}
+	}
+
+
+
+
+	monitor.x = monitorInfo.rcWork.left;
+	monitor.y = monitorInfo.rcWork.top;
+	monitor.mode.area.w = (u32)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
+	monitor.mode.area.h = (u32)(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
+
+	HDC hdc = CreateDC(monitorInfo.szDevice, NULL, NULL, NULL);
+	/* get pixels per inch */
+	float dpiX = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+	float dpiY = (float)GetDeviceCaps(hdc, LOGPIXELSX);
+
+	monitor.scaleX = dpiX / 96.0f;
+	monitor.scaleY = dpiY / 96.0f;
+	monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+
+	monitor.physW = (float)GetDeviceCaps(hdc, HORZSIZE) / 25.4f;
+	monitor.physH = (float)GetDeviceCaps(hdc, VERTSIZE) / 25.4f;
+	DeleteDC(hdc);
+
+	#ifndef RGFW_NO_DPI
+		RGFW_LOAD_LIBRARY(RGFW_Shcore_dll, "shcore.dll");
+		RGFW_PROC_DEF(RGFW_Shcore_dll, GetDpiForMonitor);
+
+		if (GetDpiForMonitor != NULL) {
+			u32 x, y;
+			GetDpiForMonitor(src, MDT_EFFECTIVE_DPI, &x, &y);
+			monitor.scaleX = (float) (x) / (float) 96.0f;
+			monitor.scaleY = (float) (y) / (float) 96.0f;
+			monitor.pixelRatio = dpiX >= 192.0f ? 2.0f : 1.0f;
+		}
+	#endif
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
+	return monitor;
+}
+#endif /* RGFW_NO_MONITOR */
+
+#ifndef RGFW_NO_MONITOR
+BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData);
+BOOL CALLBACK GetMonitorHandle(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+	RGFW_UNUSED(hdcMonitor);
+	RGFW_UNUSED(lprcMonitor);
+
+	RGFW_mInfo* info = (RGFW_mInfo*) dwData;
+
+
+	if (info->iIndex >= 6)
+		return FALSE;
+
+	info->monitors[info->iIndex] = win32CreateMonitor(hMonitor);
+	info->iIndex++;
+
+	return TRUE;
+}
+
+RGFW_monitor RGFW_getPrimaryMonitor(void) {
+	#ifdef __cplusplus
+	return win32CreateMonitor(MonitorFromPoint({ 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
+	#else
+	return win32CreateMonitor(MonitorFromPoint((POINT) { 0, 0 }, MONITOR_DEFAULTTOPRIMARY));
+	#endif
+}
+
+RGFW_monitor* RGFW_getMonitors(size_t* len) {
+	static RGFW_monitor monitors[6];
+	RGFW_mInfo info;
+	info.iIndex = 0;
+	info.monitors = monitors;
+
+	EnumDisplayMonitors(NULL, NULL, GetMonitorHandle, (LPARAM) &info);
+
+	if (len != NULL) *len = (size_t)info.iIndex;
+	return monitors;
+}
+
+RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+	HMONITOR src = MonitorFromWindow(win->src.window, MONITOR_DEFAULTTOPRIMARY);
+	return win32CreateMonitor(src);
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
+    POINT p = { mon.x, mon.y };
+    HMONITOR src = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
+
+	MONITORINFOEX  monitorInfo;
+	monitorInfo.cbSize = sizeof(MONITORINFOEX);
+	GetMonitorInfoA(src, (LPMONITORINFO)&monitorInfo);
+
+    DISPLAY_DEVICEA dd;
+    dd.cb = sizeof(dd);
+
+    /* Enumerate display devices */
+    DWORD deviceNum;
+    for (deviceNum = 0; EnumDisplayDevicesA(NULL, deviceNum, &dd, 0); deviceNum++) {
+        if (!(dd.StateFlags & DISPLAY_DEVICE_ACTIVE))
+			continue;
+
+        if (strcmp(dd.DeviceName, (const char*)monitorInfo.szDevice) != 0)
+            continue;
+		
+        DEVMODEA dm;
+		ZeroMemory(&dm, sizeof(dm));
+		dm.dmSize = sizeof(dm);
+
+		if (EnumDisplaySettingsA(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) {
+			if (request & RGFW_monitorScale) {
+				dm.dmFields |= DM_PELSWIDTH | DM_PELSHEIGHT;
+				dm.dmPelsWidth = mode.area.w;
+				dm.dmPelsHeight = mode.area.h;
+            }
+
+			if (request & RGFW_monitorRefresh) {
+				dm.dmFields |= DM_DISPLAYFREQUENCY;
+				dm.dmDisplayFrequency = mode.refreshRate;
+			}
+
+			if (request & RGFW_monitorRGB) {
+				dm.dmFields |= DM_BITSPERPEL;
+				dm.dmBitsPerPel = (DWORD)(mode.red + mode.green + mode.blue);
+			}
+
+			if (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&dm, NULL, CDS_TEST, NULL) == DISP_CHANGE_SUCCESSFUL) {
+				if (ChangeDisplaySettingsExA((LPCSTR)dd.DeviceName, (DEVMODE *)&dm, NULL, CDS_UPDATEREGISTRY, NULL) == DISP_CHANGE_SUCCESSFUL)
+					return RGFW_TRUE;
+				return RGFW_FALSE;
+			} else return RGFW_FALSE;
+		}
+	}
+
+	return RGFW_FALSE;
+}
+
+#endif
+HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon);
+HICON RGFW_loadHandleImage(u8* src, i32 c, RGFW_area a, BOOL icon) {
+    size_t channels = (size_t)c;
+
+	BITMAPV5HEADER bi;
+	ZeroMemory(&bi, sizeof(bi));
+	bi.bV5Size = sizeof(bi);
+	bi.bV5Width = (i32)a.w;
+	bi.bV5Height = -((LONG) a.h);
+	bi.bV5Planes = 1;
+	bi.bV5BitCount = (WORD)(channels * 8);
+	bi.bV5Compression = BI_RGB;
+	HDC dc = GetDC(NULL);
+	u8* target = NULL;
+
+	HBITMAP color = CreateDIBSection(dc,
+		(BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target,
+		NULL, (DWORD) 0);
+
+    size_t x, y;
+    for (y = 0; y < a.h; y++) {
+        for (x = 0; x < a.w; x++) {
+			size_t index = (y * 4 * (size_t)a.w) + x * channels;
+            target[index] = src[index + 2];
+            target[index + 1] = src[index + 1];
+            target[index + 2] = src[index];
+            target[index + 3] = src[index + 3];
+        }
+    }
+
+    ReleaseDC(NULL, dc);
+
+	HBITMAP mask = CreateBitmap((i32)a.w, (i32)a.h, 1, 1, NULL);
+
+	ICONINFO ii;
+	ZeroMemory(&ii, sizeof(ii));
+	ii.fIcon = icon;
+	ii.xHotspot = a.w / 2;
+	ii.yHotspot = a.h / 2;
+	ii.hbmMask = mask;
+	ii.hbmColor = color;
+
+	HICON handle = CreateIconIndirect(&ii);
+
+	DeleteObject(color);
+	DeleteObject(mask);
+
+	return handle;
+}
+
+void* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
+	HCURSOR cursor = (HCURSOR) RGFW_loadHandleImage(icon, channels, a, FALSE);
+	return cursor;
+}
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+	RGFW_ASSERT(win && mouse);
+	SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) mouse);
+	SetCursor((HCURSOR)mouse);
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+	RGFW_ASSERT(mouse);
+	DestroyCursor((HCURSOR)mouse);
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+	return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+	RGFW_ASSERT(win != NULL);
+
+	static const u32 mouseIconSrc[16] = {OCR_NORMAL, OCR_NORMAL, OCR_IBEAM, OCR_CROSS, OCR_HAND, OCR_SIZEWE, OCR_SIZENS, OCR_SIZENWSE, OCR_SIZENESW, OCR_SIZEALL, OCR_NO};
+	if (mouse > (sizeof(mouseIconSrc) / sizeof(u32)))
+		return RGFW_FALSE;
+
+	char* icon = MAKEINTRESOURCEA(mouseIconSrc[mouse]);
+
+	SetClassLongPtrA(win->src.window, GCLP_HCURSOR, (LPARAM) LoadCursorA(NULL, icon));
+	SetCursor(LoadCursorA(NULL, icon));
+	return RGFW_TRUE;
+}
+
+void RGFW_window_hide(RGFW_window* win) {
+	ShowWindow(win->src.window, SW_HIDE);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+	if (win->_flags & RGFW_windowFocusOnShow) RGFW_window_focus(win);
+	ShowWindow(win->src.window, SW_RESTORE);
+}
+
+#define RGFW_FREE_LIBRARY(x) if (x != NULL) FreeLibrary(x); x = NULL;
+void RGFW_deinit(void) {
+    #ifndef RGFW_NO_XINPUT
+    RGFW_FREE_LIBRARY(RGFW_XInput_dll);
+    #endif
+
+    #ifndef RGFW_NO_DPI
+        RGFW_FREE_LIBRARY(RGFW_Shcore_dll);
+    #endif
+
+    #ifndef RGFW_NO_WINMM
+        timeEndPeriod(1);
+        #ifndef RGFW_NO_LOAD_WINMM
+            RGFW_FREE_LIBRARY(RGFW_winmm_dll);
+        #endif
+    #endif
+
+    RGFW_FREE_LIBRARY(RGFW_wgl_dll);
+    _RGFW.root = NULL;
+
+    RGFW_freeMouse(_RGFW.hiddenMouse);
+    _RGFW.windowCount = -1;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
+}
+
+
+void RGFW_window_close(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	#ifdef RGFW_BUFFER
+		DeleteDC(win->src.hdcMem);
+		DeleteObject(win->src.bitmap);
+	#endif
+
+	if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
+	RemovePropW(win->src.window, L"RGFW");
+	ReleaseDC(win->src.window, win->src.hdc); /*!< delete device context */
+	DestroyWindow(win->src.window); /*!< delete window */
+
+	if (win->src.hIconSmall) DestroyIcon(win->src.hIconSmall);
+	if (win->src.hIconBig) DestroyIcon(win->src.hIconBig);
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
+    _RGFW.windowCount--;
+	if (_RGFW.windowCount == 0) RGFW_deinit();
+
+    RGFW_clipboard_switch(NULL);
+	RGFW_FREE(win->event.droppedFiles);
+	if ((win->_flags & RGFW_WINDOW_ALLOC)) {
+		RGFW_FREE(win);
+        win = NULL;
+    }
+}
+
+void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+	RGFW_ASSERT(win != NULL);
+
+	win->r.x = v.x;
+	win->r.y = v.y;
+	SetWindowPos(win->src.window, HWND_TOP, win->r.x, win->r.y, 0, 0, SWP_NOSIZE);
+}
+
+void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+
+	win->r.w = (i32)a.w;
+	win->r.h = (i32)a.h;
+	SetWindowPos(win->src.window, HWND_TOP, 0, 0, win->r.w + (i32)win->src.wOffset, win->r.h + (i32)win->src.hOffset, SWP_NOMOVE);
+}
+
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+	RGFW_ASSERT(win != NULL);
+
+	wchar_t wide_name[256];
+	MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, 256);
+	SetWindowTextW(win->src.window, wide_name);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+	RGFW_ASSERT(win != NULL);
+	COLORREF key = 0;
+	BYTE alpha = 0;
+	DWORD flags = 0;
+	i32 exStyle = GetWindowLongW(win->src.window, GWL_EXSTYLE);
+
+	if (exStyle & WS_EX_LAYERED)
+		GetLayeredWindowAttributes(win->src.window, &key, &alpha, &flags);
+
+	if (passthrough)
+		exStyle |= (WS_EX_TRANSPARENT | WS_EX_LAYERED);
+	else {
+		exStyle &= ~WS_EX_TRANSPARENT;
+		if (exStyle & WS_EX_LAYERED && !(flags & LWA_ALPHA))
+			exStyle &= ~WS_EX_LAYERED;
+	}
+
+	SetWindowLongW(win->src.window, GWL_EXSTYLE, exStyle);
+
+	if (passthrough)
+		SetLayeredWindowAttributes(win->src.window, key, alpha, flags);
+}
+#endif
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* src, RGFW_area a, i32 channels, u8 type) {
+	RGFW_ASSERT(win != NULL);
+	#ifndef RGFW_WIN95
+		RGFW_UNUSED(channels);
+
+		if (win->src.hIconSmall && (type & RGFW_iconWindow)) DestroyIcon(win->src.hIconSmall);
+		if (win->src.hIconBig && (type & RGFW_iconTaskbar)) DestroyIcon(win->src.hIconBig);
+
+		if (src == NULL) {
+			HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION);
+			if (type & RGFW_iconWindow)
+				SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)defaultIcon);
+			if (type & RGFW_iconTaskbar)
+				SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)defaultIcon);
+			return RGFW_TRUE;
+		}
+
+		if (type & RGFW_iconWindow) {
+			win->src.hIconSmall = RGFW_loadHandleImage(src, channels, a, TRUE);
+			SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)win->src.hIconSmall);
+		}
+		if (type & RGFW_iconTaskbar) {
+			win->src.hIconBig = RGFW_loadHandleImage(src, channels, a, TRUE);
+			SendMessage(win->src.window, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)win->src.hIconBig);
+		}
+		return RGFW_TRUE;
+	#else
+		RGFW_UNUSED(src);
+		RGFW_UNUSED(a);
+		RGFW_UNUSED(channels);
+		return RGFW_FALSE;
+	#endif
+}
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+	/* Open the clipboard */
+	if (OpenClipboard(NULL) == 0)
+		return -1;
+
+	/* Get the clipboard data as a Unicode string */
+	HANDLE hData = GetClipboardData(CF_UNICODETEXT);
+	if (hData == NULL) {
+		CloseClipboard();
+		return -1;
+	}
+
+	wchar_t* wstr = (wchar_t*) GlobalLock(hData);
+
+	RGFW_ssize_t textLen = 0;
+
+	{
+		setlocale(LC_ALL, "en_US.UTF-8");
+
+		textLen = (RGFW_ssize_t)wcstombs(NULL, wstr, 0) + 1;
+		if (str != NULL && (RGFW_ssize_t)strCapacity <= textLen - 1)
+			textLen = 0;
+
+		if (str != NULL && textLen) {
+			if (textLen > 1)
+				wcstombs(str, wstr, (size_t)(textLen));
+
+			str[textLen] = '\0';
+		}
+	}
+
+	/* Release the clipboard data */
+	GlobalUnlock(hData);
+	CloseClipboard();
+
+	return textLen;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+	HANDLE object;
+	WCHAR* buffer;
+
+	object = GlobalAlloc(GMEM_MOVEABLE, (1 + textLen) * sizeof(WCHAR));
+	if (!object)
+		return;
+
+	buffer = (WCHAR*) GlobalLock(object);
+	if (!buffer) {
+		GlobalFree(object);
+		return;
+	}
+
+	MultiByteToWideChar(CP_UTF8, 0, text, -1, buffer, (i32)textLen);
+	GlobalUnlock(object);
+
+	if (!OpenClipboard(_RGFW.root->src.window)) {
+		GlobalFree(object);
+		return;
+	}
+
+	EmptyClipboard();
+	SetClipboardData(CF_UNICODETEXT, object);
+	CloseClipboard();
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) {
+	RGFW_ASSERT(win != NULL);
+	win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y);
+	SetCursorPos(p.x, p.y);
+}
+
+#ifdef RGFW_OPENGL
+void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+	if (win == NULL)
+		wglMakeCurrent(NULL, NULL);
+	else
+		wglMakeCurrent(win->src.hdc, (HGLRC) win->src.ctx);
+}
+void* RGFW_getCurrent_OpenGL(void) { return wglGetCurrentContext(); }
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win){ SwapBuffers(win->src.hdc); }
+#endif
+
+#ifndef RGFW_EGL
+void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+	RGFW_ASSERT(win != NULL);
+#if defined(RGFW_OPENGL)
+    if (wglSwapIntervalEXT == NULL || wglSwapIntervalEXT(swapInterval) == FALSE)
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to set swap interval");
+#else
+	RGFW_UNUSED(swapInterval);
+#endif
+}
+#endif
+
+void RGFW_window_swapBuffers_software(RGFW_window* win) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	if (win->buffer != win->src.bitmapBits)
+	memcpy(win->src.bitmapBits, win->buffer, win->bufferSize.w * win->bufferSize.h * 4);
+
+	RGFW_RGB_to_BGR(win, win->src.bitmapBits);
+	BitBlt(win->src.hdc, 0, 0, win->r.w, win->r.h, win->src.hdcMem, 0, 0, SRCCOPY);
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+char* RGFW_createUTF8FromWideStringWin32(const WCHAR* source) {
+	if (source == NULL) {
+		return NULL;
+	}
+	i32 size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL);
+	if (!size) {
+		return NULL;
+	}
+
+	static char target[RGFW_MAX_PATH * 2];
+	if (size > RGFW_MAX_PATH * 2)
+		size = RGFW_MAX_PATH * 2;
+
+	target[size] = 0;
+
+	if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) {
+		return NULL;
+	}
+
+	return target;
+}
+
+u64 RGFW_getTimerFreq(void) {
+	static u64 frequency = 0;
+	if (frequency == 0) QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
+
+	return frequency;
+}
+
+u64 RGFW_getTimerValue(void) {
+	u64 value;
+	QueryPerformanceCounter((LARGE_INTEGER*)&value);
+	return value;
+}
+
+void RGFW_sleep(u64 ms) {
+	Sleep((u32)ms);
+}
+
+#ifndef RGFW_NO_THREADS
+
+RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) { return CreateThread(NULL, 0, ptr, args, 0, NULL); }
+void RGFW_cancelThread(RGFW_thread thread) { CloseHandle((HANDLE) thread); }
+void RGFW_joinThread(RGFW_thread thread) { WaitForSingleObject((HANDLE) thread, INFINITE); }
+void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { SetThreadPriority((HANDLE) thread, priority); }
+
+#endif
+#endif /* RGFW_WINDOWS */
+
+/*
+	End of Windows defines
+*/
+
+
+
+/*
+
+	Start of MacOS defines
+
+
+*/
+
+#if defined(RGFW_MACOS)
+/*
+	based on silicon.h
+	start of cocoa wrapper
+*/
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <objc/runtime.h>
+#include <objc/message.h>
+#include <mach/mach_time.h>
+#include <CoreVideo/CoreVideo.h>
+
+typedef CGRect NSRect;
+typedef CGPoint NSPoint;
+typedef CGSize NSSize;
+
+typedef const char* NSPasteboardType;
+typedef unsigned long NSUInteger;
+typedef long NSInteger;
+typedef NSInteger NSModalResponse;
+
+#ifdef __arm64__
+	/* ARM just uses objc_msgSend */
+#define abi_objc_msgSend_stret objc_msgSend
+#define abi_objc_msgSend_fpret objc_msgSend
+#else /* __i386__ */
+	/* x86 just uses abi_objc_msgSend_fpret and (NSColor *)objc_msgSend_id respectively */
+#define abi_objc_msgSend_stret objc_msgSend_stret
+#define abi_objc_msgSend_fpret objc_msgSend_fpret
+#endif
+
+#define NSAlloc(nsclass) objc_msgSend_id((id)nsclass, sel_registerName("alloc"))
+#define objc_msgSend_bool(x, y)			((BOOL (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void(x, y)			((void (*)(id, SEL))objc_msgSend) ((id)(x), (SEL)y)
+#define objc_msgSend_void_id(x, y, z)		((void (*)(id, SEL, id))objc_msgSend) ((id)x, (SEL)y, (id)z)
+#define objc_msgSend_uint(x, y)			((NSUInteger (*)(id, SEL))objc_msgSend)  ((id)(x), (SEL)y)
+#define objc_msgSend_void_bool(x, y, z)		((void (*)(id, SEL, BOOL))objc_msgSend)  ((id)(x), (SEL)y, (BOOL)z)
+#define objc_msgSend_bool_void(x, y)		((BOOL (*)(id, SEL))objc_msgSend)  ((id)(x), (SEL)y)
+#define objc_msgSend_void_SEL(x, y, z)		((void (*)(id, SEL, SEL))objc_msgSend)  ((id)(x), (SEL)y, (SEL)z)
+#define objc_msgSend_id(x, y)				((id (*)(id, SEL))objc_msgSend)  ((id)(x), (SEL)y)
+#define objc_msgSend_id_id(x, y, z)			((id (*)(id, SEL, id))objc_msgSend)  ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_id_bool(x, y, z)			((BOOL (*)(id, SEL, id))objc_msgSend)  ((id)(x), (SEL)y, (id)z)
+#define objc_msgSend_int(x, y, z) 				((id (*)(id, SEL, int))objc_msgSend)  ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_arr(x, y, z)				 	((id (*)(id, SEL, int))objc_msgSend)  ((id)(x), (SEL)y, (int)z)
+#define objc_msgSend_ptr(x, y, z) 					((id (*)(id, SEL, void*))objc_msgSend)  ((id)(x), (SEL)y, (void*)z)
+#define objc_msgSend_class(x, y) 					((id (*)(Class, SEL))objc_msgSend)  ((Class)(x), (SEL)y)
+#define objc_msgSend_class_char(x, y, z) 			((id (*)(Class, SEL, char*))objc_msgSend)  ((Class)(x), (SEL)y, (char*)z)
+
+id NSApp = NULL;
+
+#define NSRelease(obj) objc_msgSend_void((id)obj, sel_registerName("release"))
+id NSString_stringWithUTF8String(const char* str);
+id NSString_stringWithUTF8String(const char* str) {
+	return ((id(*)(id, SEL, const char*))objc_msgSend)
+		((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), str);
+}
+
+const char* NSString_to_char(id str);
+const char* NSString_to_char(id str) {
+	return ((const char* (*)(id, SEL)) objc_msgSend) ((id)(id)str, sel_registerName("UTF8String"));
+}
+
+void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function);
+void si_impl_func_to_SEL_with_name(const char* class_name, const char* register_name, void* function) {
+	Class selected_class;
+
+	if (RGFW_STRNCMP(class_name, "NSView", 6) == 0) {
+		selected_class = objc_getClass("ViewClass");
+	} else if (RGFW_STRNCMP(class_name, "NSWindow", 8) == 0) {
+		selected_class = objc_getClass("WindowClass");
+	} else {
+		selected_class = objc_getClass(class_name);
+	}
+
+	class_addMethod(selected_class, sel_registerName(register_name), (IMP) function, 0);
+}
+
+/* Header for the array. */
+typedef struct siArrayHeader {
+	size_t count;
+	/* TODO(EimaMei): Add a `type_width` later on. */
+} siArrayHeader;
+
+/* Gets the header of the siArray. */
+#define SI_ARRAY_HEADER(s) ((siArrayHeader*)s - 1)
+#define si_array_len(array) (SI_ARRAY_HEADER(array)->count)
+#define si_func_to_SEL(class_name, function) si_impl_func_to_SEL_with_name(class_name, #function":", (void*)function)
+/* Creates an Objective-C method (SEL) from a regular C function with the option to set the register name.*/
+#define si_func_to_SEL_with_name(class_name, register_name, function) si_impl_func_to_SEL_with_name(class_name, register_name":", (void*)function)
+
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep);
+unsigned char* NSBitmapImageRep_bitmapData(id imageRep) {
+	return ((unsigned char* (*)(id, SEL))objc_msgSend) ((id)imageRep, sel_registerName("bitmapData"));
+}
+
+typedef RGFW_ENUM(NSUInteger, NSBitmapFormat) {
+	NSBitmapFormatAlphaFirst = 1 << 0,       /* 0 means is alpha last (RGBA, CMYKA, etc.) */
+		NSBitmapFormatAlphaNonpremultiplied = 1 << 1,       /* 0 means is premultiplied */
+		NSBitmapFormatFloatingpointSamples = 1 << 2,  /* 0 is integer */
+
+		NSBitmapFormatSixteenBitLittleEndian = (1 << 8),
+		NSBitmapFormatThirtyTwoBitLittleEndian = (1 << 9),
+		NSBitmapFormatSixteenBitBigEndian = (1 << 10),
+		NSBitmapFormatThirtyTwoBitBigEndian = (1 << 11)
+};
+
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits);
+id NSBitmapImageRep_initWithBitmapData(unsigned char** planes, NSInteger width, NSInteger height, NSInteger bps, NSInteger spp, bool alpha, bool isPlanar, const char* colorSpaceName, NSBitmapFormat bitmapFormat, NSInteger rowBytes, NSInteger pixelBits) {
+	SEL func = sel_registerName("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bitmapFormat:bytesPerRow:bitsPerPixel:");
+
+	return (id) ((id(*)(id, SEL, unsigned char**, NSInteger, NSInteger, NSInteger, NSInteger, bool, bool, id, NSBitmapFormat, NSInteger, NSInteger))objc_msgSend)
+		(NSAlloc((id)objc_getClass("NSBitmapImageRep")), func, planes, width, height, bps, spp, alpha, isPlanar, NSString_stringWithUTF8String(colorSpaceName), bitmapFormat, rowBytes, pixelBits);
+}
+
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha);
+id NSColor_colorWithSRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) {
+	void* nsclass = objc_getClass("NSColor");
+	SEL func = sel_registerName("colorWithSRGBRed:green:blue:alpha:");
+	return ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)
+		((id)nsclass, func, red, green, blue, alpha);
+}
+
+typedef RGFW_ENUM(NSInteger, NSOpenGLContextParameter) {
+	NSOpenGLContextParameterSwapInterval            = 222, /* 1 param.  0 -> Don't sync, 1 -> Sync to vertical retrace     */
+		NSOpenGLContextParametectxaceOrder            = 235, /* 1 param.  1 -> Above Window (default), -1 -> Below Window    */
+		NSOpenGLContextParametectxaceOpacity          = 236, /* 1 param.  1-> Surface is opaque (default), 0 -> non-opaque   */
+		NSOpenGLContextParametectxaceBackingSize      = 304, /* 2 params.  Width/height of surface backing size              */
+		NSOpenGLContextParameterReclaimResources        = 308, /* 0 params.                                                    */
+		NSOpenGLContextParameterCurrentRendererID       = 309, /* 1 param.   Retrieves the current renderer ID                 */
+		NSOpenGLContextParameterGPUVertexProcessing     = 310, /* 1 param.   Currently processing vertices with GPU (get)      */
+		NSOpenGLContextParameterGPUFragmentProcessing   = 311, /* 1 param.   Currently processing fragments with GPU (get)     */
+		NSOpenGLContextParameterHasDrawable             = 314, /* 1 param.   Boolean returned if drawable is attached          */
+		NSOpenGLContextParameterMPSwapsInFlight         = 315, /* 1 param.   Max number of swaps queued by the MP GL engine    */
+
+		NSOpenGLContextParameterSwapRectangle API_DEPRECATED("", macos(10.0, 10.14)) = 200, /* 4 params.  Set or get the swap rectangle {x, y, w, h} */
+		NSOpenGLContextParameterSwapRectangleEnable API_DEPRECATED("", macos(10.0, 10.14)) = 201, /* Enable or disable the swap rectangle */
+		NSOpenGLContextParameterRasterizationEnable API_DEPRECATED("", macos(10.0, 10.14)) = 221, /* Enable or disable all rasterization */
+		NSOpenGLContextParameterStateValidation API_DEPRECATED("", macos(10.0, 10.14)) = 301, /* Validate state for multi-screen functionality */
+		NSOpenGLContextParametectxaceSurfaceVolatile API_DEPRECATED("", macos(10.0, 10.14)) = 306, /* 1 param.   Surface volatile state */
+};
+
+typedef RGFW_ENUM(NSInteger, NSWindowButton) {
+    NSWindowCloseButton            = 0,
+    NSWindowMiniaturizeButton      = 1,
+    NSWindowZoomButton             = 2,
+    NSWindowToolbarButton          = 3,
+    NSWindowDocumentIconButton     = 4,
+    NSWindowDocumentVersionsButton = 6,
+    NSWindowFullScreenButton       = 7,
+};
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param);
+void NSOpenGLContext_setValues(id context, const int* vals, NSOpenGLContextParameter param) {
+	((void (*)(id, SEL, const int*, NSOpenGLContextParameter))objc_msgSend)
+		(context, sel_registerName("setValues:forParameter:"), vals, param);
+}
+void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs);
+void* NSOpenGLPixelFormat_initWithAttributes(const uint32_t* attribs) {
+	return (void*) ((id(*)(id, SEL, const uint32_t*))objc_msgSend)
+		(NSAlloc((id)objc_getClass("NSOpenGLPixelFormat")), sel_registerName("initWithAttributes:"), attribs);
+}
+
+id NSPasteboard_generalPasteboard(void);
+id NSPasteboard_generalPasteboard(void) {
+	return (id) objc_msgSend_id((id)objc_getClass("NSPasteboard"), sel_registerName("generalPasteboard"));
+}
+
+id* cstrToNSStringArray(char** strs, size_t len);
+id* cstrToNSStringArray(char** strs, size_t len) {
+	static id nstrs[6];
+	size_t i;
+	for (i = 0; i < len; i++)
+		nstrs[i] = NSString_stringWithUTF8String(strs[i]);
+
+	return nstrs;
+}
+
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len);
+const char* NSPasteboard_stringForType(id pasteboard, NSPasteboardType dataType, size_t* len) {
+	SEL func = sel_registerName("stringForType:");
+	id nsstr = NSString_stringWithUTF8String(dataType);
+	id nsString = ((id(*)(id, SEL, id))objc_msgSend)(pasteboard, func, nsstr);
+	const char* str = NSString_to_char(nsString);
+	if (len != NULL)
+		*len = (size_t)((NSUInteger(*)(id, SEL, int))objc_msgSend)(nsString, sel_registerName("maximumLengthOfBytesUsingEncoding:"), 4);
+	return str;
+}
+
+id c_array_to_NSArray(void* array, size_t len);
+id c_array_to_NSArray(void* array, size_t len) {
+	SEL func = sel_registerName("initWithObjects:count:");
+	void* nsclass = objc_getClass("NSArray");
+	return ((id (*)(id, SEL, void*, NSUInteger))objc_msgSend)
+				(NSAlloc(nsclass), func, array, len);
+}
+
+
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len);
+void NSregisterForDraggedTypes(id view, NSPasteboardType* newTypes, size_t len) {
+	id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+	id array = c_array_to_NSArray(ntypes, len);
+	objc_msgSend_void_id(view, sel_registerName("registerForDraggedTypes:"), array);
+	NSRelease(array);
+}
+
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner);
+NSInteger NSPasteBoard_declareTypes(id pasteboard, NSPasteboardType* newTypes, size_t len, void* owner) {
+	id* ntypes = cstrToNSStringArray((char**)newTypes, len);
+
+	SEL func = sel_registerName("declareTypes:owner:");
+
+	id array = c_array_to_NSArray(ntypes, len);
+
+	NSInteger output = ((NSInteger(*)(id, SEL, id, void*))objc_msgSend)
+		(pasteboard, func, array, owner);
+	NSRelease(array);
+
+	return output;
+}
+
+#define NSRetain(obj) objc_msgSend_void((id)obj, sel_registerName("retain"))
+
+typedef enum NSApplicationActivationPolicy {
+	NSApplicationActivationPolicyRegular,
+	NSApplicationActivationPolicyAccessory,
+	NSApplicationActivationPolicyProhibited
+} NSApplicationActivationPolicy;
+
+typedef RGFW_ENUM(u32, NSBackingStoreType) {
+	NSBackingStoreRetained = 0,
+		NSBackingStoreNonretained = 1,
+		NSBackingStoreBuffered = 2
+};
+
+typedef RGFW_ENUM(u32, NSWindowStyleMask) {
+	NSWindowStyleMaskBorderless = 0,
+		NSWindowStyleMaskTitled = 1 << 0,
+		NSWindowStyleMaskClosable = 1 << 1,
+		NSWindowStyleMaskMiniaturizable = 1 << 2,
+		NSWindowStyleMaskResizable = 1 << 3,
+		NSWindowStyleMaskTexturedBackground = 1 << 8, /* deprecated */
+		NSWindowStyleMaskUnifiedTitleAndToolbar = 1 << 12,
+		NSWindowStyleMaskFullScreen = 1 << 14,
+		NSWindowStyleMaskFullSizeContentView = 1 << 15,
+		NSWindowStyleMaskUtilityWindow = 1 << 4,
+		NSWindowStyleMaskDocModalWindow = 1 << 6,
+		NSWindowStyleMaskNonactivatingpanel = 1 << 7,
+		NSWindowStyleMaskHUDWindow = 1 << 13
+};
+
+NSPasteboardType const NSPasteboardTypeString = "public.utf8-plain-text"; /* Replaces NSStringPasteboardType */
+
+
+typedef RGFW_ENUM(i32, NSDragOperation) {
+	NSDragOperationNone = 0,
+		NSDragOperationCopy = 1,
+		NSDragOperationLink = 2,
+		NSDragOperationGeneric = 4,
+		NSDragOperationPrivate = 8,
+		NSDragOperationMove = 16,
+		NSDragOperationDelete = 32,
+		NSDragOperationEvery = (int)ULONG_MAX
+};
+
+void* NSArray_objectAtIndex(id array, NSUInteger index) {
+	SEL func = sel_registerName("objectAtIndex:");
+	return ((id(*)(id, SEL, NSUInteger))objc_msgSend)(array, func, index);
+}
+
+id NSWindow_contentView(id window) {
+	SEL func = sel_registerName("contentView");
+	return objc_msgSend_id(window, func);
+}
+
+/*
+	End of cocoa wrapper
+*/
+
+#ifdef RGFW_OPENGL
+/* MacOS opengl API spares us yet again (there are no extensions) */
+RGFW_bool RGFW_extensionSupportedPlatform(const char * extension, size_t len) { RGFW_UNUSED(extension); RGFW_UNUSED(len); return RGFW_FALSE; }
+CFBundleRef RGFWnsglFramework = NULL;
+
+RGFW_proc RGFW_getProcAddress(const char* procname) {
+	if (RGFWnsglFramework == NULL)
+		RGFWnsglFramework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
+
+	CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII);
+
+	RGFW_proc symbol = (RGFW_proc)CFBundleGetFunctionPointerForName(RGFWnsglFramework, symbolName);
+
+	CFRelease(symbolName);
+
+	return symbol;
+}
+#endif
+
+id NSWindow_delegate(RGFW_window* win) {
+	return (id) objc_msgSend_id((id)win->src.window, sel_registerName("delegate"));
+}
+
+u32 RGFW_OnClose(id self) {
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, (const char*)"RGFW_window", (void**)&win);
+	if (win == NULL)
+		return true;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_quit; e._win = win);
+	RGFW_windowQuitCallback(win);
+
+	return false;
+}
+
+/* NOTE(EimaMei): Fixes the constant clicking when the app is running under a terminal. */
+bool acceptsFirstResponder(void) { return true; }
+bool performKeyEquivalent(id event) { RGFW_UNUSED(event); return true; }
+
+NSDragOperation draggingEntered(id self, SEL sel, id sender) {
+	RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
+
+	return NSDragOperationCopy;
+}
+NSDragOperation draggingUpdated(id self, SEL sel, id sender) {
+	RGFW_UNUSED(sel);
+
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL || (!(win->_flags & RGFW_windowAllowDND)))
+		return 0;
+
+	NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+	RGFW_eventQueuePushEx(e.type = RGFW_DNDInit;
+									e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+									e._win = win);
+
+	RGFW_dndInitCallback(win, win->event.point);
+	return NSDragOperationCopy;
+}
+bool prepareForDragOperation(id self) {
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL)
+		return true;
+
+	if (!(win->_flags & RGFW_windowAllowDND)) {
+		return false;
+	}
+
+	return true;
+}
+
+void RGFW__osxDraggingEnded(id self, SEL sel, id sender);
+void RGFW__osxDraggingEnded(id self, SEL sel, id sender) { RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);  return; }
+
+/* NOTE(EimaMei): Usually, you never need 'id self, SEL cmd' for C -> Obj-C methods. This isn't the case. */
+bool performDragOperation(id self, SEL sel, id sender) {
+	RGFW_UNUSED(sender); RGFW_UNUSED(self); RGFW_UNUSED(sel);
+
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+
+	if (win == NULL)
+		return false;
+
+	/* id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard")); */
+
+	id pasteBoard = objc_msgSend_id(sender, sel_registerName("draggingPasteboard"));
+
+	/* Get the types of data available on the pasteboard */
+	id types = objc_msgSend_id(pasteBoard, sel_registerName("types"));
+
+	/* Get the string type for file URLs */
+	id fileURLsType = objc_msgSend_class_char(objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "NSFilenamesPboardType");
+
+	/* Check if the pasteboard contains file URLs */
+	if (objc_msgSend_id_bool(types, sel_registerName("containsObject:"), fileURLsType) == 0) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errClipboard, RGFW_DEBUG_CTX(win, 0), "No files found on the pasteboard.");
+		return 0;
+	}
+
+	id fileURLs = objc_msgSend_id_id(pasteBoard, sel_registerName("propertyListForType:"), fileURLsType);
+	int count = ((int (*)(id, SEL))objc_msgSend)(fileURLs, sel_registerName("count"));
+
+	if (count == 0)
+		return 0;
+
+    int i;
+    for (i = 0; i < count; i++) {
+		id fileURL = objc_msgSend_arr(fileURLs, sel_registerName("objectAtIndex:"), i);
+		const char *filePath = ((const char* (*)(id, SEL))objc_msgSend)(fileURL, sel_registerName("UTF8String"));
+		RGFW_STRNCPY(win->event.droppedFiles[i], filePath, RGFW_MAX_PATH - 1);
+		win->event.droppedFiles[i][RGFW_MAX_PATH - 1] = '\0';
+	}
+	NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(sender, sel_registerName("draggingLocation"));
+	
+	win->event.droppedFilesCount = (size_t)count; 
+	RGFW_eventQueuePushEx(e.type = RGFW_DND;
+									e.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+									e.droppedFilesCount = (size_t)count;
+									e._win = win);
+	
+	RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount);
+
+	return false;
+}
+
+#ifndef RGFW_NO_IOKIT
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDManager.h>
+
+u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID) {
+    u32 refreshRate = 0;
+    io_iterator_t it;
+    io_service_t service;
+    CFNumberRef indexRef, clockRef, countRef;
+    uint32_t clock, count;
+
+#ifdef kIOMainPortDefault 
+    if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#elif defined(kIOMasterPortDefault) 
+    if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IOFramebuffer"), &it) != 0)
+#endif
+        return RGFW_FALSE;
+
+    while ((service = IOIteratorNext(it)) != 0) {
+        uint32_t index;
+        indexRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions);
+        if (indexRef == 0) continue;
+        
+        if (CFNumberGetValue(indexRef, kCFNumberIntType, &index) && CGOpenGLDisplayMaskToDisplayID(1 << index) == displayID) {
+            CFRelease(indexRef);
+            break;
+        }
+
+        CFRelease(indexRef);
+    }
+
+    if (service) {
+        clockRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions);
+        if (clockRef) {
+            if (CFNumberGetValue(clockRef, kCFNumberIntType, &clock) && clock) {
+                countRef = (CFNumberRef)IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions);
+                if (countRef && CFNumberGetValue(countRef, kCFNumberIntType, &count) && count) {
+                    refreshRate = (u32)RGFW_ROUND(clock / (double) count);
+                    CFRelease(countRef);
+                }
+            }
+            CFRelease(clockRef);
+        }
+    }
+
+    IOObjectRelease(it);
+    return refreshRate;
+}
+
+IOHIDDeviceRef RGFW_osxControllers[4] = {NULL};
+
+size_t findControllerIndex(IOHIDDeviceRef device) {
+    size_t i;
+    for (i = 0; i < 4; i++)
+		if (RGFW_osxControllers[i] == device)
+			return i;
+	return (size_t)-1;
+}
+
+void RGFW__osxInputValueChangedCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) {
+	RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender);
+	IOHIDElementRef element = IOHIDValueGetElement(value);
+
+	IOHIDDeviceRef device = IOHIDElementGetDevice(element);
+	size_t index = findControllerIndex(device);
+	if (index == (size_t)-1) return;
+
+	uint32_t usagePage = IOHIDElementGetUsagePage(element);
+	uint32_t usage = IOHIDElementGetUsage(element);
+
+	CFIndex intValue = IOHIDValueGetIntegerValue(value);
+
+	u8 RGFW_osx2RGFWSrc[2][RGFW_gamepadFinal] = {{
+		0, RGFW_gamepadSelect, RGFW_gamepadL3, RGFW_gamepadR3, RGFW_gamepadStart,
+		RGFW_gamepadUp, RGFW_gamepadRight, RGFW_gamepadDown, RGFW_gamepadLeft,
+		RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadL1, RGFW_gamepadR1,
+		RGFW_gamepadY, RGFW_gamepadB, RGFW_gamepadA, RGFW_gamepadX, RGFW_gamepadHome},
+		{0, RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadR3, RGFW_gamepadX,
+		RGFW_gamepadY, RGFW_gamepadRight, RGFW_gamepadL1, RGFW_gamepadR1,
+		RGFW_gamepadL2, RGFW_gamepadR2, RGFW_gamepadDown, RGFW_gamepadStart,
+		RGFW_gamepadUp, RGFW_gamepadL3, RGFW_gamepadSelect, RGFW_gamepadStart, RGFW_gamepadHome}
+	};
+
+	u8* RGFW_osx2RGFW = RGFW_osx2RGFWSrc[0];
+	if (RGFW_gamepads_type[index] == RGFW_gamepadMicrosoft)
+		RGFW_osx2RGFW = RGFW_osx2RGFWSrc[1];
+
+	switch (usagePage) {
+		case kHIDPage_Button: {
+			u8 button = 0;
+			if (usage < sizeof(RGFW_osx2RGFW))
+				button = RGFW_osx2RGFW[usage];
+
+			RGFW_gamepadButtonCallback(_RGFW.root, (u16)index, button, (u8)intValue);
+			RGFW_gamepadPressed[index][button].prev = RGFW_gamepadPressed[index][button].current;
+			RGFW_gamepadPressed[index][button].current = RGFW_BOOL(intValue);
+			RGFW_eventQueuePushEx(e.type = intValue ? RGFW_gamepadButtonPressed: RGFW_gamepadButtonReleased;
+											e.button = button;
+											e.gamepad = (u16)index;
+											e._win = _RGFW.root);
+			break;
+		}
+		case kHIDPage_GenericDesktop: {
+			CFIndex logicalMin = IOHIDElementGetLogicalMin(element);
+			CFIndex logicalMax = IOHIDElementGetLogicalMax(element);
+
+			if (logicalMax <= logicalMin) return;
+			if (intValue < logicalMin) intValue = logicalMin;
+			if (intValue > logicalMax) intValue = logicalMax;
+
+			i8 axisValue = (i8)(-100.0 + ((intValue - logicalMin) * 200.0) / (logicalMax - logicalMin));
+
+			u8 whichAxis = 0;
+			switch (usage) {
+				case kHIDUsage_GD_X: RGFW_gamepadAxes[index][0].x = axisValue; whichAxis = 0; break;
+				case kHIDUsage_GD_Y: RGFW_gamepadAxes[index][0].y = axisValue; whichAxis = 0; break;
+				case kHIDUsage_GD_Z: RGFW_gamepadAxes[index][1].x = axisValue; whichAxis = 1; break;
+				case kHIDUsage_GD_Rz: RGFW_gamepadAxes[index][1].y = axisValue; whichAxis = 1; break;
+				default: return;
+			}
+
+            RGFW_event e;
+            e.type = RGFW_gamepadAxisMove;
+            e.gamepad = (u16)index;
+            e.whichAxis = whichAxis;
+            e._win = _RGFW.root;
+            for (size_t i = 0; i < 4; i++)
+                e.axis[i] = RGFW_gamepadAxes[index][i]; 
+
+			RGFW_eventQueuePush(e);
+
+			RGFW_gamepadAxisCallback(_RGFW.root, (u16)index, RGFW_gamepadAxes[index], 2, whichAxis);
+		}
+	}
+}
+
+void RGFW__osxDeviceAddedCallback(void* context, IOReturn result, void *sender, IOHIDDeviceRef device) {
+	RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender);
+	CFTypeRef usageRef = (CFTypeRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
+	int usage = 0;
+	if (usageRef)
+		CFNumberGetValue((CFNumberRef)usageRef, kCFNumberIntType, (void*)&usage);
+
+	if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
+		return;
+	}
+
+    size_t i;
+    for (i = 0; i < 4; i++) {
+		if (RGFW_osxControllers[i] != NULL)
+			continue;
+
+		RGFW_osxControllers[i] = device;
+
+		IOHIDDeviceRegisterInputValueCallback(device, RGFW__osxInputValueChangedCallback, NULL);
+
+		CFStringRef deviceName = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey));
+		if (deviceName)
+			CFStringGetCString(deviceName, RGFW_gamepads_name[i], sizeof(RGFW_gamepads_name[i]), kCFStringEncodingUTF8);
+
+		RGFW_gamepads_type[i] = RGFW_gamepadUnknown;
+		if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box") || RGFW_STRSTR(RGFW_gamepads_name[i], "Xbox"))
+			RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5"))
+			RGFW_gamepads_type[i] = RGFW_gamepadSony;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo"))
+			RGFW_gamepads_type[i] = RGFW_gamepadNintendo;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech"))
+			RGFW_gamepads_type[i] = RGFW_gamepadLogitech;
+
+		RGFW_gamepads[i] = (u16)i;
+		RGFW_gamepadCount++;
+
+		RGFW_eventQueuePushEx(e.type = RGFW_gamepadConnected;
+										e.gamepad = (u16)i;
+										e._win = _RGFW.root);
+
+		RGFW_gamepadCallback(_RGFW.root, (u16)i, 1);
+		break;
+	}
+}
+
+void RGFW__osxDeviceRemovedCallback(void *context, IOReturn result, void *sender, IOHIDDeviceRef device) {
+	RGFW_UNUSED(context); RGFW_UNUSED(result); RGFW_UNUSED(sender); RGFW_UNUSED(device);
+	CFNumberRef usageRef = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDPrimaryUsageKey));
+	int usage = 0;
+	if (usageRef)
+		CFNumberGetValue(usageRef, kCFNumberIntType, &usage);
+
+	if (usage != kHIDUsage_GD_Joystick && usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_MultiAxisController) {
+		return;
+	}
+
+	size_t index = findControllerIndex(device);
+	if (index != (size_t)-1)
+		RGFW_osxControllers[index] = NULL;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_gamepadDisconnected;
+									e.gamepad = (u16)index;
+									e._win = _RGFW.root);
+	RGFW_gamepadCallback(_RGFW.root, (u16)index, 0);
+
+	RGFW_gamepadCount--;
+}
+
+RGFWDEF void RGFW_osxInitIOKit(void);
+void RGFW_osxInitIOKit(void) {
+	IOHIDManagerRef hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
+	if (!hidManager) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create IOHIDManager.");
+		return;
+	}
+
+	CFMutableDictionaryRef matchingDictionary = CFDictionaryCreateMutable(
+		kCFAllocatorDefault,
+		0,
+		&kCFTypeDictionaryKeyCallBacks,
+		&kCFTypeDictionaryValueCallBacks
+	);
+	if (!matchingDictionary) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errIOKit, RGFW_DEBUG_CTX(_RGFW.root, 0), "Failed to create matching dictionary for IOKit.");
+		CFRelease(hidManager);
+		return;
+	}
+
+	CFDictionarySetValue(
+		matchingDictionary,
+		CFSTR(kIOHIDDeviceUsagePageKey),
+		CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, (int[]){kHIDPage_GenericDesktop})
+	);
+
+	IOHIDManagerSetDeviceMatching(hidManager, matchingDictionary);
+
+	IOHIDManagerRegisterDeviceMatchingCallback(hidManager, RGFW__osxDeviceAddedCallback, NULL);
+	IOHIDManagerRegisterDeviceRemovalCallback(hidManager, RGFW__osxDeviceRemovedCallback, NULL);
+
+	IOHIDManagerScheduleWithRunLoop(hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+
+	IOHIDManagerOpen(hidManager, kIOHIDOptionsTypeNone);
+
+	/* Execute the run loop once in order to register any initially-attached joysticks */
+	CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
+}
+#endif
+
+void RGFW_moveToMacOSResourceDir(void) {
+	char resourcesPath[256];
+
+	CFBundleRef bundle = CFBundleGetMainBundle();
+	if (!bundle)
+		return;
+
+	CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
+	CFStringRef last = CFURLCopyLastPathComponent(resourcesURL);
+
+	if (
+		CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo ||
+		CFURLGetFileSystemRepresentation(resourcesURL, true, (u8*) resourcesPath, 255) == 0
+		) {
+		CFRelease(last);
+		CFRelease(resourcesURL);
+		return;
+	}
+
+	CFRelease(last);
+	CFRelease(resourcesURL);
+
+	chdir(resourcesPath);
+}
+
+
+void RGFW__osxWindowDeminiaturize(id self, SEL sel) {
+	RGFW_UNUSED(sel);
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+	win->_flags |= RGFW_windowMinimize;
+	RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
+	RGFW_windowRestoredCallback(win, win->r);
+
+}
+void RGFW__osxWindowMiniaturize(id self, SEL sel) {
+	RGFW_UNUSED(sel);
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+	win->_flags &= ~(u32)RGFW_windowMinimize;
+	RGFW_eventQueuePushEx(e.type = RGFW_windowMinimized; e._win = win);
+	RGFW_windowMinimizedCallback(win, win->r);
+
+}
+
+void RGFW__osxWindowBecameKey(id self, SEL sel) {
+	RGFW_UNUSED(sel);
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+	win->_flags |= RGFW_windowFocus;
+	RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = win);
+
+	RGFW_focusCallback(win, RGFW_TRUE);
+
+	if ((win->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(win, RGFW_AREA(win->r.w, win->r.h));
+}
+
+void RGFW__osxWindowResignKey(id self, SEL sel) {
+	RGFW_UNUSED(sel);
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+    RGFW_window_focusLost(win);
+    RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = win);
+	RGFW_focusCallback(win, RGFW_FALSE);
+}
+
+NSSize RGFW__osxWindowResize(id self, SEL sel, NSSize frameSize) {
+	RGFW_UNUSED(sel);
+
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return frameSize;
+
+	win->r.w = (i32)frameSize.width;
+	win->r.h = (i32)frameSize.height;
+
+	RGFW_monitor mon = RGFW_window_getMonitor(win);
+	if ((i32)mon.mode.area.w == win->r.w && (i32)mon.mode.area.h - 102 <= win->r.h) {
+		win->_flags |= RGFW_windowMaximize;
+		RGFW_eventQueuePushEx(e.type = RGFW_windowMaximized; e._win = win);
+		RGFW_windowMaximizedCallback(win, win->r);
+	} else if (win->_flags & RGFW_windowMaximize) {
+		win->_flags &= ~(u32)RGFW_windowMaximize;
+		RGFW_eventQueuePushEx(e.type = RGFW_windowRestored; e._win = win);
+		RGFW_windowRestoredCallback(win, win->r);
+
+	}
+
+
+	RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = win);
+	RGFW_windowResizedCallback(win, win->r);
+	return frameSize;
+}
+
+void RGFW__osxWindowMove(id self, SEL sel) {
+	RGFW_UNUSED(sel);
+
+	RGFW_window* win = NULL;
+	object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+	NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+	win->r.x = (i32) frame.origin.x;
+	win->r.y = (i32) frame.origin.y;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_windowMoved; e._win = win);
+	RGFW_windowMovedCallback(win, win->r);
+}
+
+void RGFW__osxViewDidChangeBackingProperties(id self, SEL _cmd) {
+	RGFW_UNUSED(_cmd);
+        RGFW_window* win = NULL;
+        object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+	RGFW_monitor mon = RGFW_window_getMonitor(win);
+	RGFW_scaleUpdatedCallback(win, mon.scaleX, mon.scaleY);
+	RGFW_eventQueuePushEx(e.type = RGFW_scaleUpdated; e.scaleX = mon.scaleX; e.scaleY = mon.scaleY ; e._win = win);
+}
+
+void RGFW__osxDrawRect(id self, SEL _cmd, CGRect rect) {
+	RGFW_UNUSED(rect); RGFW_UNUSED(_cmd);
+        RGFW_window* win = NULL;
+        object_getInstanceVariable(self, "RGFW_window", (void**)&win);
+	if (win == NULL) return;
+
+        RGFW_eventQueuePushEx(e.type = RGFW_windowRefresh; e._win = win);
+        RGFW_windowRefreshCallback(win);
+}
+
+void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area) {
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		win->buffer = buffer;
+		win->bufferSize = area;
+		win->_flags |= RGFW_BUFFER_ALLOC;
+	#ifdef RGFW_OSMESA
+		win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+		OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
+		OSMesaPixelStore(OSMESA_Y_UP, 0);
+	#endif
+	#else
+		RGFW_UNUSED(win);  RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
+	#endif
+}
+
+void RGFW_window_cocoaSetLayer(RGFW_window* win, void* layer) {
+	objc_msgSend_void_id((id)win->src.view, sel_registerName("setLayer"), (id)layer);
+}
+
+void* RGFW_cocoaGetLayer(void) {
+	return objc_msgSend_class((id)objc_getClass("CAMetalLayer"), (SEL)sel_registerName("layer"));
+}
+
+NSPasteboardType const NSPasteboardTypeURL = "public.url";
+NSPasteboardType const NSPasteboardTypeFileURL  = "public.file-url";
+
+id RGFW__osx_generateViewClass(const char* subclass, RGFW_window* win) {
+	Class customViewClass;
+	customViewClass = objc_allocateClassPair(objc_getClass(subclass), "RGFWCustomView", 0);
+
+	class_addIvar( customViewClass, "RGFW_window", sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))), "L");
+	class_addMethod(customViewClass, sel_registerName("drawRect:"), (IMP)RGFW__osxDrawRect, "v@:{CGRect=ffff}");
+	class_addMethod(customViewClass, sel_registerName("viewDidChangeBackingProperties"), (IMP)RGFW__osxViewDidChangeBackingProperties, "");
+
+	id customView  = objc_msgSend_id(NSAlloc(customViewClass), sel_registerName("init"));
+	object_setInstanceVariable(customView, "RGFW_window", win);
+
+	return customView;
+}
+
+#ifndef RGFW_EGL
+void RGFW_window_initOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+	void* attrs = RGFW_initFormatAttribs();
+	void* format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)attrs);
+
+	if (format == NULL) {
+		RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "Failed to load pixel format for OpenGL");
+        win->_flags |= RGFW_windowOpenglSoftware;
+        void* subAttrs = RGFW_initFormatAttribs();
+		format = NSOpenGLPixelFormat_initWithAttributes((uint32_t*)subAttrs);
+
+		if (format == NULL)
+			RGFW_sendDebugInfo(RGFW_typeError, RGFW_errOpenglContext, RGFW_DEBUG_CTX(win, 0), "and loading software rendering OpenGL failed");
+		else
+			RGFW_sendDebugInfo(RGFW_typeWarning, RGFW_warningOpenGL, RGFW_DEBUG_CTX(win, 0), "Switching to software rendering");
+	}
+
+	/* the pixel format can be passed directly to opengl context creation to create a context
+		this is because the format also includes information about the opengl version (which may be a bad thing) */
+
+	win->src.view = (id) ((id(*)(id, SEL, NSRect, uint32_t*))objc_msgSend) (RGFW__osx_generateViewClass("NSOpenGLView", win),
+							sel_registerName("initWithFrame:pixelFormat:"), (NSRect){{0, 0}, {win->r.w, win->r.h}}, (uint32_t*)format);
+
+	objc_msgSend_void(win->src.view, sel_registerName("prepareOpenGL"));
+	win->src.ctx = objc_msgSend_id(win->src.view, sel_registerName("openGLContext"));
+
+	if (win->_flags & RGFW_windowTransparent) {
+		i32 opacity = 0;
+		#define NSOpenGLCPSurfaceOpacity 236
+		NSOpenGLContext_setValues((id)win->src.ctx, &opacity, NSOpenGLCPSurfaceOpacity);
+	}
+
+	objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_window_freeOpenGL(RGFW_window* win) {
+#ifdef RGFW_OPENGL
+	if (win->src.ctx == NULL) return;
+	objc_msgSend_void(win->src.ctx, sel_registerName("release"));
+	win->src.ctx = NULL;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+#endif
+
+
+i32 RGFW_init(void) {
+#if defined(RGFW_C89) || defined(__cplusplus)
+    if (_RGFW_init) return 0; 
+    _RGFW_init = RGFW_TRUE;
+    _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -1; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
+#endif
+
+    /* NOTE(EimaMei): Why does Apple hate good code? Like wtf, who thought of methods being a great idea???
+	Imagine a universe, where MacOS had a proper system API (we would probably have like 20% better performance).
+	*/
+	si_func_to_SEL_with_name("NSObject", "windowShouldClose", (void*)RGFW_OnClose);
+
+	/* NOTE(EimaMei): Fixes the 'Boop' sfx from constantly playing each time you click a key. Only a problem when running in the terminal. */
+	si_func_to_SEL("NSWindow", acceptsFirstResponder);
+	si_func_to_SEL("NSWindow", performKeyEquivalent);
+
+    if (NSApp == NULL) {
+		NSApp = objc_msgSend_id((id)objc_getClass("NSApplication"), sel_registerName("sharedApplication"));
+
+		((void (*)(id, SEL, NSUInteger))objc_msgSend)
+			(NSApp, sel_registerName("setActivationPolicy:"), NSApplicationActivationPolicyRegular);
+
+		#ifndef RGFW_NO_IOKIT
+			RGFW_osxInitIOKit();
+		#endif
+	}
+
+    _RGFW.windowCount = 0;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized");
+    return 0;
+}
+
+RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
+	static u8 RGFW_loaded = 0;
+	RGFW_window_basic_init(win, rect, flags);
+
+    /* RR Create an autorelease pool */
+	id pool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+	pool = objc_msgSend_id(pool, sel_registerName("init"));
+
+	RGFW_window_setMouseDefault(win);
+
+	NSRect windowRect;
+	windowRect.origin.x = win->r.x;
+	windowRect.origin.y = win->r.y;
+	windowRect.size.width = win->r.w;
+	windowRect.size.height = win->r.h;
+
+	NSBackingStoreType macArgs = NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSBackingStoreBuffered | NSWindowStyleMaskTitled;
+
+	if (!(flags & RGFW_windowNoResize))
+		macArgs |= NSWindowStyleMaskResizable;
+	if (!(flags & RGFW_windowNoBorder))
+		macArgs |= NSWindowStyleMaskTitled;
+	{
+		void* nsclass = objc_getClass("NSWindow");
+		SEL func = sel_registerName("initWithContentRect:styleMask:backing:defer:");
+
+		win->src.window = ((id(*)(id, SEL, NSRect, NSWindowStyleMask, NSBackingStoreType, bool))objc_msgSend)
+			(NSAlloc(nsclass), func, windowRect, macArgs, macArgs, false);
+	}
+
+	id str = NSString_stringWithUTF8String(name);
+	objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+
+	if ((flags & RGFW_windowNoInitAPI) == 0) {
+		RGFW_window_initOpenGL(win);
+        RGFW_window_initBuffer(win);
+    }
+
+	#ifdef RGFW_OPENGL
+	else
+	#endif
+	{
+		NSRect contentRect = (NSRect){{0, 0}, {win->r.w, win->r.h}};
+		win->src.view = ((id(*)(id, SEL, NSRect))objc_msgSend) (NSAlloc(objc_getClass("NSView")), sel_registerName("initWithFrame:"), contentRect);
+    	}
+
+	void* contentView = NSWindow_contentView((id)win->src.window);
+	objc_msgSend_void_bool(contentView, sel_registerName("setWantsLayer:"), true);
+	objc_msgSend_int((id)win->src.view, sel_registerName("setLayerContentsPlacement:"),  4);
+	objc_msgSend_void_id((id)win->src.window, sel_registerName("setContentView:"), win->src.view);
+
+	if (flags & RGFW_windowTransparent) {
+		objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), false);
+
+		objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"),
+			NSColor_colorWithSRGB(0, 0, 0, 0));
+	}
+
+	Class delegateClass = objc_allocateClassPair(objc_getClass("NSObject"), "WindowDelegate", 0);
+
+	class_addIvar(
+		delegateClass, "RGFW_window",
+		sizeof(RGFW_window*), (u8)rint(log2(sizeof(RGFW_window*))),
+		"L"
+	);
+
+	class_addMethod(delegateClass, sel_registerName("windowWillResize:toSize:"), (IMP) RGFW__osxWindowResize, "{NSSize=ff}@:{NSSize=ff}");
+	class_addMethod(delegateClass, sel_registerName("windowWillMove:"), (IMP) RGFW__osxWindowMove, "");
+	class_addMethod(delegateClass, sel_registerName("windowDidMove:"), (IMP) RGFW__osxWindowMove, "");
+	class_addMethod(delegateClass, sel_registerName("windowDidMiniaturize:"), (IMP) RGFW__osxWindowMiniaturize, "");
+	class_addMethod(delegateClass, sel_registerName("windowDidDeminiaturize:"), (IMP) RGFW__osxWindowDeminiaturize, "");
+	class_addMethod(delegateClass, sel_registerName("windowDidBecomeKey:"), (IMP) RGFW__osxWindowBecameKey, "");
+	class_addMethod(delegateClass, sel_registerName("windowDidResignKey:"), (IMP) RGFW__osxWindowResignKey, "");
+	class_addMethod(delegateClass, sel_registerName("draggingEntered:"), (IMP)draggingEntered, "l@:@");
+	class_addMethod(delegateClass, sel_registerName("draggingUpdated:"), (IMP)draggingUpdated, "l@:@");
+	class_addMethod(delegateClass, sel_registerName("draggingExited:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+	class_addMethod(delegateClass, sel_registerName("draggingEnded:"), (IMP)RGFW__osxDraggingEnded, "v@:@");
+	class_addMethod(delegateClass, sel_registerName("prepareForDragOperation:"), (IMP)prepareForDragOperation, "B@:@");
+	class_addMethod(delegateClass, sel_registerName("performDragOperation:"), (IMP)performDragOperation, "B@:@");
+
+	id delegate = objc_msgSend_id(NSAlloc(delegateClass), sel_registerName("init"));
+
+	if (RGFW_COCOA_FRAME_NAME)
+		objc_msgSend_ptr(win->src.view, sel_registerName("setFrameAutosaveName:"), RGFW_COCOA_FRAME_NAME);
+
+	object_setInstanceVariable(delegate, "RGFW_window", win);
+
+	objc_msgSend_void_id((id)win->src.window, sel_registerName("setDelegate:"), delegate);
+
+	if (flags & RGFW_windowAllowDND) {
+		win->_flags |= RGFW_windowAllowDND;
+
+		NSPasteboardType types[] = {NSPasteboardTypeURL, NSPasteboardTypeFileURL, NSPasteboardTypeString};
+		NSregisterForDraggedTypes((id)win->src.window, types, 3);
+	}
+
+	RGFW_window_setFlags(win, flags);
+
+    /* Show the window */
+	objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+	((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+	RGFW_window_show(win);
+
+	if (!RGFW_loaded) {
+		objc_msgSend_void(win->src.window, sel_registerName("makeMainWindow"));
+
+		RGFW_loaded = 1;
+	}
+
+	objc_msgSend_void(win->src.window, sel_registerName("makeKeyWindow"));
+
+	objc_msgSend_void(NSApp, sel_registerName("finishLaunching"));
+	NSRetain(win->src.window);
+	NSRetain(NSApp);
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new  window was created");
+	return win;
+}
+
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) {
+	NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+	NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+	float offset = 0;
+
+	RGFW_setBit(&win->_flags, RGFW_windowNoBorder, !border);
+	NSBackingStoreType storeType = NSWindowStyleMaskBorderless | NSWindowStyleMaskFullSizeContentView;
+	if (border)
+		storeType = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable;
+	if (!(win->_flags & RGFW_windowNoResize)) {
+		storeType |= NSWindowStyleMaskResizable;
+	}
+
+	((void (*)(id, SEL, NSBackingStoreType))objc_msgSend)((id)win->src.window, sel_registerName("setStyleMask:"), storeType);
+
+	if (!border) {
+		id miniaturizeButton = objc_msgSend_int((id)win->src.window, sel_registerName("standardWindowButton:"),  NSWindowMiniaturizeButton);
+		id titleBarView = objc_msgSend_id(miniaturizeButton, sel_registerName("superview"));
+		objc_msgSend_void_bool(titleBarView, sel_registerName("setHidden:"), true);
+
+		offset = (float)(frame.size.height - content.size.height);
+	}
+
+	RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h + offset));
+	win->r.h -= (i32)offset;
+}
+
+RGFW_area RGFW_getScreenSize(void) {
+	static CGDirectDisplayID display = 0;
+
+	if (display == 0)
+		display = CGMainDisplayID();
+
+	return RGFW_AREA(CGDisplayPixelsWide(display), CGDisplayPixelsHigh(display));
+}
+
+RGFW_point RGFW_getGlobalMousePoint(void) {
+	RGFW_ASSERT(_RGFW.root != NULL);
+
+	CGEventRef e = CGEventCreate(NULL);
+	CGPoint point = CGEventGetLocation(e);
+	CFRelease(e);
+
+	return RGFW_POINT((u32) point.x, (u32) point.y); /*!< the point is loaded during event checks */
+}
+
+typedef RGFW_ENUM(u32, NSEventType) {        /* various types of events */
+	NSEventTypeLeftMouseDown = 1,
+		NSEventTypeLeftMouseUp = 2,
+		NSEventTypeRightMouseDown = 3,
+		NSEventTypeRightMouseUp = 4,
+		NSEventTypeMouseMoved = 5,
+		NSEventTypeLeftMouseDragged = 6,
+		NSEventTypeRightMouseDragged = 7,
+		NSEventTypeMouseEntered = 8,
+		NSEventTypeMouseExited = 9,
+		NSEventTypeKeyDown = 10,
+		NSEventTypeKeyUp = 11,
+		NSEventTypeFlagsChanged = 12,
+		NSEventTypeAppKitDefined = 13,
+		NSEventTypeSystemDefined = 14,
+		NSEventTypeApplicationDefined = 15,
+		NSEventTypePeriodic = 16,
+		NSEventTypeCursorUpdate = 17,
+		NSEventTypeScrollWheel = 22,
+		NSEventTypeTabletPoint = 23,
+		NSEventTypeTabletProximity = 24,
+		NSEventTypeOtherMouseDown = 25,
+		NSEventTypeOtherMouseUp = 26,
+		NSEventTypeOtherMouseDragged = 27,
+		/* The following event types are available on some hardware on 10.5.2 and later */
+		NSEventTypeGesture  = 29,
+		NSEventTypeMagnify  = 30,
+		NSEventTypeSwipe    = 31,
+		NSEventTypeRotate   = 18,
+		NSEventTypeBeginGesture  = 19,
+		NSEventTypeEndGesture  = 20,
+
+		NSEventTypeSmartMagnify  = 32,
+		NSEventTypeQuickLook  = 33,
+
+		NSEventTypePressure  = 34,
+		NSEventTypeDirectTouch  = 37,
+
+		NSEventTypeChangeMode  = 38,
+};
+
+typedef unsigned long long NSEventMask;
+
+typedef enum NSEventModifierFlags {
+	NSEventModifierFlagCapsLock = 1 << 16,
+	NSEventModifierFlagShift = 1 << 17,
+	NSEventModifierFlagControl = 1 << 18,
+	NSEventModifierFlagOption = 1 << 19,
+	NSEventModifierFlagCommand = 1 << 20,
+	NSEventModifierFlagNumericPad = 1 << 21
+} NSEventModifierFlags;
+
+void RGFW_stopCheckEvents(void) {
+	id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+	eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+	id e = (id) ((id(*)(Class, SEL, NSEventType, NSPoint, NSEventModifierFlags, void*, NSInteger, void**, short, NSInteger, NSInteger))objc_msgSend)
+		(objc_getClass("NSEvent"), sel_registerName("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
+			NSEventTypeApplicationDefined, (NSPoint){0, 0}, (NSEventModifierFlags)0, NULL, (NSInteger)0, NULL, 0, 0, 0);
+
+	((void (*)(id, SEL, id, bool))objc_msgSend)
+		(NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+
+	objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+	RGFW_UNUSED(win);
+
+	id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+	eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+	void* date = (void*) ((id(*)(Class, SEL, double))objc_msgSend)
+				(objc_getClass("NSDate"), sel_registerName("dateWithTimeIntervalSinceNow:"), waitMS);
+
+	SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+	id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+		(NSApp, eventFunc,
+			ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+	if (e) {
+		((void (*)(id, SEL, id, bool))objc_msgSend)
+			(NSApp, sel_registerName("postEvent:atStart:"), e, 1);
+	}
+
+	objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+}
+
+u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
+    return (u8)rgfw_keycode; /* TODO */
+}
+
+RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
+    if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
+
+    objc_msgSend_void((id)win->src.mouse, sel_registerName("set"));
+    RGFW_event* ev =  RGFW_window_checkEventCore(win);
+	if (ev) {
+		((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+		return ev;
+	}
+
+	id eventPool = objc_msgSend_class(objc_getClass("NSAutoreleasePool"), sel_registerName("alloc"));
+	eventPool = objc_msgSend_id(eventPool, sel_registerName("init"));
+
+	SEL eventFunc = sel_registerName("nextEventMatchingMask:untilDate:inMode:dequeue:");
+
+	void* date = NULL;
+
+	id e = (id) ((id(*)(id, SEL, NSEventMask, void*, id, bool))objc_msgSend)
+		(NSApp, eventFunc, ULONG_MAX, date, NSString_stringWithUTF8String("kCFRunLoopDefaultMode"), true);
+
+	if (e == NULL) {
+		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+		objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+		((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+		return NULL;
+	}
+
+	if (objc_msgSend_id(e, sel_registerName("window")) != win->src.window) {
+		((void (*)(id, SEL, id, bool))objc_msgSend)
+			(NSApp, sel_registerName("postEvent:atStart:"), e, 0);
+
+		objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+		objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+		((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+		return NULL;
+	}
+
+	if (win->event.droppedFilesCount) {
+		u32 i;
+		for (i = 0; i < win->event.droppedFilesCount; i++)
+			win->event.droppedFiles[i][0] = '\0';
+	}
+
+	win->event.droppedFilesCount = 0;
+	win->event.type = 0;
+
+	u32 type = (u32)objc_msgSend_uint(e, sel_registerName("type"));
+	switch (type) {
+		case NSEventTypeMouseEntered: {
+			win->event.type = RGFW_mouseEnter;
+			NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
+
+			win->event.point = RGFW_POINT((i32) p.x, (i32) (win->r.h - p.y));
+			RGFW_mouseNotifyCallback(win, win->event.point, 1);
+			break;
+		}
+
+		case NSEventTypeMouseExited:
+			win->event.type = RGFW_mouseLeave;
+			RGFW_mouseNotifyCallback(win, win->event.point, 0);
+			break;
+
+		case NSEventTypeKeyDown: {
+			u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
+
+			u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers")))));
+			if (((u8)mappedKey) == 239)
+				mappedKey = 0;
+
+			win->event.keyChar = (u8)mappedKey;
+
+			win->event.key = (u8)RGFW_apiKeyToRGFW(key);
+			RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
+
+			win->event.type = RGFW_keyPressed;
+			win->event.repeat = RGFW_isPressed(win, win->event.key);
+			RGFW_keyboard[win->event.key].current = 1;
+
+			RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 1);
+			break;
+		}
+
+		case NSEventTypeKeyUp: {
+			u32 key = (u16) objc_msgSend_uint(e, sel_registerName("keyCode"));
+			u32 mappedKey = (u32)*(((char*)(const char*) NSString_to_char(objc_msgSend_id(e, sel_registerName("charactersIgnoringModifiers")))));
+			if (((u8)mappedKey) == 239)
+				mappedKey = 0;
+
+			win->event.keyChar = (u8)mappedKey;
+
+			win->event.key = (u8)RGFW_apiKeyToRGFW(key);
+
+			RGFW_keyboard[win->event.key].prev = RGFW_keyboard[win->event.key].current;
+
+			win->event.type = RGFW_keyReleased;
+
+			RGFW_keyboard[win->event.key].current = 0;
+			RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, 0);
+			break;
+		}
+
+		case NSEventTypeFlagsChanged: {
+			u32 flags = (u32)objc_msgSend_uint(e, sel_registerName("modifierFlags"));
+			RGFW_updateKeyModsPro(win, ((u32)(flags & NSEventModifierFlagCapsLock) % 255), ((flags & NSEventModifierFlagNumericPad) % 255),
+										((flags & NSEventModifierFlagControl) % 255), ((flags & NSEventModifierFlagOption) % 255),
+										((flags & NSEventModifierFlagShift) % 255), ((flags & NSEventModifierFlagCommand) % 255), 0);
+			u8 i;
+			for (i = 0; i < 9; i++)
+				RGFW_keyboard[i + RGFW_capsLock].prev = 0;
+
+			for (i = 0; i < 5; i++) {
+				u32 shift = (1 << (i + 16));
+				u32 key = i + RGFW_capsLock;
+
+				if ((flags & shift) && !RGFW_wasPressed(win, (u8)key)) {
+					RGFW_keyboard[key].current = 1;
+
+					if (key != RGFW_capsLock)
+						RGFW_keyboard[key+ 4].current = 1;
+
+					win->event.type = RGFW_keyPressed;
+					win->event.key = (u8)key;
+					break;
+				}
+
+				if (!(flags & shift) && RGFW_wasPressed(win, (u8)key)) {
+					RGFW_keyboard[key].current = 0;
+
+					if (key != RGFW_capsLock)
+						RGFW_keyboard[key + 4].current = 0;
+
+					win->event.type = RGFW_keyReleased;
+					win->event.key = (u8)key;
+					break;
+				}
+			}
+
+			RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, win->event.type == RGFW_keyPressed);
+
+			break;
+		}
+		case NSEventTypeLeftMouseDragged:
+		case NSEventTypeOtherMouseDragged:
+		case NSEventTypeRightMouseDragged:
+		case NSEventTypeMouseMoved: {
+			win->event.type = RGFW_mousePosChanged;
+			NSPoint p = ((NSPoint(*)(id, SEL)) objc_msgSend)(e, sel_registerName("locationInWindow"));
+			win->event.point = RGFW_POINT((u32) p.x, (u32) (win->r.h - p.y));
+
+			p.x = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaX"));
+			p.y = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
+			win->event.vector = RGFW_POINT((i32)p.x, (i32)p.y);
+
+			win->_lastMousePoint = win->event.point;
+			RGFW_mousePosCallback(win, win->event.point, win->event.vector);
+			break;
+		}
+		case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: case NSEventTypeOtherMouseDown: {
+			u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber"));
+			switch (buttonNumber) {
+				case 0: win->event.button = RGFW_mouseLeft; break;
+				case 1: win->event.button = RGFW_mouseRight; break;
+				case 2: win->event.button = RGFW_mouseMiddle; break;
+				default: win->event.button = (u8)buttonNumber;
+			}
+
+			win->event.type = RGFW_mouseButtonPressed;
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 1;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+			break;
+		}
+		case NSEventTypeLeftMouseUp: case NSEventTypeRightMouseUp: case NSEventTypeOtherMouseUp: {
+			u32 buttonNumber = (u32)objc_msgSend_uint(e, sel_registerName("buttonNumber"));
+			switch (buttonNumber) {
+				case 0: win->event.button = RGFW_mouseLeft; break;
+				case 1: win->event.button = RGFW_mouseRight; break;
+				case 2: win->event.button = RGFW_mouseMiddle; break;
+				default: win->event.button = (u8)buttonNumber;
+			}
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 0;
+			win->event.type = RGFW_mouseButtonReleased;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 0);
+			break;
+		}
+		case NSEventTypeScrollWheel: {
+			double deltaY = ((CGFloat(*)(id, SEL))abi_objc_msgSend_fpret)(e, sel_registerName("deltaY"));
+
+			if (deltaY > 0) {
+				win->event.button = RGFW_mouseScrollUp;
+			}
+			else if (deltaY < 0) {
+				win->event.button = RGFW_mouseScrollDown;
+			}
+
+			RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current;
+			RGFW_mouseButtons[win->event.button].current = 1;
+
+			win->event.scroll = deltaY;
+
+			win->event.type = RGFW_mouseButtonPressed;
+			RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, 1);
+			break;
+		}
+
+		default:
+			objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+			((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+			return RGFW_window_checkEvent(win);
+	}
+
+	objc_msgSend_void_id(NSApp, sel_registerName("sendEvent:"), e);
+	((void(*)(id, SEL))objc_msgSend)(NSApp, sel_registerName("updateWindows"));
+	objc_msgSend_bool_void(eventPool, sel_registerName("drain"));
+	return &win->event;
+}
+
+
+void RGFW_window_move(RGFW_window* win, RGFW_point v) {
+	RGFW_ASSERT(win != NULL);
+
+	win->r.x = v.x;
+	win->r.y = v.y;
+	((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+		((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h}}, true, true);
+}
+
+void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+	RGFW_ASSERT(win != NULL);
+
+	NSRect frame = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.window, sel_registerName("frame"));
+	NSRect content = ((NSRect(*)(id, SEL))abi_objc_msgSend_stret)((id)win->src.view, sel_registerName("frame"));
+	float offset = (float)(frame.size.height - content.size.height);
+
+	win->r.w = (i32)a.w;
+	win->r.h = (i32)a.h;
+
+	((void(*)(id, SEL, NSRect, bool, bool))objc_msgSend)
+		((id)win->src.window, sel_registerName("setFrame:display:animate:"), (NSRect){{win->r.x, win->r.y}, {win->r.w, win->r.h + offset}}, true, true);
+}
+
+void RGFW_window_focus(RGFW_window* win) {
+	RGFW_ASSERT(win);
+	objc_msgSend_void_bool(NSApp, sel_registerName("activateIgnoringOtherApps:"), true);
+	((void (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyWindow"));
+}
+
+void RGFW_window_raise(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), (SEL)NULL);
+    	objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+	RGFW_ASSERT(win != NULL);
+	if (fullscreen && (win->_flags & RGFW_windowFullscreen)) return;
+	if (!fullscreen && !(win->_flags & RGFW_windowFullscreen)) return;
+
+	if (fullscreen) {
+		win->_oldRect = win->r;
+		RGFW_monitor mon = RGFW_window_getMonitor(win);
+		win->r = RGFW_RECT(0, 0, mon.x, mon.y);
+		win->_flags |= RGFW_windowFullscreen;
+		RGFW_window_resize(win, RGFW_AREA(mon.mode.area.w, mon.mode.area.h));
+		RGFW_window_move(win, RGFW_POINT(0, 0));
+	}
+	objc_msgSend_void_SEL(win->src.window, sel_registerName("toggleFullScreen:"), NULL);
+
+	if (!fullscreen) {
+		win->r = win->_oldRect;
+		win->_flags &= ~(u32)RGFW_windowFullscreen;
+
+		RGFW_window_resize(win, RGFW_AREA(win->r.w, win->r.h));
+		RGFW_window_move(win, RGFW_POINT(win->r.x, win->r.y));
+	}
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	if (RGFW_window_isMaximized(win)) return;
+
+	win->_flags |= RGFW_windowMaximize;
+	objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+}
+
+void RGFW_window_minimize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	objc_msgSend_void_SEL(win->src.window, sel_registerName("performMiniaturize:"), NULL);
+}
+
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) {
+    RGFW_ASSERT(win != NULL);
+    if (floating) objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGFloatingWindowLevelKey);
+    else 		  objc_msgSend_void_id(win->src.window, sel_registerName("setLevel:"), kCGNormalWindowLevelKey);
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+	objc_msgSend_int(win->src.window, sel_registerName("setAlphaValue:"), opacity);
+	objc_msgSend_void_bool(win->src.window, sel_registerName("setOpaque:"), (opacity < (u8)255));
+
+	if (opacity)
+		objc_msgSend_void_id((id)win->src.window, sel_registerName("setBackgroundColor:"), NSColor_colorWithSRGB(0, 0, 0, opacity));
+
+}
+
+void RGFW_window_restore(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	if (RGFW_window_isMaximized(win))
+		objc_msgSend_void_SEL(win->src.window, sel_registerName("zoom:"), NULL);
+
+	objc_msgSend_void_SEL(win->src.window, sel_registerName("deminiaturize:"), NULL);
+	RGFW_window_show(win);
+}
+
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	int level = ((int (*)(id, SEL))objc_msgSend) ((id)(win->src.window), (SEL)sel_registerName("level"));
+	return level > kCGNormalWindowLevelKey;
+}
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+	RGFW_ASSERT(win != NULL);
+
+	id str = NSString_stringWithUTF8String(name);
+	objc_msgSend_void_id((id)win->src.window, sel_registerName("setTitle:"), str);
+}
+
+#ifndef RGFW_NO_PASSTHROUGH
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+	objc_msgSend_void_bool(win->src.window, sel_registerName("setIgnoresMouseEvents:"), passthrough);
+}
+#endif
+
+void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) {
+	if (a.w == 0 && a.h == 0) a = RGFW_AREA(1, 1);
+
+	((void (*)(id, SEL, NSSize))objc_msgSend)
+		((id)win->src.window, sel_registerName("setContentAspectRatio:"), (NSSize){a.w, a.h});
+}
+
+void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) {
+	((void (*)(id, SEL, NSSize))objc_msgSend)
+		((id)win->src.window, sel_registerName("setMinSize:"), (NSSize){a.w, a.h});
+}
+
+void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) {
+	if (a.w == 0 && a.h == 0) {
+		a = RGFW_getScreenSize();
+	}
+
+	((void (*)(id, SEL, NSSize))objc_msgSend)
+		((id)win->src.window, sel_registerName("setMaxSize:"), (NSSize){a.w, a.h});
+}
+
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* data, RGFW_area area, i32 channels, u8 type) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_UNUSED(type);
+
+	if (data == NULL) {
+		objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), NULL);
+		return RGFW_TRUE;
+	}
+
+	/* code by EimaMei: Make a bitmap representation, then copy the loaded image into it. */
+	id representation = NSBitmapImageRep_initWithBitmapData(NULL, area.w, area.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, area.w * (u32)channels, 8 * (u32)channels);
+	RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), data, area.w * area.h * (u32)channels);
+
+	/* Add ze representation. */
+	id dock_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){area.w, area.h}));
+
+	objc_msgSend_void_id(dock_image, sel_registerName("addRepresentation:"), representation);
+
+	/* Finally, set the dock image to it. */
+	objc_msgSend_void_id(NSApp, sel_registerName("setApplicationIconImage:"), dock_image);
+	/* Free the garbage. */
+	NSRelease(dock_image);
+	NSRelease(representation);
+
+	return RGFW_TRUE;
+}
+
+id NSCursor_arrowStr(const char* str) {
+	void* nclass = objc_getClass("NSCursor");
+	SEL func = sel_registerName(str);
+	return (id) objc_msgSend_id(nclass, func);
+}
+
+RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) {
+	if (icon == NULL) {
+		objc_msgSend_void(NSCursor_arrowStr("arrowCursor"), sel_registerName("set"));
+		return NULL;
+	}
+
+	/* NOTE(EimaMei): Code by yours truly. */
+	/* Make a bitmap representation, then copy the loaded image into it. */
+	id representation = (id)NSBitmapImageRep_initWithBitmapData(NULL, a.w, a.h, 8, channels, (channels == 4), false, "NSCalibratedRGBColorSpace", 1 << 1, a.w * (u32)channels, 8 * (u32)channels);
+	RGFW_MEMCPY(NSBitmapImageRep_bitmapData(representation), icon, a.w * a.h * (u32)channels);
+
+	/* Add ze representation. */
+	id cursor_image = ((id(*)(id, SEL, NSSize))objc_msgSend) (NSAlloc((id)objc_getClass("NSImage")), sel_registerName("initWithSize:"), ((NSSize){a.w, a.h}));
+
+	objc_msgSend_void_id(cursor_image, sel_registerName("addRepresentation:"), representation);
+
+	/* Finally, set the cursor image. */
+	id cursor = (id) ((id(*)(id, SEL, id, NSPoint))objc_msgSend)
+		(NSAlloc(objc_getClass("NSCursor")),  sel_registerName("initWithImage:hotSpot:"), cursor_image, (NSPoint){0.0, 0.0});
+
+	/* Free the garbage. */
+	NSRelease(cursor_image);
+	NSRelease(representation);
+
+	return (void*)cursor;
+}
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) {
+	RGFW_ASSERT(win != NULL); RGFW_ASSERT(mouse);
+	CGDisplayShowCursor(kCGDirectMainDisplay);
+	objc_msgSend_void((id)mouse, sel_registerName("set"));
+	win->src.mouse = mouse;
+}
+
+void RGFW_freeMouse(RGFW_mouse* mouse) {
+	RGFW_ASSERT(mouse);
+	NSRelease((id)mouse);
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+	return RGFW_window_setMouseStandard(win, RGFW_mouseArrow);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+	RGFW_window_showMouseFlags(win, show);
+	if (show)   CGDisplayShowCursor(kCGDirectMainDisplay);
+	else        CGDisplayHideCursor(kCGDirectMainDisplay);
+}
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 stdMouses) {
+	static const char* mouseIconSrc[16] = {"arrowCursor", "arrowCursor", "IBeamCursor", "crosshairCursor", "pointingHandCursor", "resizeLeftRightCursor", "resizeUpDownCursor", "_windowResizeNorthWestSouthEastCursor", "_windowResizeNorthEastSouthWestCursor", "closedHandCursor", "operationNotAllowedCursor"};
+	if (stdMouses > ((sizeof(mouseIconSrc)) / (sizeof(char*))))
+		return RGFW_FALSE;
+
+	const char* mouseStr = mouseIconSrc[stdMouses];
+	id mouse = NSCursor_arrowStr(mouseStr);
+
+	if (mouse == NULL)
+		return RGFW_FALSE;
+
+	RGFW_UNUSED(win);
+	CGDisplayShowCursor(kCGDirectMainDisplay);
+	objc_msgSend_void(mouse, sel_registerName("set"));
+	win->src.mouse = mouse;
+
+	return RGFW_TRUE;
+}
+
+void RGFW_releaseCursor(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	CGAssociateMouseAndMouseCursorPosition(1);
+}
+
+void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
+	RGFW_UNUSED(win);
+
+	CGWarpMouseCursorPosition((CGPoint){r.x + (r.w / 2), r.y + (r.h / 2)});
+	CGAssociateMouseAndMouseCursorPosition(0);
+}
+
+void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) {
+	RGFW_UNUSED(win);
+
+	win->_lastMousePoint = RGFW_POINT(v.x - win->r.x, v.y - win->r.y);
+	CGWarpMouseCursorPosition((CGPoint){v.x, v.y});
+}
+
+
+void RGFW_window_hide(RGFW_window* win) {
+	objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), false);
+}
+
+void RGFW_window_show(RGFW_window* win) {
+	if (win->_flags & RGFW_windowFocusOnShow)
+		((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("makeKeyAndOrderFront:"), NULL);
+
+	((id(*)(id, SEL, SEL))objc_msgSend)((id)win->src.window, sel_registerName("orderFront:"), NULL);
+	objc_msgSend_void_bool(win->src.window, sel_registerName("setIsVisible:"), true);
+}
+
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	bool visible = objc_msgSend_bool(win->src.window, sel_registerName("isVisible"));
+	return visible == NO && !RGFW_window_isMinimized(win);
+}
+
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	return objc_msgSend_bool(win->src.window, sel_registerName("isMiniaturized")) == YES;
+}
+
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	RGFW_bool b = (RGFW_bool)objc_msgSend_bool(win->src.window, sel_registerName("isZoomed"));
+	return b;
+}
+
+id RGFW_getNSScreenForDisplayID(CGDirectDisplayID display) {
+	Class NSScreenClass = objc_getClass("NSScreen");
+
+	id screens = objc_msgSend_id(NSScreenClass, sel_registerName("screens"));
+
+	NSUInteger count = (NSUInteger)objc_msgSend_uint(screens, sel_registerName("count"));
+    NSUInteger i;
+	for (i = 0; i < count; i++) {
+		id screen = ((id (*)(id, SEL, int))objc_msgSend) (screens, sel_registerName("objectAtIndex:"), (int)i);
+		id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+		id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+		id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+		if ((CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue")) == display) {
+			return screen;
+		}
+	}
+
+	return NULL;
+}
+
+u32 RGFW_osx_getFallbackRefreshRate(CGDirectDisplayID displayID);
+
+u32 RGFW_osx_getRefreshRate(CGDirectDisplayID display, CGDisplayModeRef mode) {
+	if (mode) {
+		u32 refreshRate = (u32)CGDisplayModeGetRefreshRate(mode);
+		if (refreshRate != 0)  return refreshRate;
+	}
+
+#ifndef RGFW_NO_IOKIT
+    u32 res = RGFW_osx_getFallbackRefreshRate(display);
+    if (res != 0) return res;
+#else
+    RGFW_UNUSED(display);
+#endif
+    return 60;
+}
+
+RGFW_monitor RGFW_NSCreateMonitor(CGDirectDisplayID display, id screen) {
+	RGFW_monitor monitor;
+
+	const char name[] = "MacOS\0";
+	RGFW_MEMCPY(monitor.name, name, 6);
+
+	CGRect bounds = CGDisplayBounds(display);
+	monitor.x = (i32)bounds.origin.x;
+	monitor.y = (i32)bounds.origin.y;
+	monitor.mode.area = RGFW_AREA((int) bounds.size.width, (int) bounds.size.height);
+
+	monitor.mode.red = 8; monitor.mode.green = 8; monitor.mode.blue = 8;
+
+	CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display);
+	monitor.mode.refreshRate = RGFW_osx_getRefreshRate(display, mode);
+	CFRelease(mode);
+
+	CGSize screenSizeMM = CGDisplayScreenSize(display);
+	monitor.physW = (float)screenSizeMM.width / 25.4f;
+	monitor.physH = (float)screenSizeMM.height / 25.4f;
+
+	float ppi_width = (monitor.mode.area.w/monitor.physW);
+	float ppi_height = (monitor.mode.area.h/monitor.physH);
+
+	monitor.pixelRatio = (float)((CGFloat (*)(id, SEL))abi_objc_msgSend_fpret) (screen, sel_registerName("backingScaleFactor"));
+	float dpi = 96.0f * monitor.pixelRatio;
+
+	monitor.scaleX = ((i32)(((float) (ppi_width) / dpi) * 10.0f)) / 10.0f;
+	monitor.scaleY = ((i32)(((float) (ppi_height) / dpi) * 10.0f)) / 10.0f;
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoMonitor, RGFW_DEBUG_CTX_MON(monitor), "monitor found");
+	return monitor;
+}
+
+
+RGFW_monitor* RGFW_getMonitors(size_t* len) {
+	static CGDirectDisplayID displays[7];
+	u32 count;
+
+	if (CGGetActiveDisplayList(6, displays, &count) != kCGErrorSuccess)
+		return NULL;
+
+	if (count > 6) count = 6;
+
+	static RGFW_monitor monitors[7];
+
+    u32 i;
+	for (i = 0; i < count; i++)
+		monitors[i] = RGFW_NSCreateMonitor(displays[i], RGFW_getNSScreenForDisplayID(displays[i]));
+
+	if (len != NULL) *len = count;
+	return monitors;
+}
+
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) {
+    CGPoint point = { mon.x, mon.y };
+
+    CGDirectDisplayID display;
+    uint32_t displayCount = 0;
+    CGError err = CGGetDisplaysWithPoint(point, 1, &display, &displayCount);
+    if (err != kCGErrorSuccess || displayCount != 1)
+		return RGFW_FALSE;
+
+    CFArrayRef allModes = CGDisplayCopyAllDisplayModes(display, NULL);
+
+    if (allModes == NULL)
+        return RGFW_FALSE;
+
+    CFIndex i;
+    for (i = 0; i < CFArrayGetCount(allModes); i++) {
+        CGDisplayModeRef cmode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i);
+
+		RGFW_monitorMode foundMode;
+		foundMode.area = RGFW_AREA(CGDisplayModeGetWidth(cmode), CGDisplayModeGetHeight(cmode));
+		foundMode.refreshRate =  RGFW_osx_getRefreshRate(display, cmode);
+		foundMode.red = 8; foundMode.green = 8; foundMode.blue = 8;
+
+		if (RGFW_monitorModeCompare(mode, foundMode, request)) {
+				if (CGDisplaySetDisplayMode(display, cmode, NULL) == kCGErrorSuccess) {
+					CFRelease(allModes);
+					return RGFW_TRUE;
+				}
+				break;
+        }
+    }
+
+    CFRelease(allModes);
+
+	return RGFW_FALSE;
+}
+
+RGFW_monitor RGFW_getPrimaryMonitor(void) {
+	CGDirectDisplayID primary = CGMainDisplayID();
+	return RGFW_NSCreateMonitor(primary, RGFW_getNSScreenForDisplayID(primary));
+}
+
+RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) {
+	id screen = objc_msgSend_id(win->src.window, sel_registerName("screen"));
+	id description = objc_msgSend_id(screen, sel_registerName("deviceDescription"));
+	id screenNumberKey = NSString_stringWithUTF8String("NSScreenNumber");
+	id screenNumber = objc_msgSend_id_id(description, sel_registerName("objectForKey:"), screenNumberKey);
+
+	CGDirectDisplayID display = (CGDirectDisplayID)objc_msgSend_uint(screenNumber, sel_registerName("unsignedIntValue"));
+
+	return RGFW_NSCreateMonitor(display, screen);
+}
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+	size_t clip_len;
+	char* clip = (char*)NSPasteboard_stringForType(NSPasteboard_generalPasteboard(), NSPasteboardTypeString, &clip_len);
+	if (clip == NULL) return -1;
+
+	if (str != NULL) {
+		if (strCapacity < clip_len)
+			return 0;
+
+		RGFW_MEMCPY(str, clip, clip_len);
+
+		str[clip_len] = '\0';
+	}
+
+	return (RGFW_ssize_t)clip_len;
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+	RGFW_UNUSED(textLen);
+
+	NSPasteboardType array[] = { NSPasteboardTypeString, NULL };
+	NSPasteBoard_declareTypes(NSPasteboard_generalPasteboard(), array, 1, NULL);
+
+	SEL func = sel_registerName("setString:forType:");
+	((bool (*)(id, SEL, id, id))objc_msgSend)
+		(NSPasteboard_generalPasteboard(), func, NSString_stringWithUTF8String(text), NSString_stringWithUTF8String(NSPasteboardTypeString));
+}
+
+	#ifdef RGFW_OPENGL
+	void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+		if (win != NULL)
+			objc_msgSend_void(win->src.ctx, sel_registerName("makeCurrentContext"));
+		else
+			objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("clearCurrentContext"));
+	}
+	void* RGFW_getCurrent_OpenGL(void) {
+		return objc_msgSend_id(objc_getClass("NSOpenGLContext"), sel_registerName("currentContext"));
+	}
+
+	void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+		objc_msgSend_void(win->src.ctx, sel_registerName("flushBuffer"));
+	}
+	#endif
+
+	#if !defined(RGFW_EGL)
+
+	void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) {
+		RGFW_ASSERT(win != NULL);
+		#if defined(RGFW_OPENGL)
+
+		NSOpenGLContext_setValues((id)win->src.ctx, &swapInterval, 222);
+		#else
+		RGFW_UNUSED(swapInterval);
+		#endif
+	}
+
+	#endif
+
+void RGFW_window_swapBuffers_software(RGFW_window* win) {
+#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	RGFW_RGB_to_BGR(win, win->buffer);
+	i32 channels = 4;
+	id image = ((id (*)(Class, SEL))objc_msgSend)(objc_getClass("NSImage"), sel_getUid("alloc"));
+	NSSize size = (NSSize){win->bufferSize.w, win->bufferSize.h};
+	image = ((id (*)(id, SEL, NSSize))objc_msgSend)((id)image, sel_getUid("initWithSize:"), size);
+
+	id rep  = NSBitmapImageRep_initWithBitmapData(&win->buffer, win->r.w, win->r.h , 8, channels, (channels == 4), false,
+							"NSDeviceRGBColorSpace", 1 << 1, (u32)win->bufferSize.w  * (u32)channels, 8 * (u32)channels);
+	((void (*)(id, SEL, id))objc_msgSend)((id)image, sel_getUid("addRepresentation:"), rep);
+
+	id contentView = ((id (*)(id, SEL))objc_msgSend)((id)win->src.window, sel_getUid("contentView"));
+	((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setWantsLayer:"), YES);
+	id layer = ((id (*)(id, SEL))objc_msgSend)(contentView, sel_getUid("layer"));
+
+	((void (*)(id, SEL, id))objc_msgSend)(layer, sel_getUid("setContents:"), (id)image);
+	((void (*)(id, SEL, BOOL))objc_msgSend)(contentView, sel_getUid("setNeedsDisplay:"), YES);
+
+	NSRelease(rep);
+	NSRelease(image);
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_deinit(void) {
+    _RGFW.windowCount = -1;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
+}
+
+void RGFW_window_close(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+	NSRelease(win->src.view);
+	if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
+
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		if ((win->_flags & RGFW_BUFFER_ALLOC))
+			RGFW_FREE(win->buffer);
+	#endif
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized");
+    _RGFW.windowCount--;
+    if (_RGFW.windowCount == 0) RGFW_deinit();
+
+    RGFW_clipboard_switch(NULL);
+	RGFW_FREE(win->event.droppedFiles);
+	if ((win->_flags & RGFW_WINDOW_ALLOC)) {
+		RGFW_FREE(win);
+        win = NULL;
+    }
+}
+
+u64 RGFW_getTimerFreq(void) {
+	static u64 freq = 0;
+	if (freq == 0) {
+		mach_timebase_info_data_t info;
+		mach_timebase_info(&info);
+		freq = (u64)((info.denom * 1e9) / info.numer);
+	}
+
+	return freq;
+}
+
+u64 RGFW_getTimerValue(void) { return (u64)mach_absolute_time(); }
+
+#endif /* RGFW_MACOS */
+
+/*
+	End of MaOS defines
+*/
+
+/*
+	WASM defines
+*/
+
+#ifdef RGFW_WASM
+EM_BOOL Emscripten_on_resize(int eventType, const EmscriptenUiEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root);
+	RGFW_windowResizedCallback(_RGFW.root, RGFW_RECT(0, 0, E->windowInnerWidth, E->windowInnerHeight));
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_fullscreenchange(int eventType, const EmscriptenFullscreenChangeEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+	static u8 fullscreen = RGFW_FALSE;
+	static RGFW_rect ogRect;
+
+	if (fullscreen == RGFW_FALSE) {
+		ogRect = _RGFW.root->r;
+	}
+
+	fullscreen = !fullscreen;
+	RGFW_eventQueuePushEx(e.type = RGFW_windowResized; e._win = _RGFW.root);
+	_RGFW.root->r = RGFW_RECT(0, 0, E->screenWidth, E->screenHeight);
+
+	EM_ASM("Module.canvas.focus();");
+
+	if (fullscreen == RGFW_FALSE) {
+		_RGFW.root->r = RGFW_RECT(0, 0, ogRect.w, ogRect.h);
+		/* emscripten_request_fullscreen("#canvas", 0); */
+	} else {
+		#if __EMSCRIPTEN_major__  >= 1 && __EMSCRIPTEN_minor__  >= 29 && __EMSCRIPTEN_tiny__  >= 0
+			EmscriptenFullscreenStrategy FSStrat = {0};
+			FSStrat.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; /* EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT : EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; */
+			FSStrat.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_HIDEF;
+			FSStrat.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
+			emscripten_request_fullscreen_strategy("#canvas", 1, &FSStrat);
+		#else
+			emscripten_request_fullscreen("#canvas", 1);
+		#endif
+	}
+
+	emscripten_set_canvas_element_size("#canvas", _RGFW.root->r.w, _RGFW.root->r.h);
+
+	RGFW_windowResizedCallback(_RGFW.root, _RGFW.root->r);
+	return EM_TRUE;
+}
+
+
+
+EM_BOOL Emscripten_on_focusin(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+	RGFW_eventQueuePushEx(e.type = RGFW_focusIn; e._win = _RGFW.root);
+	_RGFW.root->_flags |= RGFW_windowFocus;
+	RGFW_focusCallback(_RGFW.root, 1);
+
+	if ((_RGFW.root->_flags & RGFW_HOLD_MOUSE)) RGFW_window_mouseHold(_RGFW.root, RGFW_AREA(_RGFW.root->r.w, _RGFW.root->r.h));
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_focusout(int eventType, const EmscriptenFocusEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData); RGFW_UNUSED(E);
+
+	RGFW_eventQueuePushEx(e.type = RGFW_focusOut; e._win = _RGFW.root);
+    RGFW_window_focusLost(_RGFW.root);
+    RGFW_focusCallback(_RGFW.root, 0);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousemove(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+	RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
+									e.point = RGFW_POINT(E->targetX, E->targetY);
+									e.vector = RGFW_POINT(E->movementX, E->movementY);
+									e._win = _RGFW.root);
+
+	_RGFW.root->_lastMousePoint = RGFW_POINT(E->targetX, E->targetY);
+	RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->targetX, E->targetY), RGFW_POINT(E->movementX, E->movementY));
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mousedown(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	int button = E->button;
+	if (button > 2)
+		button += 2;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
+									e.point = RGFW_POINT(E->targetX, E->targetY);
+									e.vector = RGFW_POINT(E->movementX, E->movementY);
+									e.button = (u8)button;
+									e.scroll = 0;
+									e._win = _RGFW.root);
+	RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
+	RGFW_mouseButtons[button].current = 1;
+
+	RGFW_mouseButtonCallback(_RGFW.root, button, 0, 1);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_mouseup(int eventType, const EmscriptenMouseEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	int button = E->button;
+	if (button > 2)
+		button += 2;
+
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased;
+									e.point = RGFW_POINT(E->targetX, E->targetY);
+									e.vector = RGFW_POINT(E->movementX, E->movementY);
+									e.button = (u8)button;
+									e.scroll = 0;
+									e._win = _RGFW.root);
+	RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
+	RGFW_mouseButtons[button].current = 0;
+
+	RGFW_mouseButtonCallback(_RGFW.root, button, 0, 0);
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_wheel(int eventType, const EmscriptenWheelEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	int button =  RGFW_mouseScrollUp + (E->deltaY < 0);
+	RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
+									e.button = (u8)button;
+									e.scroll = (double)(E->deltaY < 0 ? 1 : -1);
+									e._win = _RGFW.root);
+	RGFW_mouseButtons[button].prev = RGFW_mouseButtons[button].current;
+	RGFW_mouseButtons[button].current = 1;
+	RGFW_mouseButtonCallback(_RGFW.root, button, E->deltaY < 0 ? 1 : -1, 1);
+
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchstart(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+    size_t i;
+    for (i = 0; i < (size_t)E->numTouches; i++) {
+		RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonPressed;
+										e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+										e.button = RGFW_mouseLeft;
+										e._win = _RGFW.root);
+
+	    RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current;
+	    RGFW_mouseButtons[RGFW_mouseLeft].current = 1;
+
+		_RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+        RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
+	    RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 1);
+    }
+
+	return EM_TRUE;
+}
+EM_BOOL Emscripten_on_touchmove(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+    size_t i;
+    for (i = 0; i < (size_t)E->numTouches; i++) {
+		RGFW_eventQueuePushEx(e.type = RGFW_mousePosChanged;
+			e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+			e.button = RGFW_mouseLeft;
+			e._win = _RGFW.root);
+
+		_RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+        RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
+    }
+    return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchend(int eventType, const EmscriptenTouchEvent* E, void* userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+    size_t i;
+    for (i = 0; i < (size_t)E->numTouches; i++) {
+		RGFW_eventQueuePushEx(e.type = RGFW_mouseButtonReleased;
+										e.point = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+										e.button = RGFW_mouseLeft;
+										e._win = _RGFW.root);
+
+		RGFW_mouseButtons[RGFW_mouseLeft].prev = RGFW_mouseButtons[RGFW_mouseLeft].current;
+		RGFW_mouseButtons[RGFW_mouseLeft].current = 0;
+
+		_RGFW.root->_lastMousePoint = RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY);
+		RGFW_mousePosCallback(_RGFW.root, RGFW_POINT(E->touches[i].targetX, E->touches[i].targetY), _RGFW.root->event.vector);
+		RGFW_mouseButtonCallback(_RGFW.root, RGFW_mouseLeft, 0, 0);
+    }
+	return EM_TRUE;
+}
+
+EM_BOOL Emscripten_on_touchcancel(int eventType, const EmscriptenTouchEvent* E, void* userData) { RGFW_UNUSED(eventType); RGFW_UNUSED(userData); return EM_TRUE; }
+
+EM_BOOL Emscripten_on_gamepad(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) {
+	RGFW_UNUSED(eventType); RGFW_UNUSED(userData);
+
+	if (gamepadEvent->index >= 4)
+		return 0;
+
+	size_t i = gamepadEvent->index;
+	if (gamepadEvent->connected) {
+		RGFW_STRNCPY(RGFW_gamepads_name[gamepadEvent->index], gamepadEvent->id, sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1);
+		RGFW_gamepads_name[gamepadEvent->index][sizeof(RGFW_gamepads_name[gamepadEvent->index]) - 1] = '\0';
+		RGFW_gamepads_type[i] = RGFW_gamepadUnknown;
+		if (RGFW_STRSTR(RGFW_gamepads_name[i], "Microsoft") || RGFW_STRSTR(RGFW_gamepads_name[i], "X-Box"))
+			RGFW_gamepads_type[i] = RGFW_gamepadMicrosoft;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "PlayStation") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS3") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS4") || RGFW_STRSTR(RGFW_gamepads_name[i], "PS5"))
+			RGFW_gamepads_type[i] = RGFW_gamepadSony;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Nintendo"))
+			RGFW_gamepads_type[i] = RGFW_gamepadNintendo;
+		else if (RGFW_STRSTR(RGFW_gamepads_name[i], "Logitech"))
+			RGFW_gamepads_type[i] = RGFW_gamepadLogitech;
+		RGFW_gamepadCount++;
+	} else {
+		RGFW_gamepadCount--;
+	}
+
+	RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(gamepadEvent->connected ? RGFW_gamepadConnected : RGFW_gamepadConnected);
+									e.gamepad = (u16)gamepadEvent->index;
+									e._win = _RGFW.root);
+
+	RGFW_gamepadCallback(_RGFW.root, gamepadEvent->index, gamepadEvent->connected);
+	RGFW_gamepads[gamepadEvent->index] = gamepadEvent->connected;
+
+    return 1; /* The event was consumed by the callback handler */
+}
+
+u32 RGFW_wASMPhysicalToRGFW(u32 hash) {
+	switch(hash) {             /* 0x0000 */
+		case 0x67243A2DU /* Escape             */: return RGFW_escape;               /* 0x0001 */
+		case 0x67251058U /* Digit0             */: return RGFW_0;                    /* 0x0002 */
+		case 0x67251059U /* Digit1             */: return RGFW_1;                    /* 0x0003 */
+		case 0x6725105AU /* Digit2             */: return RGFW_2;                    /* 0x0004 */
+		case 0x6725105BU /* Digit3             */: return RGFW_3;                    /* 0x0005 */
+		case 0x6725105CU /* Digit4             */: return RGFW_4;                    /* 0x0006 */
+		case 0x6725105DU /* Digit5             */: return RGFW_5;                    /* 0x0007 */
+		case 0x6725105EU /* Digit6             */: return RGFW_6;                    /* 0x0008 */
+		case 0x6725105FU /* Digit7             */: return RGFW_7;                    /* 0x0009 */
+		case 0x67251050U /* Digit8             */: return RGFW_8;                    /* 0x000A */
+		case 0x67251051U /* Digit9             */: return RGFW_9;                    /* 0x000B */
+		case 0x92E14DD3U /* Minus              */: return RGFW_minus;                /* 0x000C */
+		case 0x92E1FBACU /* Equal              */: return RGFW_equals;                /* 0x000D */
+		case 0x36BF1CB5U /* Backspace          */: return RGFW_backSpace;            /* 0x000E */
+		case 0x7B8E51E2U  /* Tab                */: return RGFW_tab;                  /* 0x000F */
+		case 0x2C595B51U /* KeyQ               */: return RGFW_q;                    /* 0x0010 */
+		case 0x2C595B57U /* KeyW               */: return RGFW_w;                    /* 0x0011 */
+		case 0x2C595B45U /* KeyE               */: return RGFW_e;                    /* 0x0012 */
+		case 0x2C595B52U /* KeyR               */: return RGFW_r;                    /* 0x0013 */
+		case 0x2C595B54U /* KeyT               */: return RGFW_t;                    /* 0x0014 */
+		case 0x2C595B59U /* KeyY               */: return RGFW_y;                    /* 0x0015 */
+		case 0x2C595B55U /* KeyU               */: return RGFW_u;                    /* 0x0016 */
+		case 0x2C595B4FU /* KeyO               */: return RGFW_o;                    /* 0x0018 */
+		case 0x2C595B50U /* KeyP               */: return RGFW_p;                    /* 0x0019 */
+		case 0x45D8158CU /* BracketLeft        */: return RGFW_closeBracket;         /* 0x001A */
+		case 0xDEEABF7CU /* BracketRight       */: return RGFW_bracket;        /* 0x001B */
+		case 0x92E1C5D2U /* Enter              */: return RGFW_return;                /* 0x001C */
+		case 0xE058958CU /* ControlLeft        */: return RGFW_controlL;         /* 0x001D */
+		case 0x2C595B41U /* KeyA               */: return RGFW_a;                    /* 0x001E */
+		case 0x2C595B53U /* KeyS               */: return RGFW_s;                    /* 0x001F */
+		case 0x2C595B44U /* KeyD               */: return RGFW_d;                    /* 0x0020 */
+		case 0x2C595B46U /* KeyF               */: return RGFW_f;                    /* 0x0021 */
+		case 0x2C595B47U /* KeyG               */: return RGFW_g;                    /* 0x0022 */
+		case 0x2C595B48U /* KeyH               */: return RGFW_h;                    /* 0x0023 */
+		case 0x2C595B4AU /* KeyJ               */: return RGFW_j;                    /* 0x0024 */
+		case 0x2C595B4BU /* KeyK               */: return RGFW_k;                    /* 0x0025 */
+		case 0x2C595B4CU /* KeyL               */: return RGFW_l;                    /* 0x0026 */
+		case 0x2707219EU /* Semicolon          */: return RGFW_semicolon;            /* 0x0027 */
+		case 0x92E0B58DU /* Quote              */: return RGFW_apostrophe;                /* 0x0028 */
+		case 0x36BF358DU /* Backquote          */: return RGFW_backtick;            /* 0x0029 */
+		case 0x26B1958CU /* ShiftLeft          */: return RGFW_shiftL;           /* 0x002A */
+		case 0x36BF2438U /* Backslash          */: return RGFW_backSlash;            /* 0x002B */
+		case 0x2C595B5AU /* KeyZ               */: return RGFW_z;                    /* 0x002C */
+		case 0x2C595B58U /* KeyX               */: return RGFW_x;                    /* 0x002D */
+		case 0x2C595B43U /* KeyC               */: return RGFW_c;                    /* 0x002E */
+		case 0x2C595B56U /* KeyV               */: return RGFW_v;                    /* 0x002F */
+		case 0x2C595B42U /* KeyB               */: return RGFW_b;                    /* 0x0030 */
+		case 0x2C595B4EU /* KeyN               */: return RGFW_n;                    /* 0x0031 */
+		case 0x2C595B4DU /* KeyM               */: return RGFW_m;                    /* 0x0032 */
+		case 0x92E1A1C1U /* Comma              */: return RGFW_comma;                /* 0x0033 */
+		case 0x672FFAD4U /* Period             */: return RGFW_period;               /* 0x0034 */
+		case 0x92E0A438U /* Slash              */: return RGFW_slash;                /* 0x0035 */
+		case 0xC5A6BF7CU /* ShiftRight         */: return RGFW_shiftR;
+		case 0x5D64DA91U /* NumpadMultiply     */: return RGFW_multiply;
+		case 0xC914958CU /* AltLeft            */: return RGFW_altL;             /* 0x0038 */
+		case 0x92E09CB5U /* Space              */: return RGFW_space;                /* 0x0039 */
+		case 0xB8FAE73BU  /* CapsLock           */: return RGFW_capsLock;            /* 0x003A */
+		case 0x7174B789U /* F1                 */: return RGFW_F1;                   /* 0x003B */
+		case 0x7174B78AU /* F2                 */: return RGFW_F2;                   /* 0x003C */
+		case 0x7174B78BU /* F3                 */: return RGFW_F3;                   /* 0x003D */
+		case 0x7174B78CU /* F4                 */: return RGFW_F4;                   /* 0x003E */
+		case 0x7174B78DU /* F5                 */: return RGFW_F5;                   /* 0x003F */
+		case 0x7174B78EU /* F6                 */: return RGFW_F6;                   /* 0x0040 */
+		case 0x7174B78FU /* F7                 */: return RGFW_F7;                   /* 0x0041 */
+		case 0x7174B780U /* F8                 */: return RGFW_F8;                   /* 0x0042 */
+		case 0x7174B781U /* F9                 */: return RGFW_F9;                   /* 0x0043 */
+		case 0x7B8E57B0U  /* F10                */: return RGFW_F10;                  /* 0x0044 */
+		case 0xC925FCDFU /* Numpad7            */: return RGFW_multiply;             /* 0x0047 */
+		case 0xC925FCD0U /* Numpad8            */: return RGFW_KP_8;             /* 0x0048 */
+		case 0xC925FCD1U /* Numpad9            */: return RGFW_KP_9;             /* 0x0049 */
+		case 0x5EA3E8A4U /* NumpadSubtract     */: return RGFW_minus;      /* 0x004A */
+		case 0xC925FCDCU /* Numpad4            */: return RGFW_KP_4;             /* 0x004B */
+		case 0xC925FCDDU /* Numpad5            */: return RGFW_KP_5;             /* 0x004C */
+		case 0xC925FCDEU /* Numpad6            */: return RGFW_KP_6;             /* 0x004D */
+		case 0xC925FCD9U /* Numpad1            */: return RGFW_KP_1;             /* 0x004F */
+		case 0xC925FCDAU /* Numpad2            */: return RGFW_KP_2;             /* 0x0050 */
+		case 0xC925FCDBU /* Numpad3            */: return RGFW_KP_3;             /* 0x0051 */
+		case 0xC925FCD8U /* Numpad0            */: return RGFW_KP_0;             /* 0x0052 */
+		case 0x95852DACU /* NumpadDecimal      */: return RGFW_period;       /* 0x0053 */
+		case 0x7B8E57B1U  /* F11                */: return RGFW_F11;                  /* 0x0057 */
+		case 0x7B8E57B2U  /* F12                */: return RGFW_F12;                  /* 0x0058 */
+		case 0x7393FBACU /* NumpadEqual        */: return RGFW_KP_Return;
+		case 0xB88EBF7CU  /* AltRight           */: return RGFW_altR;            /* 0xE038 */
+		case 0xC925873BU /* NumLock            */: return RGFW_numLock;             /* 0xE045 */
+		case 0x2C595F45U /* Home               */: return RGFW_home;                 /* 0xE047 */
+		case 0xC91BB690U /* ArrowUp            */: return RGFW_up;             /* 0xE048 */
+		case 0x672F9210U /* PageUp             */: return RGFW_pageUp;              /* 0xE049 */
+		case 0x3799258CU /* ArrowLeft          */: return RGFW_left;           /* 0xE04B */
+		case 0x4CE33F7CU /* ArrowRight         */: return RGFW_right;          /* 0xE04D */
+		case 0x7B8E55DCU  /* End                */: return RGFW_end;                  /* 0xE04F */
+		case 0x3799379EU /* ArrowDown          */: return RGFW_down;           /* 0xE050 */
+		case 0xBA90179EU /* PageDown           */: return RGFW_pageDown;            /* 0xE051 */
+		case 0x6723CB2CU /* Insert             */: return RGFW_insert;               /* 0xE052 */
+		case 0x6725C50DU /* Delete             */: return RGFW_delete;               /* 0xE053 */
+		case 0x6723658CU /* OSLeft             */: return RGFW_superL;              /* 0xE05B */
+		case 0x39643F7CU /* MetaRight          */: return RGFW_superR;           /* 0xE05C */
+	}
+
+	return 0;
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyEvent(char* key, char* code, RGFW_bool press) {
+	const char* iCode = code;
+
+	u32 hash = 0;
+	while(*iCode) hash = ((hash ^ 0x7E057D79U) << 3) ^ (unsigned int)*iCode++;
+
+	u32 physicalKey = RGFW_wASMPhysicalToRGFW(hash);
+
+	u8 mappedKey = (u8)(*((u32*)key));
+
+	if (*((u16*)key) != mappedKey) {
+		mappedKey = 0;
+		if (*((u32*)key) == *((u32*)"Tab")) mappedKey = RGFW_tab;
+	}
+
+	RGFW_eventQueuePushEx(e.type = (RGFW_eventType)(press ? RGFW_keyPressed : RGFW_keyReleased);
+										e.key = (u8)physicalKey;
+										e.keyChar = (u8)mappedKey;
+										e.keyMod = _RGFW.root->event.keyMod;
+										e._win = _RGFW.root);
+
+	RGFW_keyboard[physicalKey].prev = RGFW_keyboard[physicalKey].current;
+	RGFW_keyboard[physicalKey].current = press;
+
+	RGFW_keyCallback(_RGFW.root, physicalKey, mappedKey, _RGFW.root->event.keyMod, press);
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_handleKeyMods(RGFW_bool capital, RGFW_bool numlock, RGFW_bool control, RGFW_bool alt, RGFW_bool shift, RGFW_bool super, RGFW_bool scroll) {
+	RGFW_updateKeyModsPro(_RGFW.root, capital, numlock, control, alt, shift, super, scroll);
+}
+
+void EMSCRIPTEN_KEEPALIVE Emscripten_onDrop(size_t count) {
+	if (!(_RGFW.root->_flags & RGFW_windowAllowDND))
+		return;
+
+	_RGFW.root->event.droppedFilesCount = count; 
+	RGFW_eventQueuePushEx(e.type = RGFW_DND;
+									e.droppedFilesCount = count;
+									e._win = _RGFW.root);
+	RGFW_dndCallback(_RGFW.root, _RGFW.root->event.droppedFiles, count);
+}
+
+RGFW_bool RGFW_stopCheckEvents_bool = RGFW_FALSE;
+void RGFW_stopCheckEvents(void) {
+	RGFW_stopCheckEvents_bool = RGFW_TRUE;
+}
+
+void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) {
+	RGFW_UNUSED(win);
+	if (waitMS == 0) return;
+
+	u32 start = (u32)(((u64)RGFW_getTimeNS()) / 1e+6);
+
+	while ((_RGFW.eventLen == 0) && RGFW_stopCheckEvents_bool == RGFW_FALSE && (RGFW_getTimeNS() / 1e+6) - start < waitMS)
+		emscripten_sleep(0);
+
+	RGFW_stopCheckEvents_bool = RGFW_FALSE;
+}
+
+void RGFW_window_initBufferPtr(RGFW_window* win, u8* buffer, RGFW_area area){
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+		win->buffer = buffer;
+		win->bufferSize = area;
+	#ifdef RGFW_OSMESA
+			win->src.ctx = OSMesaCreateContext(OSMESA_RGBA, NULL);
+			OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, area.w, area.h);
+			OSMesaPixelStore(OSMESA_Y_UP, 0);
+	#endif
+	#else
+	RGFW_UNUSED(win);  RGFW_UNUSED(buffer); RGFW_UNUSED(area); /*!< if buffer rendering is not being used */
+	#endif
+}
+
+void EMSCRIPTEN_KEEPALIVE RGFW_makeSetValue(size_t index, char* file) {
+	/* This seems like a terrible idea, don't replicate this unless you hate yourself or the OS */
+	/* TODO: find a better way to do this
+	*/
+	RGFW_STRNCPY((char*)_RGFW.root->event.droppedFiles[index], file, RGFW_MAX_PATH - 1);
+	_RGFW.root->event.droppedFiles[index][RGFW_MAX_PATH - 1] = '\0';
+}
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+
+void EMSCRIPTEN_KEEPALIVE RGFW_mkdir(char* name) { mkdir(name, 0755); }
+
+void EMSCRIPTEN_KEEPALIVE RGFW_writeFile(const char *path, const char *data, size_t len) {
+    FILE* file = fopen(path, "w+");
+	if (file == NULL)
+		return;
+
+    fwrite(data, sizeof(char), len, file);
+    fclose(file);
+}
+
+void RGFW_window_initOpenGL(RGFW_window* win) {
+#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_BUFFER)
+	EmscriptenWebGLContextAttributes attrs;
+	attrs.alpha = RGFW_GL_HINTS[RGFW_glDepth];
+	attrs.depth = RGFW_GL_HINTS[RGFW_glAlpha];
+	attrs.stencil = RGFW_GL_HINTS[RGFW_glStencil];
+	attrs.antialias = RGFW_GL_HINTS[RGFW_glSamples];
+	attrs.premultipliedAlpha = EM_TRUE;
+	attrs.preserveDrawingBuffer = EM_FALSE;
+
+	if (RGFW_GL_HINTS[RGFW_glDoubleBuffer] == 0)
+		attrs.renderViaOffscreenBackBuffer = 0;
+	else
+		attrs.renderViaOffscreenBackBuffer = RGFW_GL_HINTS[RGFW_glAuxBuffers];
+
+	attrs.failIfMajorPerformanceCaveat = EM_FALSE;
+	attrs.majorVersion = (RGFW_GL_HINTS[RGFW_glMajor] == 0) ? 1 : RGFW_GL_HINTS[RGFW_glMajor];
+	attrs.minorVersion = RGFW_GL_HINTS[RGFW_glMinor];
+
+	attrs.enableExtensionsByDefault = EM_TRUE;
+	attrs.explicitSwapControl = EM_TRUE;
+
+	emscripten_webgl_init_context_attributes(&attrs);
+	win->src.ctx = emscripten_webgl_create_context("#canvas", &attrs);
+	emscripten_webgl_make_context_current(win->src.ctx);
+
+	#ifdef LEGACY_GL_EMULATION
+	EM_ASM("Module.useWebGL = true; GLImmediate.init();");
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context initalized");
+    #endif
+    glViewport(0, 0, win->r.w, win->r.h);
+#endif
+}
+
+void RGFW_window_freeOpenGL(RGFW_window* win) {
+#if defined(RGFW_OPENGL) && !defined(RGFW_WEBGPU) && !defined(RGFW_OSMESA) && !defined(RGFW_OSMESA)
+	if (win->src.ctx == 0) return;
+	emscripten_webgl_destroy_context(win->src.ctx);
+	win->src.ctx = 0;
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoOpenGL, RGFW_DEBUG_CTX(win, 0), "opengl context freed");
+#elif defined(RGFW_OPENGL) && defined(RGFW_OSMESA)
+	if(win->src.ctx == 0) return;
+	OSMesaDestroyContext(win->src.ctx);
+	win->src.ctx = 0;
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+i32 RGFW_init(void) {  
+#if defined(RGFW_C89) || defined(__cplusplus)
+    if (_RGFW_init) return 0; 
+    _RGFW_init = RGFW_TRUE;
+    _RGFW.root = NULL; _RGFW.current = NULL; _RGFW.windowCount = -2; _RGFW.eventLen = 0; _RGFW.eventIndex = 0;
+#endif
+
+    _RGFW.windowCount = 0;  
+    RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context initialized"); 
+    return 0; 
+}
+
+RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) {
+    RGFW_window_basic_init(win, rect, flags);
+	RGFW_window_initOpenGL(win);
+
+	#if defined(RGFW_WEBGPU)
+		win->src.ctx = wgpuCreateInstance(NULL);
+		win->src.device = emscripten_webgpu_get_device();
+		win->src.queue = wgpuDeviceGetQueue(win->src.device);
+	#endif
+
+	emscripten_set_canvas_element_size("#canvas", rect.w, rect.h);
+	emscripten_set_window_title(name);
+
+	/* load callbacks */
+    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_resize);
+    emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, NULL, EM_FALSE, Emscripten_on_fullscreenchange);
+    emscripten_set_mousemove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousemove);
+    emscripten_set_touchstart_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchstart);
+    emscripten_set_touchend_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchend);
+    emscripten_set_touchmove_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchmove);
+    emscripten_set_touchcancel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_touchcancel);
+    emscripten_set_mousedown_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mousedown);
+    emscripten_set_mouseup_callback("#canvas", NULL, EM_FALSE, Emscripten_on_mouseup);
+    emscripten_set_wheel_callback("#canvas", NULL, EM_FALSE, Emscripten_on_wheel);
+    emscripten_set_focusin_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusin);
+    emscripten_set_focusout_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_FALSE, Emscripten_on_focusout);
+	emscripten_set_gamepadconnected_callback(NULL, 1, Emscripten_on_gamepad);
+	emscripten_set_gamepaddisconnected_callback(NULL, 1, Emscripten_on_gamepad);
+
+	if (flags & RGFW_windowAllowDND)  {
+		win->_flags |= RGFW_windowAllowDND;
+	}
+
+	EM_ASM({
+		window.addEventListener("keydown",
+			(event) => {
+				var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code);
+				Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+				Module._RGFW_handleKeyEvent(key, code, 1);
+				_free(key); _free(code);
+			},
+		true);
+		window.addEventListener("keyup",
+			(event) => {
+				var key = stringToNewUTF8(event.key); var code = stringToNewUTF8(event.code);
+				Module._RGFW_handleKeyMods(event.getModifierState("CapsLock"), event.getModifierState("NumLock"), event.getModifierState("Control"), event.getModifierState("Alt"), event.getModifierState("Shift"), event.getModifierState("Meta"), event.getModifierState("ScrollLock"));
+				Module._RGFW_handleKeyEvent(key, code, 0);
+				_free(key); _free(code);
+			},
+		true);
+	});
+
+    EM_ASM({
+		var canvas = document.getElementById('canvas');
+        canvas.addEventListener('drop', function(e) {
+            e.preventDefault();
+            if (e.dataTransfer.file < 0)
+				return;
+
+			var filenamesArray = [];
+			var count = e.dataTransfer.files.length;
+
+			/* Read and save the files to emscripten's files */
+			var drop_dir = '.rgfw_dropped_files';
+			Module._RGFW_mkdir(drop_dir);
+
+			for (var i = 0; i < count; i++) {
+				var file = e.dataTransfer.files[i];
+
+				var path = '/' + drop_dir + '/' + file.name.replace("//", '_');
+				var reader = new FileReader();
+
+				reader.onloadend = (e) => {
+					if (reader.readyState != 2) {
+						out('failed to read dropped file: '+file.name+': '+reader.error);
+					}
+					else {
+						var data = e.target.result;
+
+						_RGFW_writeFile(path, new Uint8Array(data), file.size);
+					}
+				};
+
+				reader.readAsArrayBuffer(file);
+				/* This works weird on modern opengl */
+				var filename = stringToNewUTF8(path);
+
+				filenamesArray.push(filename);
+
+				Module._RGFW_makeSetValue(i, filename);
+			}
+
+			Module._Emscripten_onDrop(count);
+
+			for (var i = 0; i < count; ++i) {
+				_free(filenamesArray[i]);
+			}
+        }, true);
+
+        canvas.addEventListener('dragover', function(e) { e.preventDefault(); return false; }, true);
+    });
+
+	RGFW_window_setFlags(win, flags);
+
+	if ((flags & RGFW_windowNoInitAPI) == 0) {
+        RGFW_window_initBuffer(win);
+    }
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a new  window was created");
+    return win;
+}
+
+u8 RGFW_rgfwToKeyChar(u32 rgfw_keycode) {
+    return (u8)rgfw_keycode; /* TODO */
+}
+
+RGFW_event* RGFW_window_checkEvent(RGFW_window* win) {
+    if (win == NULL || ((win->_flags & RGFW_windowFreeOnClose) && (win->_flags & RGFW_EVENT_QUIT))) return NULL;
+    RGFW_event* ev =  RGFW_window_checkEventCore(win);
+	if (ev) return ev;
+
+	emscripten_sample_gamepad_data();
+	/* check gamepads */
+    int i;
+    for (i = 0; (i < emscripten_get_num_gamepads()) && (i < 4); i++) {
+		if (RGFW_gamepads[i] == 0)
+			continue;
+        EmscriptenGamepadEvent gamepadState;
+
+        if (emscripten_get_gamepad_status(i, &gamepadState) != EMSCRIPTEN_RESULT_SUCCESS)
+			break;
+
+		/* Register buttons data for every connected gamepad */
+        int j;
+        for (j = 0; (j < gamepadState.numButtons) && (j < 16); j++) {
+			u32 map[] = {
+				RGFW_gamepadA, RGFW_gamepadB, RGFW_gamepadX, RGFW_gamepadY,
+				RGFW_gamepadL1, RGFW_gamepadR1, RGFW_gamepadL2, RGFW_gamepadR2,
+				RGFW_gamepadSelect, RGFW_gamepadStart,
+				RGFW_gamepadL3, RGFW_gamepadR3,
+				RGFW_gamepadUp, RGFW_gamepadDown, RGFW_gamepadLeft, RGFW_gamepadRight, RGFW_gamepadHome
+			};
+
+
+			u32 button = map[j];
+			if (button == 404)
+				continue;
+
+			if (RGFW_gamepadPressed[i][button].current != gamepadState.digitalButton[j]) {
+				if (gamepadState.digitalButton[j])
+					win->event.type = RGFW_gamepadButtonPressed;
+				else
+					win->event.type = RGFW_gamepadButtonReleased;
+
+				win->event.gamepad = i;
+				win->event.button = map[j];
+
+				RGFW_gamepadPressed[i][button].prev = RGFW_gamepadPressed[i][button].current;
+				RGFW_gamepadPressed[i][button].current = gamepadState.digitalButton[j];
+
+				RGFW_gamepadButtonCallback(win, win->event.gamepad, win->event.button, gamepadState.digitalButton[j]);
+				return &win->event;
+			}
+		}
+
+		for (j = 0; (j < gamepadState.numAxes) && (j < 4); j += 2) {
+			win->event.axisesCount = gamepadState.numAxes / 2;
+			if (RGFW_gamepadAxes[i][(size_t)(j / 2)].x != (i8)(gamepadState.axis[j] * 100.0f) ||
+				RGFW_gamepadAxes[i][(size_t)(j / 2)].y != (i8)(gamepadState.axis[j + 1] * 100.0f)
+			) {
+
+				RGFW_gamepadAxes[i][(size_t)(j / 2)].x = (i8)(gamepadState.axis[j] * 100.0f);
+				RGFW_gamepadAxes[i][(size_t)(j / 2)].y = (i8)(gamepadState.axis[j + 1] * 100.0f);
+				win->event.axis[(size_t)(j / 2)] = RGFW_gamepadAxes[i][(size_t)(j / 2)];
+
+				win->event.type = RGFW_gamepadAxisMove;
+				win->event.gamepad = i;
+				win->event.whichAxis = j / 2;
+
+				RGFW_gamepadAxisCallback(win, win->event.gamepad, win->event.axis, win->event.axisesCount, win->event.whichAxis);
+				return &win->event;
+			}
+		}
+    }
+
+	return NULL;
+}
+
+void RGFW_window_resize(RGFW_window* win, RGFW_area a) {
+	RGFW_UNUSED(win);
+	emscripten_set_canvas_element_size("#canvas", a.w, a.h);
+}
+
+/* NOTE: I don't know if this is possible */
+void RGFW_window_moveMouse(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
+/* this one might be possible but it looks iffy */
+RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { RGFW_UNUSED(channels); RGFW_UNUSED(a); RGFW_UNUSED(icon); return NULL; }
+
+void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { RGFW_UNUSED(win); RGFW_UNUSED(mouse); }
+void RGFW_freeMouse(RGFW_mouse* mouse) { RGFW_UNUSED(mouse); }
+
+RGFW_bool RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) {
+	static const char cursors[16][16] = {
+		"default", "default", "text", "crosshair",
+		"pointer", "ew-resize", "ns-resize", "nwse-resize", "nesw-resize",
+		"move", "not-allowed"
+	};
+
+	RGFW_UNUSED(win);
+	EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursors[mouse]);
+	return RGFW_TRUE;
+}
+
+RGFW_bool RGFW_window_setMouseDefault(RGFW_window* win) {
+	return RGFW_window_setMouseStandard(win, RGFW_mouseNormal);
+}
+
+void RGFW_window_showMouse(RGFW_window* win, RGFW_bool show) {
+	RGFW_window_showMouseFlags(win, show);
+	if (show)
+		RGFW_window_setMouseDefault(win);
+	else
+		EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
+}
+
+RGFW_point RGFW_getGlobalMousePoint(void) {
+    RGFW_point point;
+    point.x = EM_ASM_INT({
+        return window.mouseX || 0;
+    });
+    point.y = EM_ASM_INT({
+        return window.mouseY || 0;
+    });
+    return point;
+}
+
+void RGFW_window_setMousePassthrough(RGFW_window* win, RGFW_bool passthrough) {
+	RGFW_UNUSED(win);
+
+    EM_ASM_({
+        var canvas = document.getElementById('canvas');
+        if ($0) {
+            canvas.style.pointerEvents = 'none';
+        } else {
+            canvas.style.pointerEvents = 'auto';
+        }
+    }, passthrough);
+}
+
+void RGFW_writeClipboard(const char* text, u32 textLen) {
+	RGFW_UNUSED(textLen);
+	EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
+}
+
+
+RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) {
+	RGFW_UNUSED(str); RGFW_UNUSED(strCapacity);
+	/*
+		placeholder code for later
+		I'm not sure if this is possible do the the async stuff
+	*/
+	return 0;
+}
+
+void RGFW_window_swapBuffers_software(RGFW_window* win) {
+#if defined(RGFW_OSMESA)
+	EM_ASM_({
+		var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
+		let context = document.getElementById("canvas").getContext("2d");
+		let image = context.getImageData(0, 0, $1, $2);
+		image.data.set(data);
+		context.putImageData(image, 0, $4 - $2);
+	}, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h);
+#elif defined(RGFW_BUFFER)
+	EM_ASM_({
+		var data = Module.HEAPU8.slice($0, $0 + $1 * $2 * 4);
+		let context = document.getElementById("canvas").getContext("2d");
+		let image = context.getImageData(0, 0, $1, $2);
+		image.data.set(data);
+		context.putImageData(image, 0, 0);
+	}, win->buffer, win->bufferSize.w, win->bufferSize.h, win->r.w, win->r.h);
+	emscripten_sleep(0);
+#else
+	RGFW_UNUSED(win);
+#endif
+}
+
+void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) {
+#if !defined(RGFW_WEBGPU) && !(defined(RGFW_OSMESA) || defined(RGFW_BUFFER))
+	if (win == NULL)
+	    emscripten_webgl_make_context_current(0);
+	else
+	    emscripten_webgl_make_context_current(win->src.ctx);
+#endif
+}
+
+
+void RGFW_window_swapBuffers_OpenGL(RGFW_window* win) {
+#ifndef RGFW_WEBGPU
+	emscripten_webgl_commit_frame();
+
+#endif
+    emscripten_sleep(0);
+}
+
+#ifndef RGFW_WEBGPU
+void* RGFW_getCurrent_OpenGL(void) { return (void*)emscripten_webgl_get_current_context(); }
+#endif
+
+#ifndef RGFW_EGL
+void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); }
+#endif
+
+void RGFW_deinit(void) { _RGFW.windowCount = -1; RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoGlobal, RGFW_DEBUG_CTX(NULL, 0), "global context deinitialized"); }
+
+void RGFW_window_close(RGFW_window* win) {
+	if ((win->_flags & RGFW_windowNoInitAPI) == 0) RGFW_window_freeOpenGL(win);
+
+	#if defined(RGFW_OSMESA) || defined(RGFW_BUFFER)
+	if ((win->_flags & RGFW_BUFFER_ALLOC))
+		RGFW_FREE(win->buffer);
+	#endif
+
+	RGFW_sendDebugInfo(RGFW_typeInfo, RGFW_infoWindow, RGFW_DEBUG_CTX(win, 0), "a window was freed");
+    _RGFW.windowCount--;
+    if (_RGFW.windowCount == 0) RGFW_deinit();
+
+    RGFW_clipboard_switch(NULL);
+	RGFW_FREE(win->event.droppedFiles);
+	if ((win->_flags & RGFW_WINDOW_ALLOC)) {
+	    RGFW_FREE(win);
+        win = NULL;
+    }
+}
+
+int RGFW_innerWidth(void) {   return EM_ASM_INT({ return window.innerWidth; });  }
+int RGFW_innerHeight(void) {  return EM_ASM_INT({ return window.innerHeight; });  }
+
+RGFW_area RGFW_getScreenSize(void) {
+	return RGFW_AREA(RGFW_innerWidth(), RGFW_innerHeight());
+}
+
+RGFW_bool RGFW_extensionSupportedPlatform(const char* extension, size_t len) {
+#ifdef RGFW_OPENGL
+    return EM_ASM_INT({
+        var ext = UTF8ToString($0, $1);
+        var canvas = document.querySelector('canvas');
+        var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
+        if (!gl) return 0;
+
+        var supported = gl.getSupportedExtensions();
+        return supported && supported.includes(ext) ? 1 : 0;
+    }, extension, len);
+#else
+    return RGFW_FALSE;
+#endif
+}
+
+RGFW_proc RGFW_getProcAddress(const char* procname) {
+#ifdef RGFW_OPENGL
+    return (RGFW_proc)emscripten_webgl_get_proc_address(procname);
+#else
+    return NULL
+#endif
+}
+
+void RGFW_sleep(u64 milisecond) {
+	emscripten_sleep(milisecond);
+}
+
+u64 RGFW_getTimerFreq(void) { return (u64)1000; }
+u64 RGFW_getTimerValue(void) { return emscripten_get_now() * 1e+6; }
+
+void RGFW_releaseCursor(RGFW_window* win) {
+	RGFW_UNUSED(win);
+	emscripten_exit_pointerlock();
+}
+
+void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) {
+	RGFW_UNUSED(win); RGFW_UNUSED(r);
+
+	emscripten_request_pointerlock("#canvas", 1);
+}
+
+
+void RGFW_window_setName(RGFW_window* win, const char* name) {
+	RGFW_UNUSED(win);
+	emscripten_set_window_title(name);
+}
+
+void RGFW_window_maximize(RGFW_window* win) {
+	RGFW_ASSERT(win != NULL);
+
+	RGFW_area screen = RGFW_getScreenSize();
+	RGFW_window_move(win, RGFW_POINT(0, 0));
+	RGFW_window_resize(win, screen);
+}
+
+void RGFW_window_setFullscreen(RGFW_window* win, RGFW_bool fullscreen) {
+	RGFW_ASSERT(win != NULL);
+	if (fullscreen) {
+		win->_flags |= RGFW_windowFullscreen;
+		EM_ASM( Module.requestFullscreen(false, true); );
+		return;
+	}
+	win->_flags &= ~(u32)RGFW_windowFullscreen;
+	EM_ASM( Module.exitFullscreen(false, true); );
+}
+
+void RGFW_window_setOpacity(RGFW_window* win, u8 opacity) {
+	RGFW_UNUSED(win);
+	EM_ASM({
+		var element = document.getElementById("canvas");
+		if (element)
+		  element.style.opacity = $1;
+	  }, "elementId", opacity);
+}
+
+/* unsupported functions */
+void RGFW_window_focus(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_raise(RGFW_window* win) { RGFW_UNUSED(win); }
+RGFW_bool RGFW_monitor_requestMode(RGFW_monitor mon, RGFW_monitorMode mode, RGFW_modeRequest request) { RGFW_UNUSED(mon); RGFW_UNUSED(mode); RGFW_UNUSED(request); return RGFW_FALSE; }
+RGFW_monitor* RGFW_getMonitors(size_t* len) { RGFW_UNUSED(len); return NULL; }
+RGFW_monitor RGFW_getPrimaryMonitor(void) { return (RGFW_monitor){}; }
+void RGFW_window_move(RGFW_window* win, RGFW_point v) { RGFW_UNUSED(win); RGFW_UNUSED(v); }
+void RGFW_window_setAspectRatio(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a); }
+void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a);  }
+void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { RGFW_UNUSED(win); RGFW_UNUSED(a);  }
+void RGFW_window_minimize(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_restore(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_setFloating(RGFW_window* win, RGFW_bool floating) { RGFW_UNUSED(win); RGFW_UNUSED(floating); }
+void RGFW_window_setBorder(RGFW_window* win, RGFW_bool border) { RGFW_UNUSED(win); RGFW_UNUSED(border);  }
+RGFW_bool RGFW_window_setIconEx(RGFW_window* win, u8* icon, RGFW_area a, i32 channels, u8 type) { RGFW_UNUSED(win); RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); RGFW_UNUSED(type); return RGFW_FALSE;  }
+void RGFW_window_hide(RGFW_window* win) { RGFW_UNUSED(win); }
+void RGFW_window_show(RGFW_window* win) {RGFW_UNUSED(win); }
+RGFW_bool RGFW_window_isHidden(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMinimized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isMaximized(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_bool RGFW_window_isFloating(RGFW_window* win) { RGFW_UNUSED(win); return RGFW_FALSE; }
+RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { RGFW_UNUSED(win); return (RGFW_monitor){}; }
+#endif
+
+/* end of web asm defines */
+
+/* unix (macOS, linux, web asm) only stuff */
+#if defined(RGFW_X11) || defined(RGFW_MACOS) || defined(RGFW_WASM)  || defined(RGFW_WAYLAND)
+#ifndef RGFW_NO_THREADS
+#include <pthread.h>
+
+RGFW_thread RGFW_createThread(RGFW_threadFunc_ptr ptr, void* args) {
+	RGFW_thread t;
+	pthread_create((pthread_t*) &t, NULL, *ptr, args);
+	return t;
+}
+void RGFW_cancelThread(RGFW_thread thread) { pthread_cancel((pthread_t) thread); }
+void RGFW_joinThread(RGFW_thread thread) { pthread_join((pthread_t) thread, NULL); }
+
+#if defined(__linux__)
+void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { pthread_setschedprio((pthread_t)thread, priority); }
+#else
+void RGFW_setThreadPriority(RGFW_thread thread, u8 priority) { RGFW_UNUSED(thread); RGFW_UNUSED(priority); }
+#endif
+#endif
+
+#ifndef RGFW_WASM
+void RGFW_sleep(u64 ms) {
+	struct timespec time;
+	time.tv_sec = 0;
+	time.tv_nsec = (long int)((double)ms * 1e+6);
+
+	#ifndef RGFW_NO_UNIX_CLOCK
+	nanosleep(&time, NULL);
+	#endif
+}
+#endif
+
+#endif /* end of unix / mac stuff */
+#endif /* RGFW_IMPLEMENTATION */
+
+#if defined(__cplusplus) && !defined(__EMSCRIPTEN__)
+}
+#endif
+
+#if _MSC_VER
+	#pragma warning( pop )
+#endif
diff --git a/raylib/src/external/cgltf.h b/raylib/src/external/cgltf.h
--- a/raylib/src/external/cgltf.h
+++ b/raylib/src/external/cgltf.h
@@ -1,7 +1,7 @@
 /**
  * cgltf - a single-file glTF 2.0 parser written in C99.
  *
- * Version: 1.13
+ * Version: 1.14
  *
  * Website: https://github.com/jkuhlmann/cgltf
  *
@@ -395,6 +395,8 @@
 	cgltf_sampler* sampler;
 	cgltf_bool has_basisu;
 	cgltf_image* basisu_image;
+	cgltf_bool has_webp;
+	cgltf_image* webp_image;
 	cgltf_extras extras;
 	cgltf_size extensions_count;
 	cgltf_extension* extensions;
@@ -1697,8 +1699,21 @@
 	{
 		if (data->nodes[i].weights && data->nodes[i].mesh)
 		{
-			CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf);
+			CGLTF_ASSERT_IF(data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf);
 		}
+
+		if (data->nodes[i].has_mesh_gpu_instancing)
+		{
+			CGLTF_ASSERT_IF(data->nodes[i].mesh == NULL, cgltf_result_invalid_gltf);
+			CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes_count == 0, cgltf_result_invalid_gltf);
+
+			cgltf_accessor* first = data->nodes[i].mesh_gpu_instancing.attributes[0].data;
+
+			for (cgltf_size k = 0; k < data->nodes[i].mesh_gpu_instancing.attributes_count; ++k)
+			{
+				CGLTF_ASSERT_IF(data->nodes[i].mesh_gpu_instancing.attributes[k].data->count != first->count, cgltf_result_invalid_gltf);
+			}
+		}
 	}
 
 	for (cgltf_size i = 0; i < data->nodes_count; ++i)
@@ -4538,6 +4553,34 @@
 						}
 					}
 				}
+				else if (cgltf_json_strcmp(tokens + i, json_chunk, "EXT_texture_webp") == 0)
+				{
+					out_texture->has_webp = 1;
+					++i;
+					CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
+					int num_properties = tokens[i].size;
+					++i;
+
+					for (int t = 0; t < num_properties; ++t)
+					{
+						CGLTF_CHECK_KEY(tokens[i]);
+
+						if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0)
+						{
+							++i;
+							out_texture->webp_image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk));
+							++i;
+						}
+						else
+						{
+							i = cgltf_skip_json(tokens, i + 1);
+						}
+						if (i < 0)
+						{
+							return i;
+						}
+					}
+				}
 				else
 				{
 					i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture->extensions[out_texture->extensions_count++]));
@@ -6548,6 +6591,7 @@
 	{
 		CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count);
 		CGLTF_PTRFIXUP(data->textures[i].basisu_image, data->images, data->images_count);
+		CGLTF_PTRFIXUP(data->textures[i].webp_image, data->images, data->images_count);
 		CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count);
 	}
 
diff --git a/raylib/src/external/dr_flac.h b/raylib/src/external/dr_flac.h
--- a/raylib/src/external/dr_flac.h
+++ b/raylib/src/external/dr_flac.h
@@ -1,6 +1,6 @@
 /*
 FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
-dr_flac - v0.12.42 - 2023-11-02
+dr_flac - v0.13.0 - TBD
 
 David Reid - mackron@gmail.com
 
@@ -8,115 +8,6 @@
 */
 
 /*
-RELEASE NOTES - v0.12.0
-=======================
-Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs.
-
-
-Improved Client-Defined Memory Allocation
------------------------------------------
-The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
-existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom
-allocation callbacks are specified.
-
-To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this:
-
-    void* my_malloc(size_t sz, void* pUserData)
-    {
-        return malloc(sz);
-    }
-    void* my_realloc(void* p, size_t sz, void* pUserData)
-    {
-        return realloc(p, sz);
-    }
-    void my_free(void* p, void* pUserData)
-    {
-        free(p);
-    }
-
-    ...
-
-    drflac_allocation_callbacks allocationCallbacks;
-    allocationCallbacks.pUserData = &myData;
-    allocationCallbacks.onMalloc  = my_malloc;
-    allocationCallbacks.onRealloc = my_realloc;
-    allocationCallbacks.onFree    = my_free;
-    drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks);
-
-The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
-
-Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC,
-DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions.
-
-Every API that opens a drflac object now takes this extra parameter. These include the following:
-
-    drflac_open()
-    drflac_open_relaxed()
-    drflac_open_with_metadata()
-    drflac_open_with_metadata_relaxed()
-    drflac_open_file()
-    drflac_open_file_with_metadata()
-    drflac_open_memory()
-    drflac_open_memory_with_metadata()
-    drflac_open_and_read_pcm_frames_s32()
-    drflac_open_and_read_pcm_frames_s16()
-    drflac_open_and_read_pcm_frames_f32()
-    drflac_open_file_and_read_pcm_frames_s32()
-    drflac_open_file_and_read_pcm_frames_s16()
-    drflac_open_file_and_read_pcm_frames_f32()
-    drflac_open_memory_and_read_pcm_frames_s32()
-    drflac_open_memory_and_read_pcm_frames_s16()
-    drflac_open_memory_and_read_pcm_frames_f32()
-
-
-
-Optimizations
--------------
-Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly
-improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes
-advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which
-means it will be disabled when DR_FLAC_NO_CRC is used.
-
-The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in
-particular. 16-bit streams should also see some improvement.
-
-drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32
-to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths.
-
-A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo
-channel reconstruction which is the last part of the decoding process.
-
-The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when
-compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at
-compile time and the REV instruction requires ARM architecture version 6.
-
-An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling.
-
-
-Removed APIs
-------------
-The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0:
-
-    drflac_read_s32()                   -> drflac_read_pcm_frames_s32()
-    drflac_read_s16()                   -> drflac_read_pcm_frames_s16()
-    drflac_read_f32()                   -> drflac_read_pcm_frames_f32()
-    drflac_seek_to_sample()             -> drflac_seek_to_pcm_frame()
-    drflac_open_and_decode_s32()        -> drflac_open_and_read_pcm_frames_s32()
-    drflac_open_and_decode_s16()        -> drflac_open_and_read_pcm_frames_s16()
-    drflac_open_and_decode_f32()        -> drflac_open_and_read_pcm_frames_f32()
-    drflac_open_and_decode_file_s32()   -> drflac_open_file_and_read_pcm_frames_s32()
-    drflac_open_and_decode_file_s16()   -> drflac_open_file_and_read_pcm_frames_s16()
-    drflac_open_and_decode_file_f32()   -> drflac_open_file_and_read_pcm_frames_f32()
-    drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32()
-    drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16()
-    drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32()
-
-Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate
-to the old per-sample APIs. You now need to use the "pcm_frame" versions.
-*/
-
-
-/*
 Introduction
 ============
 dr_flac is a single file library. To use it, do something like the following in one .c file.
@@ -179,7 +70,7 @@
 
 The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style
 streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs:
-    
+
     `drflac_open_relaxed()`
     `drflac_open_with_metadata_relaxed()`
 
@@ -234,8 +125,8 @@
 #define DRFLAC_XSTRINGIFY(x)     DRFLAC_STRINGIFY(x)
 
 #define DRFLAC_VERSION_MAJOR     0
-#define DRFLAC_VERSION_MINOR     12
-#define DRFLAC_VERSION_REVISION  42
+#define DRFLAC_VERSION_MINOR     13
+#define DRFLAC_VERSION_REVISION  0
 #define DRFLAC_VERSION_STRING    DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION)
 
 #include <stddef.h> /* For size_t. */
@@ -348,11 +239,11 @@
 #define DRFLAC_64BIT
 #endif
 
-#if defined(__x86_64__) || defined(_M_X64)
+#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
     #define DRFLAC_X64
 #elif defined(__i386) || defined(_M_IX86)
     #define DRFLAC_X86
-#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
+#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
     #define DRFLAC_ARM
 #endif
 /* End Architecture Detection */
@@ -406,8 +297,9 @@
 
 typedef enum
 {
-    drflac_seek_origin_start,
-    drflac_seek_origin_current
+    DRFLAC_SEEK_SET,
+    DRFLAC_SEEK_CUR,
+    DRFLAC_SEEK_END
 } drflac_seek_origin;
 
 /* The order of members in this structure is important because we map this directly to the raw data within the SEEKTABLE metadata block. */
@@ -547,7 +439,7 @@
     The number of bytes to move, relative to the origin. Will never be negative.
 
 origin (in)
-    The origin of the seek - the current position or the start of the stream.
+    The origin of the seek - the current position, the start of the stream, or the end of the stream.
 
 
 Return Value
@@ -557,8 +449,7 @@
 
 Remarks
 -------
-The offset will never be negative. Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be
-either drflac_seek_origin_start or drflac_seek_origin_current.
+Seeking relative to the start and the current position must always be supported. If seeking from the end of the stream is not supported, return DRFLAC_FALSE.
 
 When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of the FLAC stream. This needs to be detected
 and handled by returning DRFLAC_FALSE.
@@ -566,6 +457,25 @@
 typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
 
 /*
+Callback for when the current position in the stream needs to be retrieved.
+
+
+Parameters
+----------
+pUserData (in)
+    The user data that was passed to drflac_open() and family.
+
+pCursor (out)
+    A pointer to a variable to receive the current position in the stream.
+
+
+Return Value
+------------
+Whether or not the operation was successful.
+*/
+typedef drflac_bool32 (* drflac_tell_proc)(void* pUserData, drflac_int64* pCursor);
+
+/*
 Callback for when a metadata block is read.
 
 
@@ -603,6 +513,9 @@
     /* The function to call when the current read position needs to be moved. */
     drflac_seek_proc onSeek;
 
+    /* The function to call when the current read position needs to be retrieved. */
+    drflac_tell_proc onTell;
+
     /* The user data to pass around to onRead and onSeek. */
     void* pUserData;
 
@@ -828,7 +741,7 @@
 drflac_open_with_metadata()
 drflac_close()
 */
-DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /*
 Opens a FLAC stream with relaxed validation of the header block.
@@ -869,7 +782,7 @@
 
 Use `drflac_open_with_metadata_relaxed()` if you need access to metadata.
 */
-DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /*
 Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.).
@@ -926,7 +839,7 @@
 drflac_open()
 drflac_close()
 */
-DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /*
 The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present.
@@ -936,7 +849,7 @@
 drflac_open_with_metadata()
 drflac_open_relaxed()
 */
-DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /*
 Closes the given FLAC decoder.
@@ -1234,13 +1147,13 @@
 
 Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
 */
-DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */
-DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 /* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */
-DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
 
 #ifndef DR_FLAC_NO_STDIO
 /* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */
@@ -2960,25 +2873,25 @@
     */
     if (offsetFromStart > 0x7FFFFFFF) {
         drflac_uint64 bytesRemaining = offsetFromStart;
-        if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+        if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) {
             return DRFLAC_FALSE;
         }
         bytesRemaining -= 0x7FFFFFFF;
 
         while (bytesRemaining > 0x7FFFFFFF) {
-            if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+            if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;
             }
             bytesRemaining -= 0x7FFFFFFF;
         }
 
         if (bytesRemaining > 0) {
-            if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
+            if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;
             }
         }
     } else {
-        if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
+        if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, DRFLAC_SEEK_SET)) {
             return DRFLAC_FALSE;
         }
     }
@@ -5393,6 +5306,12 @@
         return DRFLAC_FALSE;
     }
 
+    /*
+    Default to 0 for the LPC order. It's important that we always set this to 0 for non LPC
+    and FIXED subframes because we'll be using it in a generic validation check later.
+    */
+    pSubframe->lpcOrder = 0;
+
     type = (header & 0x7E) >> 1;
     if (type == 0) {
         pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
@@ -5465,6 +5384,18 @@
 
     pSubframe->pSamplesS32 = pDecodedSamplesOut;
 
+    /*
+    pDecodedSamplesOut will be pointing to a buffer that was allocated with enough memory to store
+    maxBlockSizeInPCMFrames samples (as specified in the FLAC header). We need to guard against an
+    overflow here. At a higher level we are checking maxBlockSizeInPCMFrames from the header, but
+    here we need to do an additional check to ensure this frame's block size fully encompasses any
+    warmup samples which is determined by the LPC order. For non LPC and FIXED subframes, the LPC
+    order will be have been set to 0 in drflac__read_subframe_header().
+    */
+    if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) {
+        return DRFLAC_FALSE;
+    }
+
     switch (pSubframe->subframeType)
     {
         case DRFLAC_SUBFRAME_CONSTANT:
@@ -6312,6 +6243,7 @@
 {
     drflac_read_proc onRead;
     drflac_seek_proc onSeek;
+    drflac_tell_proc onTell;
     drflac_meta_proc onMeta;
     drflac_container container;
     void* pUserData;
@@ -6479,7 +6411,7 @@
 }
 
 
-static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks)
+static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks)
 {
     /*
     We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that
@@ -6489,6 +6421,8 @@
     drflac_uint64 seektablePos   = 0;
     drflac_uint32 seektableSize  = 0;
 
+    (void)onTell;
+
     for (;;) {
         drflac_metadata metadata;
         drflac_uint8 isLastBlock = 0;
@@ -6702,10 +6636,10 @@
 
                             /* Skip to the index point count */
                             pRunningData += 35;
-                            
+
                             indexCount = pRunningData[0];
                             pRunningData += 1;
-                            
+
                             bufferSize += indexCount * sizeof(drflac_cuesheet_track_index);
 
                             /* Quick validation check. */
@@ -6840,7 +6774,7 @@
                     metadata.data.padding.unused = 0;
 
                     /* Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. */
-                    if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+                    if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
                         isLastBlock = DRFLAC_TRUE;  /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
                     } else {
                         onMeta(pUserDataMD, &metadata);
@@ -6852,7 +6786,7 @@
             {
                 /* Invalid chunk. Just skip over this one. */
                 if (onMeta) {
-                    if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+                    if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
                         isLastBlock = DRFLAC_TRUE;  /* An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. */
                     }
                 }
@@ -6886,7 +6820,7 @@
 
         /* If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. */
         if (onMeta == NULL && blockSize > 0) {
-            if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+            if (!onSeek(pUserData, blockSize, DRFLAC_SEEK_CUR)) {
                 isLastBlock = DRFLAC_TRUE;
             }
         }
@@ -7220,6 +7154,7 @@
 {
     drflac_read_proc onRead;                /* The original onRead callback from drflac_open() and family. */
     drflac_seek_proc onSeek;                /* The original onSeek callback from drflac_open() and family. */
+    drflac_tell_proc onTell;                /* The original onTell callback from drflac_open() and family. */
     void* pUserData;                        /* The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. */
     drflac_uint64 currentBytePos;           /* The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. */
     drflac_uint64 firstBytePos;             /* The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. */
@@ -7241,32 +7176,32 @@
 
 static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
 {
-    if (origin == drflac_seek_origin_start) {
+    if (origin == DRFLAC_SEEK_SET) {
         if (offset <= 0x7FFFFFFF) {
-            if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
+            if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_SET)) {
                 return DRFLAC_FALSE;
             }
             oggbs->currentBytePos = offset;
 
             return DRFLAC_TRUE;
         } else {
-            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_SET)) {
                 return DRFLAC_FALSE;
             }
             oggbs->currentBytePos = offset;
 
-            return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
+            return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, DRFLAC_SEEK_CUR);
         }
     } else {
         while (offset > 0x7FFFFFFF) {
-            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+            if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;
             }
             oggbs->currentBytePos += 0x7FFFFFFF;
             offset -= 0x7FFFFFFF;
         }
 
-        if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) {    /* <-- Safe cast thanks to the loop above. */
+        if (!oggbs->onSeek(oggbs->pUserData, (int)offset, DRFLAC_SEEK_CUR)) {    /* <-- Safe cast thanks to the loop above. */
             return DRFLAC_FALSE;
         }
         oggbs->currentBytePos += offset;
@@ -7298,7 +7233,7 @@
 
         if (header.serialNumber != oggbs->serialNumber) {
             /* It's not a FLAC page. Skip it. */
-            if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
+            if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;
             }
             continue;
@@ -7384,7 +7319,7 @@
         At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
         want to load the next page and keep searching for the end of the packet.
         */
-        drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
+        drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, DRFLAC_SEEK_CUR);
         oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
 
         if (atEndOfPage) {
@@ -7462,8 +7397,8 @@
     DRFLAC_ASSERT(offset >= 0);  /* <-- Never seek backwards. */
 
     /* Seeking is always forward which makes things a lot simpler. */
-    if (origin == drflac_seek_origin_start) {
-        if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
+    if (origin == DRFLAC_SEEK_SET) {
+        if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, DRFLAC_SEEK_SET)) {
             return DRFLAC_FALSE;
         }
 
@@ -7471,39 +7406,51 @@
             return DRFLAC_FALSE;
         }
 
-        return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
-    }
-
-    DRFLAC_ASSERT(origin == drflac_seek_origin_current);
-
-    while (bytesSeeked < offset) {
-        int bytesRemainingToSeek = offset - bytesSeeked;
-        DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
+        return drflac__on_seek_ogg(pUserData, offset, DRFLAC_SEEK_CUR);
+    } else if (origin == DRFLAC_SEEK_CUR) {
+        while (bytesSeeked < offset) {
+            int bytesRemainingToSeek = offset - bytesSeeked;
+            DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
 
-        if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
-            bytesSeeked += bytesRemainingToSeek;
-            (void)bytesSeeked;  /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */
-            oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
-            break;
-        }
+            if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
+                bytesSeeked += bytesRemainingToSeek;
+                (void)bytesSeeked;  /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */
+                oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
+                break;
+            }
 
-        /* If we get here it means some of the requested data is contained in the next pages. */
-        if (oggbs->bytesRemainingInPage > 0) {
-            bytesSeeked += (int)oggbs->bytesRemainingInPage;
-            oggbs->bytesRemainingInPage = 0;
-        }
+            /* If we get here it means some of the requested data is contained in the next pages. */
+            if (oggbs->bytesRemainingInPage > 0) {
+                bytesSeeked += (int)oggbs->bytesRemainingInPage;
+                oggbs->bytesRemainingInPage = 0;
+            }
 
-        DRFLAC_ASSERT(bytesRemainingToSeek > 0);
-        if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
-            /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */
-            return DRFLAC_FALSE;
+            DRFLAC_ASSERT(bytesRemainingToSeek > 0);
+            if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
+                /* Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. */
+                return DRFLAC_FALSE;
+            }
         }
+    } else if (origin == DRFLAC_SEEK_END) {
+        /* Seeking to the end is not supported. */
+        return DRFLAC_FALSE;
     }
 
     return DRFLAC_TRUE;
 }
 
+static drflac_bool32 drflac__on_tell_ogg(void* pUserData, drflac_int64* pCursor)
+{
+    /*
+    Not implemented for Ogg containers because we don't currently track the byte position of the logical bitstream. To support this, we'll need
+    to track the position in drflac__on_read_ogg and drflac__on_seek_ogg.
+    */
+    (void)pUserData;
+    (void)pCursor;
+    return DRFLAC_FALSE;
+}
 
+
 static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
 {
     drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
@@ -7525,7 +7472,7 @@
     runningGranulePosition = 0;
     for (;;) {
         if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
-            drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+            drflac_oggbs__seek_physical(oggbs, originalBytePos, DRFLAC_SEEK_SET);
             return DRFLAC_FALSE;   /* Never did find that sample... */
         }
 
@@ -7559,7 +7506,7 @@
     a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
     we find the one containing the target sample.
     */
-    if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
+    if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, DRFLAC_SEEK_SET)) {
         return DRFLAC_FALSE;
     }
     if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
@@ -7726,7 +7673,7 @@
                     The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
                     be handling it in a generic way based on the serial number and packet types.
                     */
-                    if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
+                    if (!onSeek(pUserData, 2, DRFLAC_SEEK_CUR)) {
                         return DRFLAC_FALSE;
                     }
 
@@ -7783,18 +7730,18 @@
                     }
                 } else {
                     /* Not a FLAC header. Skip it. */
-                    if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+                    if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) {
                         return DRFLAC_FALSE;
                     }
                 }
             } else {
                 /* Not a FLAC header. Seek past the entire page and move on to the next. */
-                if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+                if (!onSeek(pUserData, bytesRemainingInPage, DRFLAC_SEEK_CUR)) {
                     return DRFLAC_FALSE;
                 }
             }
         } else {
-            if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
+            if (!onSeek(pUserData, pageBodySize, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;
             }
         }
@@ -7819,18 +7766,19 @@
 }
 #endif
 
-static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
+static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
 {
     drflac_bool32 relaxed;
     drflac_uint8 id[4];
 
-    if (pInit == NULL || onRead == NULL || onSeek == NULL) {
+    if (pInit == NULL || onRead == NULL || onSeek == NULL) {    /* <-- onTell is optional. */
         return DRFLAC_FALSE;
     }
 
     DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
     pInit->onRead       = onRead;
     pInit->onSeek       = onSeek;
+    pInit->onTell       = onTell;
     pInit->onMeta       = onMeta;
     pInit->container    = container;
     pInit->pUserData    = pUserData;
@@ -7838,6 +7786,7 @@
 
     pInit->bs.onRead    = onRead;
     pInit->bs.onSeek    = onSeek;
+    pInit->bs.onTell    = onTell;
     pInit->bs.pUserData = pUserData;
     drflac__reset_cache(&pInit->bs);
 
@@ -7870,7 +7819,7 @@
                 headerSize += 10;
             }
 
-            if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
+            if (!onSeek(pUserData, headerSize, DRFLAC_SEEK_CUR)) {
                 return DRFLAC_FALSE;    /* Failed to seek past the tag. */
             }
             pInit->runningFilePos += headerSize;
@@ -7922,7 +7871,7 @@
 }
 
 
-static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
+static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac_init_info init;
     drflac_uint32 allocationSize;
@@ -7940,7 +7889,7 @@
     /* CPU support first. */
     drflac__init_cpu_caps();
 
-    if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
+    if (!drflac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) {
         return NULL;
     }
 
@@ -7996,6 +7945,7 @@
         DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
         pOggbs->onRead = onRead;
         pOggbs->onSeek = onSeek;
+        pOggbs->onTell = onTell;
         pOggbs->pUserData = pUserData;
         pOggbs->currentBytePos = init.oggFirstBytePos;
         pOggbs->firstBytePos = init.oggFirstBytePos;
@@ -8016,17 +7966,19 @@
     if (init.hasMetadataBlocks) {
         drflac_read_proc onReadOverride = onRead;
         drflac_seek_proc onSeekOverride = onSeek;
+        drflac_tell_proc onTellOverride = onTell;
         void* pUserDataOverride = pUserData;
 
 #ifndef DR_FLAC_NO_OGG
         if (init.container == drflac_container_ogg) {
             onReadOverride = drflac__on_read_ogg;
             onSeekOverride = drflac__on_seek_ogg;
+            onTellOverride = drflac__on_tell_ogg;
             pUserDataOverride = (void*)pOggbs;
         }
 #endif
 
-        if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
+        if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
         #ifndef DR_FLAC_NO_OGG
             drflac__free_from_callbacks(pOggbs, &allocationCallbacks);
         #endif
@@ -8061,6 +8013,7 @@
         /* The Ogg bistream needs to be layered on top of the original bitstream. */
         pFlac->bs.onRead = drflac__on_read_ogg;
         pFlac->bs.onSeek = drflac__on_seek_ogg;
+        pFlac->bs.onTell = drflac__on_tell_ogg;
         pFlac->bs.pUserData = (void*)pInternalOggbs;
         pFlac->_oggbs = (void*)pInternalOggbs;
     }
@@ -8087,7 +8040,7 @@
             DRFLAC_ASSERT(pFlac->bs.onRead != NULL);
 
             /* Seek to the seektable, then just read directly into our seektable buffer. */
-            if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
+            if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, DRFLAC_SEEK_SET)) {
                 drflac_uint32 iSeekpoint;
 
                 for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
@@ -8105,7 +8058,7 @@
                 }
 
                 /* We need to seek back to where we were. If this fails it's a critical error. */
-                if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
+                if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, DRFLAC_SEEK_SET)) {
                     drflac__free_from_callbacks(pFlac, &allocationCallbacks);
                     return NULL;
                 }
@@ -8276,7 +8229,7 @@
     #ifdef ENOSYS
         case ENOSYS: return DRFLAC_NOT_IMPLEMENTED;
     #endif
-    #ifdef ENOTEMPTY
+    #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST   /* In AIX, ENOTEMPTY and EEXIST use the same value. */
         case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY;
     #endif
     #ifdef ELOOP
@@ -8727,12 +8680,42 @@
 
 static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
 {
-    DRFLAC_ASSERT(offset >= 0);  /* <-- Never seek backwards. */
+    int whence = SEEK_SET;
+    if (origin == DRFLAC_SEEK_CUR) {
+        whence = SEEK_CUR;
+    } else if (origin == DRFLAC_SEEK_END) {
+        whence = SEEK_END;
+    }
 
-    return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+    return fseek((FILE*)pUserData, offset, whence) == 0;
 }
 
+static drflac_bool32 drflac__on_tell_stdio(void* pUserData, drflac_int64* pCursor)
+{
+    FILE* pFileStdio = (FILE*)pUserData;
+    drflac_int64 result;
 
+    /* These were all validated at a higher level. */
+    DRFLAC_ASSERT(pFileStdio != NULL);
+    DRFLAC_ASSERT(pCursor    != NULL);
+
+#if defined(_WIN32)
+    #if defined(_MSC_VER) && _MSC_VER > 1200
+        result = _ftelli64(pFileStdio);
+    #else
+        result = ftell(pFileStdio);
+    #endif
+#else
+    result = ftell(pFileStdio);
+#endif
+
+    *pCursor = result;
+
+    return DRFLAC_TRUE;
+}
+
+
+
 DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac* pFlac;
@@ -8742,7 +8725,7 @@
         return NULL;
     }
 
-    pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+    pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
     if (pFlac == NULL) {
         fclose(pFile);
         return NULL;
@@ -8761,7 +8744,7 @@
         return NULL;
     }
 
-    pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+    pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
     if (pFlac == NULL) {
         fclose(pFile);
         return NULL;
@@ -8780,7 +8763,7 @@
         return NULL;
     }
 
-    pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         fclose(pFile);
         return pFlac;
@@ -8799,7 +8782,7 @@
         return NULL;
     }
 
-    pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, drflac__on_tell_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         fclose(pFile);
         return pFlac;
@@ -8834,31 +8817,48 @@
 static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
 {
     drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+    drflac_int64 newCursor;
 
     DRFLAC_ASSERT(memoryStream != NULL);
-    DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */
 
-    if (offset > (drflac_int64)memoryStream->dataSize) {
+    newCursor = memoryStream->currentReadPos;
+
+    if (origin == DRFLAC_SEEK_SET) {
+        newCursor = 0;
+    } else if (origin == DRFLAC_SEEK_CUR) {
+        newCursor = (drflac_int64)memoryStream->currentReadPos;
+    } else if (origin == DRFLAC_SEEK_END) {
+        newCursor = (drflac_int64)memoryStream->dataSize;
+    } else {
+        DRFLAC_ASSERT(!"Invalid seek origin");
         return DRFLAC_FALSE;
     }
 
-    if (origin == drflac_seek_origin_current) {
-        if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
-            memoryStream->currentReadPos += offset;
-        } else {
-            return DRFLAC_FALSE;  /* Trying to seek too far forward. */
-        }
-    } else {
-        if ((drflac_uint32)offset <= memoryStream->dataSize) {
-            memoryStream->currentReadPos = offset;
-        } else {
-            return DRFLAC_FALSE;  /* Trying to seek too far forward. */
-        }
+    newCursor += offset;
+
+    if (newCursor < 0) {
+        return DRFLAC_FALSE;  /* Trying to seek prior to the start of the buffer. */
     }
+    if ((size_t)newCursor > memoryStream->dataSize) {
+        return DRFLAC_FALSE;  /* Trying to seek beyond the end of the buffer. */
+    }
 
+    memoryStream->currentReadPos = (size_t)newCursor;
+
     return DRFLAC_TRUE;
 }
 
+static drflac_bool32 drflac__on_tell_memory(void* pUserData, drflac_int64* pCursor)
+{
+    drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+
+    DRFLAC_ASSERT(memoryStream != NULL);
+    DRFLAC_ASSERT(pCursor != NULL);
+
+    *pCursor = (drflac_int64)memoryStream->currentReadPos;
+    return DRFLAC_TRUE;
+}
+
 DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac__memory_stream memoryStream;
@@ -8867,7 +8867,7 @@
     memoryStream.data = (const drflac_uint8*)pData;
     memoryStream.dataSize = dataSize;
     memoryStream.currentReadPos = 0;
-    pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
+    pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, &memoryStream, pAllocationCallbacks);
     if (pFlac == NULL) {
         return NULL;
     }
@@ -8898,7 +8898,7 @@
     memoryStream.data = (const drflac_uint8*)pData;
     memoryStream.dataSize = dataSize;
     memoryStream.currentReadPos = 0;
-    pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, drflac__on_tell_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         return NULL;
     }
@@ -8923,22 +8923,22 @@
 
 
 
-DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
-    return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
+    return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
 }
-DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
-    return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
+    return drflac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks);
 }
 
-DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
-    return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
+    return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
 }
-DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
-    return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
+    return drflac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
 }
 
 DRFLAC_API void drflac_close(drflac* pFlac)
@@ -11770,7 +11770,7 @@
 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
 DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
 
-DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac* pFlac;
 
@@ -11784,7 +11784,7 @@
         *totalPCMFrameCountOut = 0;
     }
 
-    pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         return NULL;
     }
@@ -11792,7 +11792,7 @@
     return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
 }
 
-DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac* pFlac;
 
@@ -11806,7 +11806,7 @@
         *totalPCMFrameCountOut = 0;
     }
 
-    pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         return NULL;
     }
@@ -11814,7 +11814,7 @@
     return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
 }
 
-DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
 {
     drflac* pFlac;
 
@@ -11828,7 +11828,7 @@
         *totalPCMFrameCountOut = 0;
     }
 
-    pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+    pFlac = drflac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
     if (pFlac == NULL) {
         return NULL;
     }
@@ -12077,6 +12077,26 @@
 /*
 REVISION HISTORY
 ================
+v0.13.0 - TBD
+  - API CHANGE: Seek origin enums have been renamed to match the naming convention used by other dr_libs libraries:
+    - drflac_seek_origin_start   -> DRFLAC_SEEK_SET
+    - drflac_seek_origin_current -> DRFLAC_SEEK_CUR
+    - DRFLAC_SEEK_END (new)
+  - API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you should now detect and handle `DRFLAC_SEEK_END`. If seeking to the end is not supported, return `DRFLAC_FALSE`. If you only use `*_open_file()` or `*_open_memory()`, you need not change anything.
+  - API CHANGE: An `onTell` callback has been added to the following functions:
+    - drflac_open()
+    - drflac_open_relaxed()
+    - drflac_open_with_metadata()
+    - drflac_open_with_metadata_relaxed()
+    - drflac_open_and_read_pcm_frames_s32()
+    - drflac_open_and_read_pcm_frames_s16()
+    - drflac_open_and_read_pcm_frames_f32()
+  - Fix compilation for AIX OS.
+
+v0.12.43 - 2024-12-17
+  - Fix a possible buffer overflow during decoding.
+  - Improve detection of ARM64EC
+
 v0.12.42 - 2023-11-02
   - Fix build for ARMv6-M.
   - Fix a compilation warning with GCC.
diff --git a/raylib/src/external/dr_mp3.h b/raylib/src/external/dr_mp3.h
--- a/raylib/src/external/dr_mp3.h
+++ b/raylib/src/external/dr_mp3.h
@@ -1,6 +1,6 @@
 /*
 MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
-dr_mp3 - v0.6.38 - 2023-11-02
+dr_mp3 - v0.7.0 - TBD
 
 David Reid - mackron@gmail.com
 
@@ -10,30 +10,7 @@
 */
 
 /*
-RELEASE NOTES - VERSION 0.6
-===========================
-Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been
-removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done
-yourself.
-
-
-Changes to Initialization
--------------------------
-Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been
-removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed.
-
-    `drmp3_init()`
-    `drmp3_init_memory()`
-    `drmp3_init_file()`
-
-
-Miscellaneous Changes
----------------------
-Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API.
-*/
-
-/*
-Introducation
+Introduction
 =============
 dr_mp3 is a single file library. To use it, do something like the following in one .c file.
 
@@ -94,8 +71,8 @@
 #define DRMP3_XSTRINGIFY(x)     DRMP3_STRINGIFY(x)
 
 #define DRMP3_VERSION_MAJOR     0
-#define DRMP3_VERSION_MINOR     6
-#define DRMP3_VERSION_REVISION  38
+#define DRMP3_VERSION_MINOR     7
+#define DRMP3_VERSION_REVISION  0
 #define DRMP3_VERSION_STRING    DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
 
 #include <stddef.h> /* For size_t. */
@@ -124,7 +101,7 @@
         #pragma GCC diagnostic pop
     #endif
 #endif
-#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__)
     typedef drmp3_uint64        drmp3_uintptr;
 #else
     typedef drmp3_uint32        drmp3_uintptr;
@@ -133,6 +110,9 @@
 typedef drmp3_uint32            drmp3_bool32;
 #define DRMP3_TRUE              1
 #define DRMP3_FALSE             0
+
+/* Weird shifting syntax is for VC6 compatibility. */
+#define DRMP3_UINT64_MAX        (((drmp3_uint64)0xFFFFFFFF << 32) | (drmp3_uint64)0xFFFFFFFF)
 /* End Sized Types */
 
 /* Decorations */
@@ -154,7 +134,7 @@
             #endif
         #endif
 
-        #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
+        #if defined(DR_MP3_IMPLEMENTATION)
             #define DRMP3_API  DRMP3_DLL_EXPORT
         #else
             #define DRMP3_API  DRMP3_DLL_IMPORT
@@ -279,7 +259,7 @@
 */
 typedef struct
 {
-    int frame_bytes, channels, hz, layer, bitrate_kbps;
+    int frame_bytes, channels, sample_rate, layer, bitrate_kbps;
 } drmp3dec_frame_info;
 
 typedef struct
@@ -306,8 +286,9 @@
 */
 typedef enum
 {
-    drmp3_seek_origin_start,
-    drmp3_seek_origin_current
+    DRMP3_SEEK_SET,
+    DRMP3_SEEK_CUR,
+    DRMP3_SEEK_END
 } drmp3_seek_origin;
 
 typedef struct
@@ -318,10 +299,27 @@
     drmp3_uint16 pcmFramesToDiscard;    /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */
 } drmp3_seek_point;
 
+typedef enum
+{
+    DRMP3_METADATA_TYPE_ID3V1,
+    DRMP3_METADATA_TYPE_ID3V2,
+    DRMP3_METADATA_TYPE_APE,
+    DRMP3_METADATA_TYPE_XING,
+    DRMP3_METADATA_TYPE_VBRI
+} drmp3_metadata_type;
+
+typedef struct
+{
+    drmp3_metadata_type type;
+    const void* pRawData;               /* A pointer to the raw data. */
+    size_t rawDataSize;
+} drmp3_metadata;
+
+
 /*
 Callback for when data is read. Return value is the number of bytes actually read.
 
-pUserData   [in]  The user data that was passed to drmp3_init(), drmp3_open() and family.
+pUserData   [in]  The user data that was passed to drmp3_init(), and family.
 pBufferOut  [out] The output buffer.
 bytesToRead [in]  The number of bytes to read.
 
@@ -335,17 +333,33 @@
 /*
 Callback for when data needs to be seeked.
 
-pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
-offset    [in] The number of bytes to move, relative to the origin. Will never be negative.
-origin    [in] The origin of the seek - the current position or the start of the stream.
+pUserData [in] The user data that was passed to drmp3_init(), and family.
+offset    [in] The number of bytes to move, relative to the origin. Can be negative.
+origin    [in] The origin of the seek.
 
 Returns whether or not the seek was successful.
-
-Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
-will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
 */
 typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
 
+/*
+Callback for retrieving the current cursor position.
+
+pUserData [in]  The user data that was passed to drmp3_init(), and family.
+pCursor   [out] The cursor position in bytes from the start of the stream.
+
+Returns whether or not the cursor position was successfully retrieved.
+*/
+typedef drmp3_bool32 (* drmp3_tell_proc)(void* pUserData, drmp3_int64* pCursor);
+
+
+/*
+Callback for when metadata is read.
+
+Only the raw data is provided. The client is responsible for parsing the contents of the data themsevles.
+*/
+typedef void (* drmp3_meta_proc)(void* pUserData, const drmp3_metadata* pMetadata);
+
+
 typedef struct
 {
     drmp3_uint32 channels;
@@ -359,22 +373,31 @@
     drmp3_uint32 sampleRate;
     drmp3_read_proc onRead;
     drmp3_seek_proc onSeek;
+    drmp3_meta_proc onMeta;
     void* pUserData;
+    void* pUserDataMeta;
     drmp3_allocation_callbacks allocationCallbacks;
     drmp3_uint32 mp3FrameChannels;      /* The number of channels in the currently loaded MP3 frame. Internal use only. */
     drmp3_uint32 mp3FrameSampleRate;    /* The sample rate of the currently loaded MP3 frame. Internal use only. */
     drmp3_uint32 pcmFramesConsumedInMP3Frame;
     drmp3_uint32 pcmFramesRemainingInMP3Frame;
     drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME];  /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */
-    drmp3_uint64 currentPCMFrame;       /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */
+    drmp3_uint64 currentPCMFrame;       /* The current PCM frame, globally. */
     drmp3_uint64 streamCursor;          /* The current byte the decoder is sitting on in the raw stream. */
+    drmp3_uint64 streamLength;          /* The length of the stream in bytes. dr_mp3 will not read beyond this. If a ID3v1 or APE tag is present, this will be set to the first byte of the tag. */
+    drmp3_uint64 streamStartOffset;     /* The offset of the start of the MP3 data. This is used for skipping ID3v2 and VBR tags. */
     drmp3_seek_point* pSeekPoints;      /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */
     drmp3_uint32 seekPointCount;        /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */
+    drmp3_uint32 delayInPCMFrames;
+    drmp3_uint32 paddingInPCMFrames;
+    drmp3_uint64 totalPCMFrameCount;    /* Set to DRMP3_UINT64_MAX if the length is unknown. Includes delay and padding. */
+    drmp3_bool32 isVBR;
+    drmp3_bool32 isCBR;
     size_t dataSize;
     size_t dataCapacity;
     size_t dataConsumed;
     drmp3_uint8* pData;
-    drmp3_bool32 atEnd : 1;
+    drmp3_bool32 atEnd;
     struct
     {
         const drmp3_uint8* pData;
@@ -388,6 +411,7 @@
 
 onRead    [in]           The function to call when data needs to be read from the client.
 onSeek    [in]           The function to call when the read position of the client data needs to move.
+onTell    [in]           The function to call when the read position of the client data needs to be retrieved.
 pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
 
 Returns true if successful; false otherwise.
@@ -396,7 +420,7 @@
 
 See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
 */
-DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
+DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
 
 /*
 Initializes an MP3 decoder from a block of memory.
@@ -406,6 +430,7 @@
 
 The buffer should contain the contents of the entire MP3 file.
 */
+DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks);
 DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
 
 #ifndef DR_MP3_NO_STDIO
@@ -416,6 +441,9 @@
 objects because the operating system may restrict the number of file handles an application can have open at
 any given time.
 */
+DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks);
+DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
 DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
 DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
 #endif
@@ -495,8 +523,8 @@
 
 Free the returned pointer with drmp3_free().
 */
-DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
-DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
 
 DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
 DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
@@ -529,7 +557,7 @@
 
  ************************************************************************************************************************************************************
  ************************************************************************************************************************************************************/
-#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
+#if defined(DR_MP3_IMPLEMENTATION)
 #ifndef dr_mp3_c
 #define dr_mp3_c
 
@@ -604,7 +632,7 @@
 
 #if !defined(DR_MP3_NO_SIMD)
 
-#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
+#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
 /* x64 always have SSE2, arm64 always have neon, no need for generic code */
 #define DR_MP3_ONLY_SIMD
 #endif
@@ -680,7 +708,7 @@
     return g_have_simd - 1;
 #endif
 }
-#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
+#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
 #include <arm_neon.h>
 #define DRMP3_HAVE_SSE 0
 #define DRMP3_HAVE_SIMD 1
@@ -713,7 +741,7 @@
 
 #endif
 
-#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(__ARM_ARCH_6M__)
+#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__)
 #define DRMP3_HAVE_ARMV6 1
 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a)
 {
@@ -1985,8 +2013,8 @@
     s32 -= (s32 < 0);
     s = (drmp3_int16)drmp3_clip_int16_arm(s32);
 #else
-    if (sample >=  32766.5) return (drmp3_int16) 32767;
-    if (sample <= -32767.5) return (drmp3_int16)-32768;
+    if (sample >=  32766.5f) return (drmp3_int16) 32767;
+    if (sample <= -32767.5f) return (drmp3_int16)-32768;
     s = (drmp3_int16)(sample + .5f);
     s -= (s < 0);   /* away from zero, to be compliant */
 #endif
@@ -2296,7 +2324,7 @@
     DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE);
     info->frame_bytes = i + frame_size;
     info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
-    info->hz = drmp3_hdr_sample_rate_hz(hdr);
+    info->sample_rate = drmp3_hdr_sample_rate_hz(hdr);
     info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
     info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
 
@@ -2404,9 +2432,9 @@
     for(; i < num_samples; i++)
     {
         float sample = in[i] * 32768.0f;
-        if (sample >=  32766.5)
+        if (sample >=  32766.5f)
             out[i] = (drmp3_int16) 32767;
-        else if (sample <= -32767.5)
+        else if (sample <= -32767.5f)
             out[i] = (drmp3_int16)-32768;
         else
         {
@@ -2589,22 +2617,56 @@
 
 static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
 {
-    size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
+    size_t bytesRead;
+
+    DRMP3_ASSERT(pMP3         != NULL);
+    DRMP3_ASSERT(pMP3->onRead != NULL);
+
+    /*
+    Don't try reading 0 bytes from the callback. This can happen when the stream is clamped against
+    ID3v1 or APE tags at the end of the stream.
+    */
+    if (bytesToRead == 0) {
+        return 0;
+    }
+
+    bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
     pMP3->streamCursor += bytesRead;
+
     return bytesRead;
 }
 
+static size_t drmp3__on_read_clamped(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
+{
+    DRMP3_ASSERT(pMP3         != NULL);
+    DRMP3_ASSERT(pMP3->onRead != NULL);
+
+    if (pMP3->streamLength == DRMP3_UINT64_MAX) {
+        return drmp3__on_read(pMP3, pBufferOut, bytesToRead);
+    } else {
+        drmp3_uint64 bytesRemaining;
+
+        bytesRemaining = (pMP3->streamLength - pMP3->streamCursor);
+        if (bytesToRead >         bytesRemaining) {
+            bytesToRead = (size_t)bytesRemaining;
+        }
+    
+        return drmp3__on_read(pMP3, pBufferOut, bytesToRead);
+    }
+}
+
 static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
 {
     DRMP3_ASSERT(offset >= 0);
+    DRMP3_ASSERT(origin == DRMP3_SEEK_SET || origin == DRMP3_SEEK_CUR);
 
     if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
         return DRMP3_FALSE;
     }
 
-    if (origin == drmp3_seek_origin_start) {
+    if (origin == DRMP3_SEEK_SET) {
         pMP3->streamCursor = (drmp3_uint64)offset;
-    } else {
+    } else{
         pMP3->streamCursor += offset;
     }
 
@@ -2617,21 +2679,20 @@
         return drmp3__on_seek(pMP3, (int)offset, origin);
     }
 
-
     /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */
-    if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
+    if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_SET)) {
         return DRMP3_FALSE;
     }
 
     offset -= 0x7FFFFFFF;
     while (offset > 0) {
         if (offset <= 0x7FFFFFFF) {
-            if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
+            if (!drmp3__on_seek(pMP3, (int)offset, DRMP3_SEEK_CUR)) {
                 return DRMP3_FALSE;
             }
             offset = 0;
         } else {
-            if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
+            if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, DRMP3_SEEK_CUR)) {
                 return DRMP3_FALSE;
             }
             offset -= 0x7FFFFFFF;
@@ -2641,8 +2702,22 @@
     return DRMP3_TRUE;
 }
 
+static void drmp3__on_meta(drmp3* pMP3, drmp3_metadata_type type, const void* pRawData, size_t rawDataSize)
+{
+    if (pMP3->onMeta) {
+        drmp3_metadata metadata;
 
-static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
+        DRMP3_ZERO_OBJECT(&metadata);
+        metadata.type        = type;
+        metadata.pRawData    = pRawData;
+        metadata.rawDataSize = rawDataSize;
+
+        pMP3->onMeta(pMP3->pUserDataMeta, &metadata);
+    }
+}
+
+
+static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData)
 {
     drmp3_uint32 pcmFramesRead = 0;
 
@@ -2682,7 +2757,7 @@
                 pMP3->dataCapacity = newDataCap;
             }
 
-            bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+            bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
             if (bytesRead == 0) {
                 if (pMP3->dataSize == 0) {
                     pMP3->atEnd = DRMP3_TRUE;
@@ -2709,10 +2784,8 @@
         pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);    /* <-- Safe size_t -> int conversion thanks to the check above. */
 
         /* Consume the data. */
-        if (info.frame_bytes > 0) {
-            pMP3->dataConsumed += (size_t)info.frame_bytes;
-            pMP3->dataSize     -= (size_t)info.frame_bytes;
-        }
+        pMP3->dataConsumed += (size_t)info.frame_bytes;
+        pMP3->dataSize     -= (size_t)info.frame_bytes;
 
         /* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */
         if (pcmFramesRead > 0) {
@@ -2720,7 +2793,16 @@
             pMP3->pcmFramesConsumedInMP3Frame = 0;
             pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
             pMP3->mp3FrameChannels = info.channels;
-            pMP3->mp3FrameSampleRate = info.hz;
+            pMP3->mp3FrameSampleRate = info.sample_rate;
+
+            if (pMP3FrameInfo != NULL) {
+                *pMP3FrameInfo = info;
+            }
+
+            if (ppMP3FrameData != NULL) {
+                *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes;
+            }
+
             break;
         } else if (info.frame_bytes == 0) {
             /* Need more data. minimp3 recommends doing data submission in 16K chunks. */
@@ -2747,7 +2829,7 @@
             }
 
             /* Fill in a chunk. */
-            bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+            bytesRead = drmp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
             if (bytesRead == 0) {
                 pMP3->atEnd = DRMP3_TRUE;
                 return 0; /* Error reading more data. */
@@ -2760,7 +2842,7 @@
     return pcmFramesRead;
 }
 
-static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
+static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData)
 {
     drmp3_uint32 pcmFramesRead = 0;
     drmp3dec_frame_info info;
@@ -2779,11 +2861,21 @@
             pMP3->pcmFramesConsumedInMP3Frame  = 0;
             pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
             pMP3->mp3FrameChannels             = info.channels;
-            pMP3->mp3FrameSampleRate           = info.hz;
+            pMP3->mp3FrameSampleRate           = info.sample_rate;
+
+            if (pMP3FrameInfo != NULL) {
+                *pMP3FrameInfo = info;
+            }
+
+            if (ppMP3FrameData != NULL) {
+                *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos;
+            }
+
             break;
         } else if (info.frame_bytes > 0) {
             /* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */
             pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
+            pMP3->streamCursor          += (size_t)info.frame_bytes;
         } else {
             /* Nothing at all was read. Abort. */
             break;
@@ -2792,23 +2884,24 @@
 
     /* Consume the data. */
     pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
+    pMP3->streamCursor          += (size_t)info.frame_bytes;
 
     return pcmFramesRead;
 }
 
-static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3dec_frame_info* pMP3FrameInfo, const drmp3_uint8** ppMP3FrameData)
 {
     if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
-        return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
+        return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
     } else {
-        return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
+        return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
     }
 }
 
 static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
 {
     DRMP3_ASSERT(pMP3 != NULL);
-    return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
+    return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, NULL, NULL);
 }
 
 #if 0
@@ -2818,7 +2911,7 @@
 
     DRMP3_ASSERT(pMP3 != NULL);
 
-    pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
+    pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
     if (pcmFrameCount == 0) {
         return 0;
     }
@@ -2832,8 +2925,13 @@
 }
 #endif
 
-static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
+static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks)
 {
+    drmp3dec_frame_info firstFrameInfo;
+    const drmp3_uint8* pFirstFrameData;
+    drmp3_uint32 firstFramePCMFrameCount;
+    drmp3_uint32 detectedMP3FrameCount = 0xFFFFFFFF;
+
     DRMP3_ASSERT(pMP3 != NULL);
     DRMP3_ASSERT(onRead != NULL);
 
@@ -2842,33 +2940,342 @@
 
     pMP3->onRead = onRead;
     pMP3->onSeek = onSeek;
+    pMP3->onMeta = onMeta;
     pMP3->pUserData = pUserData;
+    pMP3->pUserDataMeta = pUserDataMeta;
     pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
 
     if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
         return DRMP3_FALSE;    /* Invalid allocation callbacks. */
     }
 
-    /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */
-    if (drmp3_decode_next_frame(pMP3) == 0) {
+    pMP3->streamCursor       = 0;
+    pMP3->streamLength       = DRMP3_UINT64_MAX;
+    pMP3->streamStartOffset  = 0;
+    pMP3->delayInPCMFrames   = 0;
+    pMP3->paddingInPCMFrames = 0;
+    pMP3->totalPCMFrameCount = DRMP3_UINT64_MAX;
+
+    /* We'll first check for any ID3v1 or APE tags. */
+    #if 1
+    if (onSeek != NULL && onTell != NULL) {
+        if (onSeek(pUserData, 0, DRMP3_SEEK_END)) {
+            drmp3_int64 streamLen;
+            int streamEndOffset = 0;
+        
+            /* First get the length of the stream. We need this so we can ensure the stream is big enough to store the tags. */
+            if (onTell(pUserData, &streamLen)) {
+                /* ID3v1 */
+                if (streamLen > 128) {
+                    char id3[3];
+                    if (onSeek(pUserData, streamEndOffset - 128, DRMP3_SEEK_END)) {
+                        if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') {
+                            /* We have an ID3v1 tag. */
+                            streamEndOffset -= 128;
+                            streamLen       -= 128;
+
+                            /* Fire a metadata callback for the TAG data. */
+                            if (onMeta != NULL) {
+                                drmp3_uint8 tag[128];
+                                tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G';
+
+                                if (onRead(pUserData, tag + 3, 125) == 125) {
+                                    drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V1, tag, 128);
+                                }
+                            }
+                        } else {
+                            /* No ID3v1 tag. */
+                        }
+                    } else {
+                        /* Failed to seek to the ID3v1 tag. */
+                    }
+                } else {
+                    /* Stream too short. No ID3v1 tag. */
+                }
+
+                /* APE */
+                if (streamLen > 32) {
+                    char ape[32];   /* The footer. */
+                    if (onSeek(pUserData, streamEndOffset - 32, DRMP3_SEEK_END)) {
+                        if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') {
+                            /* We have an APE tag. */
+                            drmp3_uint32 tagSize =
+                                ((drmp3_uint32)ape[24] << 0)  |
+                                ((drmp3_uint32)ape[25] << 8)  |
+                                ((drmp3_uint32)ape[26] << 16) |
+                                ((drmp3_uint32)ape[27] << 24);
+
+                            streamEndOffset -= 32 + tagSize;
+                            streamLen       -= 32 + tagSize;
+                            
+                            /* Fire a metadata callback for the APE data. Must include both the main content and footer. */
+                            if (onMeta != NULL) {
+                                /* We first need to seek to the start of the APE tag. */
+                                if (onSeek(pUserData, streamEndOffset, DRMP3_SEEK_END)) {
+                                    size_t apeTagSize = (size_t)tagSize + 32;
+                                    drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(apeTagSize, pAllocationCallbacks);
+                                    if (pTagData != NULL) {
+                                        if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
+                                            drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_APE, pTagData, apeTagSize);
+                                        }
+
+                                        drmp3_free(pTagData, pAllocationCallbacks);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    /* Stream too short. No APE tag. */
+                }
+
+                /* Seek back to the start. */
+                if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) {
+                    return DRMP3_FALSE; /* Failed to seek back to the start. */
+                }
+
+                pMP3->streamLength = (drmp3_uint64)streamLen;
+
+                if (pMP3->memory.pData != NULL) {
+                    pMP3->memory.dataSize = (size_t)pMP3->streamLength;
+                }
+            } else {
+                /* Failed to get the length of the stream. ID3v1 and APE tags cannot be skipped. */
+                if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) {
+                    return DRMP3_FALSE; /* Failed to seek back to the start. */
+                }
+            }
+        } else {
+            /* Failed to seek to the end. Cannot skip ID3v1 or APE tags. */
+        }
+    } else {
+        /* No onSeek or onTell callback. Cannot skip ID3v1 or APE tags. */
+    }
+    #endif
+
+
+    /* ID3v2 tags */
+    #if 1
+    {
+        char header[10];
+        if (onRead(pUserData, header, 10) == 10) {
+            if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') {
+                drmp3_uint32 tagSize =
+                    (((drmp3_uint32)header[6] & 0x7F) << 21) |
+                    (((drmp3_uint32)header[7] & 0x7F) << 14) |
+                    (((drmp3_uint32)header[8] & 0x7F) << 7)  |
+                    (((drmp3_uint32)header[9] & 0x7F) << 0);
+
+                /* Account for the footer. */
+                if (header[5] & 0x10) {
+                    tagSize += 10;
+                }
+
+                /* Read the tag content and fire a metadata callback. */
+                if (onMeta != NULL) {
+                    size_t tagSizeWithHeader = 10 + tagSize;
+                    drmp3_uint8* pTagData = (drmp3_uint8*)drmp3_malloc(tagSizeWithHeader, pAllocationCallbacks);
+                    if (pTagData != NULL) {
+                        DRMP3_COPY_MEMORY(pTagData, header, 10);
+
+                        if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) {
+                            drmp3__on_meta(pMP3, DRMP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader);
+                        }
+
+                        drmp3_free(pTagData, pAllocationCallbacks);
+                    }
+                } else {
+                    /* Don't have a metadata callback, so just skip the tag. */
+                    if (onSeek != NULL) {
+                        if (!onSeek(pUserData, tagSize, DRMP3_SEEK_CUR)) {
+                            return DRMP3_FALSE; /* Failed to seek past the ID3v2 tag. */
+                        }
+                    } else {
+                        /* Don't have a seek callback. Read and discard. */
+                        char discard[1024];
+
+                        while (tagSize > 0) {
+                            size_t bytesToRead = tagSize;
+                            if (bytesToRead > sizeof(discard)) {
+                                bytesToRead = sizeof(discard);
+                            }
+
+                            if (onRead(pUserData, discard, bytesToRead) != bytesToRead) {
+                                return DRMP3_FALSE; /* Failed to read data. */
+                            }
+
+                            tagSize -= (drmp3_uint32)bytesToRead;
+                        }
+                    }
+                }
+
+                pMP3->streamStartOffset += 10 + tagSize;    /* +10 for the header. */
+                pMP3->streamCursor = pMP3->streamStartOffset;
+            } else {
+                /* Not an ID3v2 tag. Seek back to the start. */
+                if (onSeek != NULL) {
+                    if (!onSeek(pUserData, 0, DRMP3_SEEK_SET)) {
+                        return DRMP3_FALSE; /* Failed to seek back to the start. */
+                    }
+                } else {
+                    /* Don't have a seek callback to move backwards. We'll just fall through and let the decoding process re-sync. The ideal solution here would be to read into the cache. */
+
+                    /*
+                    TODO: Copy the header into the cache. Will need to allocate space. See drmp3_decode_next_frame_ex__callbacks. There is not need
+                    to handle the memory case because that will always have a seek implementation and will never hit this code path.
+                    */
+                }
+            }
+        } else {
+            /* Failed to read the header. We can return false here. If we couldn't read 10 bytes there's no way we'll have a valid MP3 stream. */
+            return DRMP3_FALSE;
+        }
+    }
+    #endif
+
+    /*
+    Decode the first frame to confirm that it is indeed a valid MP3 stream. Note that it's possible the first frame
+    is actually a Xing/LAME/VBRI header. If this is the case we need to skip over it.
+    */
+    firstFramePCMFrameCount = drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData);
+    if (firstFramePCMFrameCount > 0) {
+        DRMP3_ASSERT(pFirstFrameData != NULL);
+
+        /*
+        It might be a header. If so, we need to clear out the cached PCM frames in order to trigger a reload of fresh
+        data when decoding starts. We can assume all validation has already been performed to check if this is a valid
+        MP3 frame and that there is more than 0 bytes making up the frame.
+
+        We're going to be basing this parsing code off the minimp3_ex implementation.
+        */
+        #if 1
+        DRMP3_ASSERT(firstFrameInfo.frame_bytes > 0);
+        {
+            drmp3_bs bs;
+            drmp3_L3_gr_info grInfo[4];
+            const drmp3_uint8* pTagData = pFirstFrameData;
+
+            drmp3_bs_init(&bs, pFirstFrameData + DRMP3_HDR_SIZE, firstFrameInfo.frame_bytes - DRMP3_HDR_SIZE);
+
+            if (DRMP3_HDR_IS_CRC(pFirstFrameData)) {
+                drmp3_bs_get_bits(&bs, 16); /* CRC. */
+            }
+
+            if (drmp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) {
+                drmp3_bool32 isXing = DRMP3_FALSE;
+                drmp3_bool32 isInfo = DRMP3_FALSE;
+                const drmp3_uint8* pTagDataBeg;
+
+                pTagDataBeg = pFirstFrameData + DRMP3_HDR_SIZE + (bs.pos/8);
+                pTagData    = pTagDataBeg;
+
+                /* Check for both "Xing" and "Info" identifiers. */
+                isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g');
+                isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o');
+
+                if (isXing || isInfo) {
+                    drmp3_uint32 bytes = 0;
+                    drmp3_uint32 flags = pTagData[7];
+
+                    pTagData += 8;  /* Skip past the ID and flags. */
+
+                    if (flags & 0x01) { /* FRAMES flag. */
+                        detectedMP3FrameCount = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3];
+                        pTagData += 4;
+                    }
+
+                    if (flags & 0x02) { /* BYTES flag. */
+                        bytes  = (drmp3_uint32)pTagData[0] << 24 | (drmp3_uint32)pTagData[1] << 16 | (drmp3_uint32)pTagData[2] << 8 | (drmp3_uint32)pTagData[3];
+                        (void)bytes;    /* <-- Just to silence a warning about `bytes` being assigned but unused. Want to leave this here in case I want to make use of it later. */
+                        pTagData += 4;
+                    }
+
+                    if (flags & 0x04) { /* TOC flag. */
+                        /* TODO: Extract and bind seek points. */
+                        pTagData += 100;
+                    }
+
+                    if (flags & 0x08) { /* SCALE flag. */
+                        pTagData += 4;
+                    }
+
+                    /* At this point we're done with the Xing/Info header. Now we can look at the LAME data. */
+                    if (pTagData[0]) {
+                        pTagData += 21;
+
+                        if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) {
+                            int delayInPCMFrames;
+                            int paddingInPCMFrames;
+
+                            delayInPCMFrames   = (( (drmp3_uint32)pTagData[0]        << 4) | ((drmp3_uint32)pTagData[1] >> 4)) + (528 + 1);
+                            paddingInPCMFrames = ((((drmp3_uint32)pTagData[1] & 0xF) << 8) | ((drmp3_uint32)pTagData[2]     )) - (528 + 1);
+                            if (paddingInPCMFrames < 0) {
+                                paddingInPCMFrames = 0; /* Padding cannot be negative. Probably a malformed file. Ignore. */
+                            }
+                            
+                            pMP3->delayInPCMFrames   = (drmp3_uint32)delayInPCMFrames;
+                            pMP3->paddingInPCMFrames = (drmp3_uint32)paddingInPCMFrames;
+                        }
+                    }
+
+                    /*
+                    My understanding is that if the "Xing" header is present we can consider this to be a VBR stream and if the "Info" header is
+                    present it's a CBR stream. If this is not the case let me know! I'm just tracking this for the time being in case I want to
+                    look at doing some CBR optimizations later on, such as faster seeking.
+                    */
+                    if (isXing) {
+                        pMP3->isVBR = DRMP3_TRUE;
+                    } else if (isInfo) {
+                        pMP3->isCBR = DRMP3_TRUE;
+                    }
+
+                    /* Post the raw data of the tag to the metadata callback. */
+                    if (onMeta != NULL) {
+                        drmp3_metadata_type metadataType = isXing ? DRMP3_METADATA_TYPE_XING : DRMP3_METADATA_TYPE_VBRI;
+                        size_t tagDataSize;
+                    
+                        tagDataSize  = (size_t)firstFrameInfo.frame_bytes;
+                        tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData);
+
+                        drmp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize);
+                    }
+
+                    /* Since this was identified as a tag, we don't want to treat it as audio. We need to clear out the PCM cache. */
+                    pMP3->pcmFramesRemainingInMP3Frame = 0;
+
+                    /* The start offset needs to be moved to the end of this frame so it's not included in any audio processing after seeking. */
+                    pMP3->streamStartOffset += (drmp3_uint32)(firstFrameInfo.frame_bytes);
+                    pMP3->streamCursor = pMP3->streamStartOffset;
+                }
+            } else {
+                /* Failed to read the side info. */
+            }
+        }
+        #endif
+    } else {
+        /* Not a valid MP3 stream. */
         drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);    /* The call above may have allocated memory. Need to make sure it's freed before aborting. */
-        return DRMP3_FALSE; /* Not a valid MP3 stream. */
+        return DRMP3_FALSE;
     }
 
+    if (detectedMP3FrameCount != 0xFFFFFFFF) {
+        pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount;
+    }
+
     pMP3->channels   = pMP3->mp3FrameChannels;
     pMP3->sampleRate = pMP3->mp3FrameSampleRate;
 
     return DRMP3_TRUE;
 }
 
-DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
+DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, drmp3_meta_proc onMeta, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
 {
     if (pMP3 == NULL || onRead == NULL) {
         return DRMP3_FALSE;
     }
 
     DRMP3_ZERO_OBJECT(pMP3);
-    return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
+    return drmp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks);
 }
 
 
@@ -2896,35 +3303,52 @@
 static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
 {
     drmp3* pMP3 = (drmp3*)pUserData;
+    drmp3_int64 newCursor;
 
     DRMP3_ASSERT(pMP3 != NULL);
 
-    if (origin == drmp3_seek_origin_current) {
-        if (byteOffset > 0) {
-            if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
-                byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);  /* Trying to seek too far forward. */
-            }
-        } else {
-            if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
-                byteOffset = -(int)pMP3->memory.currentReadPos;  /* Trying to seek too far backwards. */
-            }
-        }
+    newCursor = pMP3->memory.currentReadPos;
 
-        /* This will never underflow thanks to the clamps above. */
-        pMP3->memory.currentReadPos += byteOffset;
+    if (origin == DRMP3_SEEK_SET) {
+        newCursor = 0;
+    } else if (origin == DRMP3_SEEK_CUR) {
+        newCursor = (drmp3_int64)pMP3->memory.currentReadPos;
+    } else if (origin == DRMP3_SEEK_END) {
+        newCursor = (drmp3_int64)pMP3->memory.dataSize;
     } else {
-        if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
-            pMP3->memory.currentReadPos = byteOffset;
-        } else {
-            pMP3->memory.currentReadPos = pMP3->memory.dataSize;  /* Trying to seek too far forward. */
-        }
+        DRMP3_ASSERT(!"Invalid seek origin");
+        return DRMP3_FALSE;
     }
 
+    newCursor += byteOffset;
+
+    if (newCursor < 0) {
+        return DRMP3_FALSE;  /* Trying to seek prior to the start of the buffer. */
+    }
+    if ((size_t)newCursor > pMP3->memory.dataSize) {
+        return DRMP3_FALSE;  /* Trying to seek beyond the end of the buffer. */
+    }
+
+    pMP3->memory.currentReadPos = (size_t)newCursor;
+
     return DRMP3_TRUE;
 }
 
-DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
+static drmp3_bool32 drmp3__on_tell_memory(void* pUserData, drmp3_int64* pCursor)
 {
+    drmp3* pMP3 = (drmp3*)pUserData;
+
+    DRMP3_ASSERT(pMP3 != NULL);
+    DRMP3_ASSERT(pCursor != NULL);
+
+    *pCursor = (drmp3_int64)pMP3->memory.currentReadPos;
+    return DRMP3_TRUE;
+}
+
+DRMP3_API drmp3_bool32 drmp3_init_memory_with_metadata(drmp3* pMP3, const void* pData, size_t dataSize, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+    drmp3_bool32 result;
+
     if (pMP3 == NULL) {
         return DRMP3_FALSE;
     }
@@ -2939,10 +3363,29 @@
     pMP3->memory.dataSize = dataSize;
     pMP3->memory.currentReadPos = 0;
 
-    return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
+    result = drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, drmp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks);
+    if (result == DRMP3_FALSE) {
+        return DRMP3_FALSE;
+    }
+
+    /* Adjust the length of the memory stream to account for ID3v1 and APE tags. */
+    if (pMP3->streamLength <= (drmp3_uint64)DRMP3_SIZE_MAX) {
+        pMP3->memory.dataSize = (size_t)pMP3->streamLength; /* Safe cast. */
+    }
+
+    if (pMP3->streamStartOffset > (drmp3_uint64)DRMP3_SIZE_MAX) {
+        return DRMP3_FALSE; /* Tags too big. */
+    }
+
+    return DRMP3_TRUE;
 }
 
+DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+    return drmp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks);
+}
 
+
 #ifndef DR_MP3_NO_STDIO
 #include <stdio.h>
 #include <wchar.h>      /* For wcslen(), wcsrtombs() */
@@ -3069,7 +3512,7 @@
     #ifdef ENOSYS
         case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
     #endif
-    #ifdef ENOTEMPTY
+    #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST   /* In AIX, ENOTEMPTY and EEXIST use the same value. */
         case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
     #endif
     #ifdef ELOOP
@@ -3519,19 +3962,56 @@
 
 static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
 {
-    return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+    int whence = SEEK_SET;
+    if (origin == DRMP3_SEEK_CUR) {
+        whence = SEEK_CUR;
+    } else if (origin == DRMP3_SEEK_END) {
+        whence = SEEK_END;
+    }
+
+    return fseek((FILE*)pUserData, offset, whence) == 0;
 }
 
-DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
+static drmp3_bool32 drmp3__on_tell_stdio(void* pUserData, drmp3_int64* pCursor)
 {
+    FILE* pFileStdio = (FILE*)pUserData;
+    drmp3_int64 result;
+
+    /* These were all validated at a higher level. */
+    DRMP3_ASSERT(pFileStdio != NULL);
+    DRMP3_ASSERT(pCursor    != NULL);
+
+#if defined(_WIN32)
+    #if defined(_MSC_VER) && _MSC_VER > 1200
+        result = _ftelli64(pFileStdio);
+    #else
+        result = ftell(pFileStdio);
+    #endif
+#else
+    result = ftell(pFileStdio);
+#endif
+
+    *pCursor = result;
+
+    return DRMP3_TRUE;
+}
+
+DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata(drmp3* pMP3, const char* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
     drmp3_bool32 result;
     FILE* pFile;
 
+    if (pMP3 == NULL) {
+        return DRMP3_FALSE;
+    }
+
+    DRMP3_ZERO_OBJECT(pMP3);
+
     if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
         return DRMP3_FALSE;
     }
 
-    result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+    result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
     if (result != DRMP3_TRUE) {
         fclose(pFile);
         return result;
@@ -3540,16 +4020,22 @@
     return DRMP3_TRUE;
 }
 
-DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
+DRMP3_API drmp3_bool32 drmp3_init_file_with_metadata_w(drmp3* pMP3, const wchar_t* pFilePath, drmp3_meta_proc onMeta, void* pUserDataMeta, const drmp3_allocation_callbacks* pAllocationCallbacks)
 {
     drmp3_bool32 result;
     FILE* pFile;
 
+    if (pMP3 == NULL) {
+        return DRMP3_FALSE;
+    }
+
+    DRMP3_ZERO_OBJECT(pMP3);
+
     if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
         return DRMP3_FALSE;
     }
 
-    result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+    result = drmp3_init_internal(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, drmp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
     if (result != DRMP3_TRUE) {
         fclose(pFile);
         return result;
@@ -3557,6 +4043,16 @@
 
     return DRMP3_TRUE;
 }
+
+DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+    return drmp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
+}
+
+DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+    return drmp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
+}
 #endif
 
 DRMP3_API void drmp3_uninit(drmp3* pMP3)
@@ -3564,7 +4060,7 @@
     if (pMP3 == NULL) {
         return;
     }
-    
+
 #ifndef DR_MP3_NO_STDIO
     if (pMP3->onRead == drmp3__on_read_stdio) {
         FILE* pFile = (FILE*)pMP3->pUserData;
@@ -3644,19 +4140,48 @@
     DRMP3_ASSERT(pMP3->onRead != NULL);
 
     while (framesToRead > 0) {
-        drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
+        drmp3_uint32 framesToConsume;
+
+        /* Skip frames if necessary. */
+        if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) {
+            drmp3_uint32 framesToSkip = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame);
+
+            pMP3->currentPCMFrame              += framesToSkip;
+            pMP3->pcmFramesConsumedInMP3Frame  += framesToSkip;
+            pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip;
+        }
+
+        framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
+
+        /* Clamp the number of frames to read to the padding. */
+        if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) {
+            if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
+                drmp3_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame;
+                if (framesToConsume >               framesRemainigToPadding) {
+                    framesToConsume = (drmp3_uint32)framesRemainigToPadding;
+                }
+            } else {
+                /* We're into the padding. Abort. */
+                break;
+            }
+        }
+
         if (pBufferOut != NULL) {
-        #if defined(DR_MP3_FLOAT_OUTPUT)
-            /* f32 */
-            float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(float) * totalFramesRead                   * pMP3->channels);
-            float* pFramesInF32  = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
-            DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
-        #else
-            /* s16 */
-            drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(drmp3_int16) * totalFramesRead                   * pMP3->channels);
-            drmp3_int16* pFramesInS16  = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
-            DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
-        #endif
+            #if defined(DR_MP3_FLOAT_OUTPUT)
+            {
+                /* f32 */
+                float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(float) * totalFramesRead                   * pMP3->channels);
+                float* pFramesInF32  = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
+                DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
+            }
+            #else
+            {
+                /* s16 */
+                drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut,          sizeof(drmp3_int16) * totalFramesRead                   * pMP3->channels);
+                drmp3_int16* pFramesInS16  = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
+                DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
+            }
+            #endif
         }
 
         pMP3->currentPCMFrame              += framesToConsume;
@@ -3669,12 +4194,14 @@
             break;
         }
 
+        /* If the cursor is already at the padding we need to abort. */
+        if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
+            break;
+        }
+
         DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
 
-        /*
-        At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed
-        at this point which means we'll also need to update our sample rate conversion pipeline.
-        */
+        /* At this point we have exhausted our in-memory buffer so we need to re-fill. */
         if (drmp3_decode_next_frame(pMP3) == 0) {
             break;
         }
@@ -3776,7 +4303,7 @@
     DRMP3_ASSERT(pMP3->onSeek != NULL);
 
     /* Seek to the start of the stream to begin with. */
-    if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
+    if (!drmp3__on_seek_64(pMP3, pMP3->streamStartOffset, DRMP3_SEEK_SET)) {
         return DRMP3_FALSE;
     }
 
@@ -3876,7 +4403,7 @@
     }
 
     /* First thing to do is seek to the first byte of the relevant MP3 frame. */
-    if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
+    if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, DRMP3_SEEK_SET)) {
         return DRMP3_FALSE; /* Failed to seek. */
     }
 
@@ -3895,7 +4422,7 @@
         }
 
         /* We first need to decode the next frame. */
-        pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
+        pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL);
         if (pcmFramesRead == 0) {
             return DRMP3_FALSE;
         }
@@ -3952,7 +4479,7 @@
 
     /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */
     currentPCMFrame = pMP3->currentPCMFrame;
-    
+
     if (!drmp3_seek_to_start_of_stream(pMP3)) {
         return DRMP3_FALSE;
     }
@@ -3963,7 +4490,7 @@
     for (;;) {
         drmp3_uint32 pcmFramesInCurrentMP3Frame;
 
-        pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
+        pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
         if (pcmFramesInCurrentMP3Frame == 0) {
             break;
         }
@@ -3994,11 +4521,35 @@
 DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
 {
     drmp3_uint64 totalPCMFrameCount;
-    if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
+
+    if (pMP3 == NULL) {
         return 0;
     }
 
-    return totalPCMFrameCount;
+    if (pMP3->totalPCMFrameCount != DRMP3_UINT64_MAX) {
+        totalPCMFrameCount = pMP3->totalPCMFrameCount;
+
+        if (totalPCMFrameCount >= pMP3->delayInPCMFrames) {
+            totalPCMFrameCount -= pMP3->delayInPCMFrames;
+        } else {
+            /* The delay is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */
+        }
+
+        if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) {
+            totalPCMFrameCount -= pMP3->paddingInPCMFrames;
+        } else {
+            /* The padding is greater than the frame count reported by the Xing/Info tag. Assume it's invalid and ignore. */
+        }
+
+        return totalPCMFrameCount;
+    } else {
+        /* Unknown frame count. Need to calculate it. */
+        if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
+            return 0;
+        }
+
+        return totalPCMFrameCount;
+    }
 }
 
 DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
@@ -4050,7 +4601,7 @@
 
     /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */
     currentPCMFrame = pMP3->currentPCMFrame;
-    
+
     /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */
     if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
         return DRMP3_FALSE;
@@ -4101,7 +4652,7 @@
             mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
 
             /* We need to get information about this frame so we can know how many samples it contained. */
-            pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
+            pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
             if (pcmFramesInCurrentMP3FrameIn == 0) {
                 return DRMP3_FALSE; /* This should never happen. */
             }
@@ -4145,7 +4696,7 @@
                     Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it
                     should only ever do it for the last seek point.
                     */
-                    pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
+                    pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
                     if (pcmFramesInCurrentMP3FrameIn == 0) {
                         pSeekPoints[iSeekPoint].seekPosInBytes     = mp3FrameInfo[0].bytePos;
                         pSeekPoints[iSeekPoint].pcmFrameIndex      = nextTargetPCMFrame;
@@ -4327,20 +4878,20 @@
 }
 
 
-DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
 {
     drmp3 mp3;
-    if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
         return NULL;
     }
 
     return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
 }
 
-DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, drmp3_tell_proc onTell, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
 {
     drmp3 mp3;
-    if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drmp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
         return NULL;
     }
 
@@ -4428,73 +4979,28 @@
 */
 
 /*
-RELEASE NOTES - v0.5.0
-=======================
-Version 0.5.0 has breaking API changes.
-
-Improved Client-Defined Memory Allocation
------------------------------------------
-The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
-existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom
-allocation callbacks are specified.
-
-To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this:
-
-    void* my_malloc(size_t sz, void* pUserData)
-    {
-        return malloc(sz);
-    }
-    void* my_realloc(void* p, size_t sz, void* pUserData)
-    {
-        return realloc(p, sz);
-    }
-    void my_free(void* p, void* pUserData)
-    {
-        free(p);
-    }
-
-    ...
-
-    drmp3_allocation_callbacks allocationCallbacks;
-    allocationCallbacks.pUserData = &myData;
-    allocationCallbacks.onMalloc  = my_malloc;
-    allocationCallbacks.onRealloc = my_realloc;
-    allocationCallbacks.onFree    = my_free;
-    drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks);
-
-The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
-
-Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC,
-DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions.
-
-Every API that opens a drmp3 object now takes this extra parameter. These include the following:
-
-    drmp3_init()
-    drmp3_init_file()
-    drmp3_init_memory()
-    drmp3_open_and_read_pcm_frames_f32()
-    drmp3_open_and_read_pcm_frames_s16()
-    drmp3_open_memory_and_read_pcm_frames_f32()
-    drmp3_open_memory_and_read_pcm_frames_s16()
-    drmp3_open_file_and_read_pcm_frames_f32()
-    drmp3_open_file_and_read_pcm_frames_s16()
+REVISION HISTORY
+================
+v0.7.0 - TBD
+  - The old `DRMP3_IMPLEMENTATION` has been removed. Use `DR_MP3_IMPLEMENTATION` instead. The reason for this change is that in the future everything will eventually be using the underscored naming convention in the future, so `drmp3` will become `dr_mp3`.
+  - API CHANGE: Seek origins have been renamed to match the naming convention used by dr_wav and my other libraries.
+    - drmp3_seek_origin_start   -> DRMP3_SEEK_SET
+    - drmp3_seek_origin_current -> DRMP3_SEEK_CUR
+    - DRMP3_SEEK_END (new)
+  - API CHANGE: Add DRMP3_SEEK_END as a seek origin for the seek callback. This is required for detection of ID3v1 and APE tags.
+  - API CHANGE: Add onTell callback to `drmp3_init()`. This is needed in order to track the location of ID3v1 and APE tags.
+  - API CHANGE: Add onMeta callback to `drmp3_init()`. This is used for reporting tag data back to the caller. Currently this only reports the raw tag data which means applications need to parse the data themselves.
+  - API CHANGE: Rename `drmp3dec_frame_info.hz` to `drmp3dec_frame_info.sample_rate`.
+  - Add detection of ID3v2, ID3v1, APE and Xing/VBRI tags. This should fix errors with some files where the decoder was reading tags as audio data.
+  - Delay and padding samples from LAME tags are now handled.
+  - Fix compilation for AIX OS.
 
-Renamed APIs
-------------
-The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame
-counts rather than sample counts.
+v0.6.40 - 2024-12-17
+  - Improve detection of ARM64EC
 
-    drmp3_open_and_read_f32()        -> drmp3_open_and_read_pcm_frames_f32()
-    drmp3_open_and_read_s16()        -> drmp3_open_and_read_pcm_frames_s16()
-    drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
-    drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
-    drmp3_open_file_and_read_f32()   -> drmp3_open_file_and_read_pcm_frames_f32()
-    drmp3_open_file_and_read_s16()   -> drmp3_open_file_and_read_pcm_frames_s16()
-*/
+v0.6.39 - 2024-02-27
+  - Fix a Wdouble-promotion warning.
 
-/*
-REVISION HISTORY
-================
 v0.6.38 - 2023-11-02
   - Fix build for ARMv6-M.
 
diff --git a/raylib/src/external/dr_wav.h b/raylib/src/external/dr_wav.h
--- a/raylib/src/external/dr_wav.h
+++ b/raylib/src/external/dr_wav.h
@@ -1,6 +1,6 @@
 /*
 WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
-dr_wav - v0.13.13 - 2023-11-02
+dr_wav - v0.14.0 - TBD
 
 David Reid - mackron@gmail.com
 
@@ -146,8 +146,8 @@
 #define DRWAV_XSTRINGIFY(x)     DRWAV_STRINGIFY(x)
 
 #define DRWAV_VERSION_MAJOR     0
-#define DRWAV_VERSION_MINOR     13
-#define DRWAV_VERSION_REVISION  13
+#define DRWAV_VERSION_MINOR     14
+#define DRWAV_VERSION_REVISION  0
 #define DRWAV_VERSION_STRING    DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
 
 #include <stddef.h> /* For size_t. */
@@ -176,7 +176,7 @@
         #pragma GCC diagnostic pop
     #endif
 #endif
-#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
+#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__powerpc64__)
     typedef drwav_uint64        drwav_uintptr;
 #else
     typedef drwav_uint32        drwav_uintptr;
@@ -305,8 +305,9 @@
 
 typedef enum
 {
-    drwav_seek_origin_start,
-    drwav_seek_origin_current
+    DRWAV_SEEK_SET,
+    DRWAV_SEEK_CUR,
+    DRWAV_SEEK_END
 } drwav_seek_origin;
 
 typedef enum
@@ -415,12 +416,22 @@
 
 Returns whether or not the seek was successful.
 
-Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or
-drwav_seek_origin_current.
+Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either DRWAV_SEEK_SET or
+DRWAV_SEEK_CUR.
 */
 typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
 
 /*
+Callback for when the current position in the stream needs to be retrieved.
+
+pUserData [in]  The user data that was passed to drwav_init() and family.
+pCursor   [out] A pointer to a variable to receive the current position in the stream.
+
+Returns whether or not the operation was successful.
+*/
+typedef drwav_bool32 (* drwav_tell_proc)(void* pUserData, drwav_int64* pCursor);
+
+/*
 Callback for when drwav_init_ex() finds a chunk.
 
 pChunkUserData    [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.
@@ -514,6 +525,11 @@
     drwav_metadata_type_list_info_genre             = 1 << 15,
     drwav_metadata_type_list_info_album             = 1 << 16,
     drwav_metadata_type_list_info_tracknumber       = 1 << 17,
+    drwav_metadata_type_list_info_location          = 1 << 18,
+    drwav_metadata_type_list_info_organization      = 1 << 19,
+    drwav_metadata_type_list_info_keywords          = 1 << 20,
+    drwav_metadata_type_list_info_medium            = 1 << 21,
+    drwav_metadata_type_list_info_description       = 1 << 22,
 
     /* Other type constants for convenience. */
     drwav_metadata_type_list_all_info_strings       = drwav_metadata_type_list_info_software
@@ -524,7 +540,12 @@
                                                     | drwav_metadata_type_list_info_date
                                                     | drwav_metadata_type_list_info_genre
                                                     | drwav_metadata_type_list_info_album
-                                                    | drwav_metadata_type_list_info_tracknumber,
+                                                    | drwav_metadata_type_list_info_tracknumber
+                                                    | drwav_metadata_type_list_info_location
+                                                    | drwav_metadata_type_list_info_organization
+                                                    | drwav_metadata_type_list_info_keywords
+                                                    | drwav_metadata_type_list_info_medium
+                                                    | drwav_metadata_type_list_info_description,
 
     drwav_metadata_type_list_all_adtl               = drwav_metadata_type_list_label
                                                     | drwav_metadata_type_list_note
@@ -555,11 +576,11 @@
     /* See drwav_smpl_loop_type. */
     drwav_uint32 type;
 
-    /* The byte offset of the first sample to be played in the loop. */
-    drwav_uint32 firstSampleByteOffset;
+    /* The offset of the first sample to be played in the loop. */
+    drwav_uint32 firstSampleOffset;
 
-    /* The byte offset into the audio data of the last sample to be played in the loop. */
-    drwav_uint32 lastSampleByteOffset;
+    /* The offset into the audio data of the last sample to be played in the loop. */
+    drwav_uint32 lastSampleOffset;
 
     /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */
     drwav_uint32 sampleFraction;
@@ -637,8 +658,8 @@
     /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */
     drwav_uint32 blockStart;
 
-    /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
-    drwav_uint32 sampleByteOffset;
+    /* For uncompressed formats this is the offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
+    drwav_uint32 sampleOffset;
 } drwav_cue_point;
 
 typedef struct
@@ -846,6 +867,9 @@
     /* A pointer to the function to call when the wav file needs to be seeked. */
     drwav_seek_proc onSeek;
 
+    /* A pointer to the function to call when the position of the stream needs to be retrieved. */
+    drwav_tell_proc onTell;
+
     /* The user data to pass to callbacks. */
     void* pUserData;
 
@@ -968,9 +992,9 @@
 
 See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
 */
-DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
 
 /*
 Initializes a pre-allocated drwav object for writing.
@@ -1273,9 +1297,9 @@
 
 The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
 */
-DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
 #ifndef DR_WAV_NO_STDIO
 /*
 Opens and decodes an entire wav file in a single operation.
@@ -1384,7 +1408,7 @@
 #define DRWAV_MAX_SIMD_VECTOR_SIZE         32
 
 /* Architecture Detection */
-#if defined(__x86_64__) || defined(_M_X64)
+#if defined(__x86_64__) || (defined(_M_X64) && !defined(_M_ARM64EC))
     #define DRWAV_X64
 #elif defined(__i386) || defined(_M_IX86)
     #define DRWAV_X86
@@ -1962,12 +1986,12 @@
     drwav_uint64 bytesRemainingToSeek = offset;
     while (bytesRemainingToSeek > 0) {
         if (bytesRemainingToSeek > 0x7FFFFFFF) {
-            if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+            if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
                 return DRWAV_FALSE;
             }
             bytesRemainingToSeek -= 0x7FFFFFFF;
         } else {
-            if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
+            if (!onSeek(pUserData, (int)bytesRemainingToSeek, DRWAV_SEEK_CUR)) {
                 return DRWAV_FALSE;
             }
             bytesRemainingToSeek = 0;
@@ -1980,21 +2004,21 @@
 DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
 {
     if (offset <= 0x7FFFFFFF) {
-        return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
+        return onSeek(pUserData, (int)offset, DRWAV_SEEK_SET);
     }
 
     /* Larger than 32-bit seek. */
-    if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
+    if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_SET)) {
         return DRWAV_FALSE;
     }
     offset -= 0x7FFFFFFF;
 
     for (;;) {
         if (offset <= 0x7FFFFFFF) {
-            return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
+            return onSeek(pUserData, (int)offset, DRWAV_SEEK_CUR);
         }
 
-        if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+        if (!onSeek(pUserData, 0x7FFFFFFF, DRWAV_SEEK_CUR)) {
             return DRWAV_FALSE;
         }
         offset -= 0x7FFFFFFF;
@@ -2028,7 +2052,7 @@
         return DRWAV_FALSE;
     }
 
-    if (origin == drwav_seek_origin_start) {
+    if (origin == DRWAV_SEEK_SET) {
         *pCursor = offset;
     } else {
         *pCursor += offset;
@@ -2096,7 +2120,7 @@
             pParser->pDataCursor += align - modulo;
         }
     }
-    
+
     pResult = pParser->pDataCursor;
 
     /*
@@ -2189,12 +2213,12 @@
                 bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
 
                 if (bytesJustRead == sizeof(smplLoopData)) {
-                    pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId            = drwav_bytes_to_u32(smplLoopData + 0);
-                    pMetadata->data.smpl.pLoops[iSampleLoop].type                  = drwav_bytes_to_u32(smplLoopData + 4);
-                    pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
-                    pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset  = drwav_bytes_to_u32(smplLoopData + 12);
-                    pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction        = drwav_bytes_to_u32(smplLoopData + 16);
-                    pMetadata->data.smpl.pLoops[iSampleLoop].playCount             = drwav_bytes_to_u32(smplLoopData + 20);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId        = drwav_bytes_to_u32(smplLoopData + 0);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].type              = drwav_bytes_to_u32(smplLoopData + 4);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = drwav_bytes_to_u32(smplLoopData + 8);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset  = drwav_bytes_to_u32(smplLoopData + 12);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction    = drwav_bytes_to_u32(smplLoopData + 16);
+                    pMetadata->data.smpl.pLoops[iSampleLoop].playCount         = drwav_bytes_to_u32(smplLoopData + 20);
                 } else {
                     break;
                 }
@@ -2254,7 +2278,7 @@
                         pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3]    = cuePointData[11];
                         pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart        = drwav_bytes_to_u32(cuePointData + 12);
                         pMetadata->data.cue.pCuePoints[iCuePoint].blockStart        = drwav_bytes_to_u32(cuePointData + 16);
-                        pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset  = drwav_bytes_to_u32(cuePointData + 20);
+                        pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset      = drwav_bytes_to_u32(cuePointData + 20);
                     } else {
                         break;
                     }
@@ -2407,7 +2431,7 @@
     size_t bytesRemaining;
 
     DRWAV_ASSERT(pReader != NULL);
-    
+
     if (pBytesRead != NULL) {
         *pBytesRead = 0;
     }
@@ -2487,7 +2511,7 @@
     size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
 
     DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-    
+
     if (bytesRead == sizeof(bextData)) {
         drwav_buffer_reader reader;
         drwav_uint32 timeReferenceLow;
@@ -2549,7 +2573,7 @@
     drwav_uint64 totalBytesRead = 0;
     size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
 
-    DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);    
+    DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
 
     if (bytesJustRead == sizeof(cueIDBuffer)) {
         drwav_uint32 sizeIncludingNullTerminator;
@@ -2698,7 +2722,7 @@
                 drwav_uint8 buffer[4];
                 size_t bytesJustRead;
 
-                if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
+                if (!pParser->onSeek(pParser->pReadSeekUserData, 28, DRWAV_SEEK_CUR)) {
                     return bytesRead;
                 }
                 bytesRead += 28;
@@ -2721,7 +2745,7 @@
                         }
                     } else {
                         /* Loop count in header does not match the size of the chunk. */
-                    }                    
+                    }
                 }
             } else {
                 bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
@@ -2811,7 +2835,7 @@
                     return bytesRead;
                 }
                 allocSizeNeeded += drwav__strlen(buffer) + 1;
-                allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */
+                allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES + 1; /* Coding history. */
 
                 drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
 
@@ -2916,6 +2940,16 @@
                 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_album);
             } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
                 subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_tracknumber);
+            } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_location, "IARL")) {
+                subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_location);
+            } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_organization, "ICMS")) {
+                subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_organization);
+            } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_keywords, "IKEY")) {
+                subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_keywords);
+            } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_medium, "IMED")) {
+                subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_medium);
+            } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_description, "ISBJ")) {
+                subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize,  drwav_metadata_type_list_info_description);
             } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
                 subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
             }
@@ -2926,14 +2960,14 @@
             if (subchunkBytesRead < subchunkDataSize) {
                 drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
 
-                if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
+                if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, DRWAV_SEEK_CUR)) {
                     break;
                 }
                 bytesRead += bytesToSeek;
             }
 
             if ((subchunkDataSize % 2) == 1) {
-                if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
+                if (!pParser->onSeek(pParser->pReadSeekUserData, 1, DRWAV_SEEK_CUR)) {
                     break;
                 }
                 bytesRead += 1;
@@ -2985,16 +3019,17 @@
     }
 }
 
-DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pReadSeekTellUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
-    if (pWav == NULL || onRead == NULL || onSeek == NULL) {
+    if (pWav == NULL || onRead == NULL || onSeek == NULL) { /* <-- onTell is optional. */
         return DRWAV_FALSE;
     }
 
     DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
     pWav->onRead    = onRead;
     pWav->onSeek    = onSeek;
-    pWav->pUserData = pReadSeekUserData;
+    pWav->onTell    = onTell;
+    pWav->pUserData = pReadSeekTellUserData;
     pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
 
     if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
@@ -3075,7 +3110,13 @@
 
         if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
             if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
-                return DRWAV_FALSE;    /* Chunk size should always be at least 36 bytes. */
+                /*
+                I've had a report of a WAV file failing to load when the size of the WAVE chunk is not encoded
+                and is instead just set to 0. I'm going to relax the validation here to allow these files to
+                load. Considering the chunk size isn't actually used this should be safe. With this change my
+                test suite still passes.
+                */
+                /*return DRWAV_FALSE;*/    /* Chunk size should always be at least 36 bytes. */
             }
         } else if (pWav->container == drwav_container_rf64) {
             if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
@@ -3305,7 +3346,7 @@
                         fmt.channelMask        = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container);
                         drwav_bytes_to_guid(fmtext + 6, fmt.subFormat);
                     } else {
-                        if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, drwav_seek_origin_current) == DRWAV_FALSE) {
+                        if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
                             return DRWAV_FALSE;
                         }
                     }
@@ -3315,7 +3356,7 @@
                 }
 
                 /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
-                if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current) == DRWAV_FALSE) {
+                if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), DRWAV_SEEK_CUR) == DRWAV_FALSE) {
                     return DRWAV_FALSE;
                 }
                 cursor += (header.sizeInBytes - bytesReadSoFar);
@@ -3336,7 +3377,7 @@
         if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) ||
             ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) {
             foundChunk_data = DRWAV_TRUE;
-            
+
             pWav->dataChunkDataPos  = cursor;
 
             if (pWav->container != drwav_container_rf64) {  /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */
@@ -3426,7 +3467,7 @@
                 return DRWAV_FALSE;
             }
 
-            
+
             channels         = drwav_bytes_to_u16_ex     (commData + 0, pWav->container);
             frameCount       = drwav_bytes_to_u32_ex     (commData + 2, pWav->container);
             sampleSizeInBits = drwav_bytes_to_u16_ex     (commData + 6, pWav->container);
@@ -3459,12 +3500,15 @@
                     compressionFormat = DR_WAVE_FORMAT_MULAW;
                 } else if (drwav_fourcc_equal(type, "ima4")) {
                     compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM;
-                    sampleSizeInBits = 4;
+                    sampleSizeInBits  = 4;
 
                     /*
                     I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out
                     we'll need to abort when we encounter such an encoding. Advice welcome!
                     */
+                    (void)compressionFormat;
+                    (void)sampleSizeInBits;
+
                     return DRWAV_FALSE;
                 } else {
                     return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */
@@ -3501,8 +3545,8 @@
 
             /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */
             fmt.bitsPerSample += (fmt.bitsPerSample & 7);
-            
 
+
             /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */
             if (isAIFCFormType) {
                 if (drwav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == DRWAV_FALSE) {
@@ -3527,20 +3571,46 @@
                 return DRWAV_FALSE;
             }
 
-            /* We need to seek forward by the offset. */
+            /* The position of the audio data starts at an offset. */
             offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
-            if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
-                return DRWAV_FALSE;
-            }
-            cursor += offset;
+            pWav->dataChunkDataPos = cursor + offset;
 
-            pWav->dataChunkDataPos = cursor;
+            /* The data chunk size needs to be reduced by the offset or else seeking will break. */
             dataChunkSize = chunkSize;
+            if (dataChunkSize  > offset) {
+                dataChunkSize -= offset;
+            } else {
+                dataChunkSize = 0;
+            }
 
-            /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */
-            if (sequential || !isProcessingMetadata) {
-                break;      /* No need to keep reading beyond the data chunk. */
+            if (sequential) {
+                if (foundChunk_fmt) {   /* <-- Name is misleading, but will be set to true if the COMM chunk has been parsed. */
+                    /*
+                    Getting here means we're opening in sequential mode and we've found the SSND (data) and COMM (fmt) chunks. We need
+                    to get out of the loop here or else we'll end up going past the data chunk and will have no way of getting back to
+                    it since we're not allowed to seek backwards.
+
+                    One subtle detail here is that there is an offset with the SSND chunk. We need to make sure we seek past this offset
+                    so we're left sitting on the first byte of actual audio data.
+                    */
+                    if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
+                        return DRWAV_FALSE;
+                    }
+                    cursor += offset;
+
+                    break;
+                } else {
+                    /*
+                    Getting here means the COMM chunk was not found. In sequential mode, if we haven't yet found the COMM chunk
+                    we'll need to abort because we can't be doing a backwards seek back to the SSND chunk in order to read the
+                    data. For this reason, this configuration of AIFF files are not supported with sequential mode.
+                    */
+                    return DRWAV_FALSE; 
+                }
             } else {
+                chunkSize += header.paddingSize;                /* <-- Make sure we seek past the padding. */
+                chunkSize -= sizeof(offsetAndBlockSizeData);    /* <-- This was read earlier. */
+
                 if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
                     break;
                 }
@@ -3551,13 +3621,9 @@
         }
 
 
-
         /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
         if (isProcessingMetadata) {
-            drwav_uint64 metadataBytesRead;
-            
-            metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
-            DRWAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
+            drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
 
             /* Go back to the start of the chunk so we can normalize the position of the cursor. */
             if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
@@ -3644,8 +3710,26 @@
         pWav->metadataCount = metadataParser.metadataCount;
     }
 
-
-    /* At this point we should be sitting on the first byte of the raw audio data. */
+    /*
+    It's possible for the size reported in the data chunk to be greater than that of the file. We
+    need to do a validation check here to make sure we don't exceed the file size. To skip this
+    check, set the onTell callback to NULL.
+    */
+    if (pWav->onTell != NULL && pWav->onSeek != NULL) {
+        if (pWav->onSeek(pWav->pUserData, 0, DRWAV_SEEK_END) == DRWAV_TRUE) {
+            drwav_int64 fileSize;
+            if (pWav->onTell(pWav->pUserData, &fileSize)) {
+                if (dataChunkSize + pWav->dataChunkDataPos > (drwav_uint64)fileSize) {
+                    dataChunkSize = (drwav_uint64)fileSize - pWav->dataChunkDataPos;
+                }
+            }
+        } else {
+            /*
+            Failed to seek to the end of the file. It might not be supported by the backend so in
+            this case we cannot perform the validation check.
+            */
+        }
+    }
 
     /*
     I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and
@@ -3667,6 +3751,7 @@
         }
     }
 
+    /* At this point we want to be sitting on the first byte of the raw audio data. */
     if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) {
         drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
         return DRWAV_FALSE;
@@ -3677,8 +3762,26 @@
     pWav->sampleRate          = fmt.sampleRate;
     pWav->channels            = fmt.channels;
     pWav->bitsPerSample       = fmt.bitsPerSample;
-    pWav->bytesRemaining      = dataChunkSize;
     pWav->translatedFormatTag = translatedFormatTag;
+
+    /*
+    I've had a report where files would start glitching after seeking. The reason for this is the data
+    chunk is not a clean multiple of the PCM frame size in bytes. Where this becomes a problem is when
+    seeking, because the number of bytes remaining in the data chunk is used to calculate the current
+    byte position. If this byte position is not aligned to the number of bytes in a PCM frame, it will
+    result in the seek not being cleanly positioned at the start of the PCM frame thereby resulting in
+    all decoded frames after that being corrupted.
+
+    To address this, we need to round the data chunk size down to the nearest multiple of the frame size.
+    */
+    if (!drwav__is_compressed_format_tag(translatedFormatTag)) {
+        drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+        if (bytesPerFrame > 0) {
+            dataChunkSize -= (dataChunkSize % bytesPerFrame);
+        }
+    }
+
+    pWav->bytesRemaining      = dataChunkSize;
     pWav->dataChunkDataSize   = dataChunkSize;
 
     if (sampleCountFromFactChunk != 0) {
@@ -3761,23 +3864,23 @@
     return DRWAV_TRUE;
 }
 
-DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
-    return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
+    return drwav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks);
 }
 
-DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, drwav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
-    if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
+    if (!drwav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) {
         return DRWAV_FALSE;
     }
 
     return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
 }
 
-DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
-    if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drwav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
         return DRWAV_FALSE;
     }
 
@@ -3992,8 +4095,8 @@
                 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
-                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
-                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
+                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset);
+                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset);
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
                 }
@@ -4033,7 +4136,7 @@
                     bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
                     bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
-                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
+                    bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset);
                 }
             } break;
 
@@ -4139,15 +4242,20 @@
                 const char* pID = NULL;
 
                 switch (pMetadata->type) {
-                    case drwav_metadata_type_list_info_software:    pID = "ISFT"; break;
-                    case drwav_metadata_type_list_info_copyright:   pID = "ICOP"; break;
-                    case drwav_metadata_type_list_info_title:       pID = "INAM"; break;
-                    case drwav_metadata_type_list_info_artist:      pID = "IART"; break;
-                    case drwav_metadata_type_list_info_comment:     pID = "ICMT"; break;
-                    case drwav_metadata_type_list_info_date:        pID = "ICRD"; break;
-                    case drwav_metadata_type_list_info_genre:       pID = "IGNR"; break;
-                    case drwav_metadata_type_list_info_album:       pID = "IPRD"; break;
-                    case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
+                    case drwav_metadata_type_list_info_software:     pID = "ISFT"; break;
+                    case drwav_metadata_type_list_info_copyright:    pID = "ICOP"; break;
+                    case drwav_metadata_type_list_info_title:        pID = "INAM"; break;
+                    case drwav_metadata_type_list_info_artist:       pID = "IART"; break;
+                    case drwav_metadata_type_list_info_comment:      pID = "ICMT"; break;
+                    case drwav_metadata_type_list_info_date:         pID = "ICRD"; break;
+                    case drwav_metadata_type_list_info_genre:        pID = "IGNR"; break;
+                    case drwav_metadata_type_list_info_album:        pID = "IPRD"; break;
+                    case drwav_metadata_type_list_info_tracknumber:  pID = "ITRK"; break;
+                    case drwav_metadata_type_list_info_location:     pID = "IARL"; break;
+                    case drwav_metadata_type_list_info_organization: pID = "ICMS"; break;
+                    case drwav_metadata_type_list_info_keywords:     pID = "IKEY"; break;
+                    case drwav_metadata_type_list_info_medium:       pID = "IMED"; break;
+                    case drwav_metadata_type_list_info_description:  pID = "ISBJ"; break;
                     default: break;
                 }
 
@@ -4192,7 +4300,7 @@
 
                     if (pMetadata->data.labelOrNote.stringLength > 0) {
                         chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
-                    }    
+                    }
                 } break;
 
                 case drwav_metadata_type_list_labelled_cue_region:
@@ -4431,7 +4539,7 @@
 
     /* "RIFF" chunk. */
     if (pFormat->container == drwav_container_riff) {
-        drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize;   /* +28 = "WAVE" + [sizeof "fmt " chunk] */
+        drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize;   /* +36 = "WAVE" + [sizeof "fmt " chunk] + [data chunk header] */
         runningPos += drwav__write(pWav, "RIFF", 4);
         runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
         runningPos += drwav__write(pWav, "WAVE", 4);
@@ -4701,7 +4809,7 @@
     #ifdef ENOSYS
         case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
     #endif
-    #ifdef ENOTEMPTY
+    #if defined(ENOTEMPTY) && ENOTEMPTY != EEXIST   /* In AIX, ENOTEMPTY and EEXIST use the same value. */
         case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
     #endif
     #ifdef ELOOP
@@ -5158,9 +5266,40 @@
 
 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
 {
-    return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+    int whence = SEEK_SET;
+    if (origin == DRWAV_SEEK_CUR) {
+        whence = SEEK_CUR;
+    } else if (origin == DRWAV_SEEK_END) {
+        whence = SEEK_END;
+    }
+
+    return fseek((FILE*)pUserData, offset, whence) == 0;
 }
 
+DRWAV_PRIVATE drwav_bool32 drwav__on_tell_stdio(void* pUserData, drwav_int64* pCursor)
+{
+    FILE* pFileStdio = (FILE*)pUserData;
+    drwav_int64 result;
+
+    /* These were all validated at a higher level. */
+    DRWAV_ASSERT(pFileStdio != NULL);
+    DRWAV_ASSERT(pCursor    != NULL);
+
+#if defined(_WIN32)
+    #if defined(_MSC_VER) && _MSC_VER > 1200
+        result = _ftelli64(pFileStdio);
+    #else
+        result = ftell(pFileStdio);
+    #endif
+#else
+    result = ftell(pFileStdio);
+#endif
+
+    *pCursor = result;
+
+    return DRWAV_TRUE;
+}
+
 DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
@@ -5171,12 +5310,12 @@
 {
     drwav_bool32 result;
 
-    result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+    result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, drwav__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
     if (result != DRWAV_TRUE) {
         fclose(pFile);
         return result;
     }
-    
+
     result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
     if (result != DRWAV_TRUE) {
         fclose(pFile);
@@ -5349,29 +5488,34 @@
 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
 {
     drwav* pWav = (drwav*)pUserData;
+    drwav_int64 newCursor;
+
     DRWAV_ASSERT(pWav != NULL);
 
-    if (origin == drwav_seek_origin_current) {
-        if (offset > 0) {
-            if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
-                return DRWAV_FALSE; /* Trying to seek too far forward. */
-            }
-        } else {
-            if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
-                return DRWAV_FALSE; /* Trying to seek too far backwards. */
-            }
-        }
+    newCursor = pWav->memoryStream.currentReadPos;
 
-        /* This will never underflow thanks to the clamps above. */
-        pWav->memoryStream.currentReadPos += offset;
+    if (origin == DRWAV_SEEK_SET) {
+        newCursor = 0;
+    } else if (origin == DRWAV_SEEK_CUR) {
+        newCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
+    } else if (origin == DRWAV_SEEK_END) {
+        newCursor = (drwav_int64)pWav->memoryStream.dataSize;
     } else {
-        if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
-            pWav->memoryStream.currentReadPos = offset;
-        } else {
-            return DRWAV_FALSE; /* Trying to seek too far forward. */
-        }
+        DRWAV_ASSERT(!"Invalid seek origin");
+        return DRWAV_FALSE;
     }
 
+    newCursor += offset;
+
+    if (newCursor < 0) {
+        return DRWAV_FALSE;  /* Trying to seek prior to the start of the buffer. */
+    }
+    if ((size_t)newCursor > pWav->memoryStream.dataSize) {
+        return DRWAV_FALSE;  /* Trying to seek beyond the end of the buffer. */
+    }
+
+    pWav->memoryStream.currentReadPos = (size_t)newCursor;
+
     return DRWAV_TRUE;
 }
 
@@ -5418,32 +5562,48 @@
 DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
 {
     drwav* pWav = (drwav*)pUserData;
+    drwav_int64 newCursor;
+
     DRWAV_ASSERT(pWav != NULL);
 
-    if (origin == drwav_seek_origin_current) {
-        if (offset > 0) {
-            if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
-                offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos);  /* Trying to seek too far forward. */
-            }
-        } else {
-            if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
-                offset = -(int)pWav->memoryStreamWrite.currentWritePos;  /* Trying to seek too far backwards. */
-            }
-        }
+    newCursor = pWav->memoryStreamWrite.currentWritePos;
 
-        /* This will never underflow thanks to the clamps above. */
-        pWav->memoryStreamWrite.currentWritePos += offset;
+    if (origin == DRWAV_SEEK_SET) {
+        newCursor = 0;
+    } else if (origin == DRWAV_SEEK_CUR) {
+        newCursor = (drwav_int64)pWav->memoryStreamWrite.currentWritePos;
+    } else if (origin == DRWAV_SEEK_END) {
+        newCursor = (drwav_int64)pWav->memoryStreamWrite.dataSize;
     } else {
-        if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
-            pWav->memoryStreamWrite.currentWritePos = offset;
-        } else {
-            pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize;  /* Trying to seek too far forward. */
-        }
+        DRWAV_ASSERT(!"Invalid seek origin");
+        return DRWAV_INVALID_ARGS;
     }
 
+    newCursor += offset;
+
+    if (newCursor < 0) {
+        return DRWAV_FALSE;  /* Trying to seek prior to the start of the buffer. */
+    }
+    if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) {
+        return DRWAV_FALSE;  /* Trying to seek beyond the end of the buffer. */
+    }
+
+    pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor;
+
     return DRWAV_TRUE;
 }
 
+DRWAV_PRIVATE drwav_bool32 drwav__on_tell_memory(void* pUserData, drwav_int64* pCursor)
+{
+    drwav* pWav = (drwav*)pUserData;
+
+    DRWAV_ASSERT(pWav != NULL);
+    DRWAV_ASSERT(pCursor != NULL);
+
+    *pCursor = (drwav_int64)pWav->memoryStream.currentReadPos;
+    return DRWAV_TRUE;
+}
+
 DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
@@ -5455,7 +5615,7 @@
         return DRWAV_FALSE;
     }
 
-    if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
+    if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
         return DRWAV_FALSE;
     }
 
@@ -5472,7 +5632,7 @@
         return DRWAV_FALSE;
     }
 
-    if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
+    if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, drwav__on_tell_memory, pWav, pAllocationCallbacks)) {
         return DRWAV_FALSE;
     }
 
@@ -5562,25 +5722,25 @@
         if (pWav->onSeek && !pWav->isSequentialWrite) {
             if (pWav->container == drwav_container_riff) {
                 /* The "RIFF" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, 4, DRWAV_SEEK_SET)) {
                     drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
                     drwav__write_u32ne_to_le(pWav, riffChunkSize);
                 }
 
                 /* The "data" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, DRWAV_SEEK_SET)) {
                     drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
                     drwav__write_u32ne_to_le(pWav, dataChunkSize);
                 }
             } else if (pWav->container == drwav_container_w64) {
                 /* The "RIFF" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, 16, DRWAV_SEEK_SET)) {
                     drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
                     drwav__write_u64ne_to_le(pWav, riffChunkSize);
                 }
 
                 /* The "data" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, DRWAV_SEEK_SET)) {
                     drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
                     drwav__write_u64ne_to_le(pWav, dataChunkSize);
                 }
@@ -5589,13 +5749,13 @@
                 int ds64BodyPos = 12 + 8;
 
                 /* The "RIFF" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, DRWAV_SEEK_SET)) {
                     drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
                     drwav__write_u64ne_to_le(pWav, riffChunkSize);
                 }
 
                 /* The "data" chunk size. */
-                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
+                if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, DRWAV_SEEK_SET)) {
                     drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
                     drwav__write_u64ne_to_le(pWav, dataChunkSize);
                 }
@@ -5660,7 +5820,7 @@
                 bytesToSeek = 0x7FFFFFFF;
             }
 
-            if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
+            if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, DRWAV_SEEK_CUR) == DRWAV_FALSE) {
                 break;
             }
 
@@ -5807,7 +5967,7 @@
         return DRWAV_FALSE; /* No seeking in write mode. */
     }
 
-    if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
+    if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, DRWAV_SEEK_SET)) {
         return DRWAV_FALSE;
     }
 
@@ -5925,7 +6085,7 @@
 
         while (offset > 0) {
             int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
-            if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
+            if (!pWav->onSeek(pWav->pUserData, offset32, DRWAV_SEEK_CUR)) {
                 return DRWAV_FALSE;
             }
 
@@ -6098,6 +6258,13 @@
 {
     drwav_uint64 totalFramesRead = 0;
 
+    static drwav_int32 adaptationTable[] = {
+        230, 230, 230, 230, 307, 409, 512, 614,
+        768, 614, 512, 409, 307, 230, 230, 230
+    };
+    static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };
+    static drwav_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };
+
     DRWAV_ASSERT(pWav != NULL);
     DRWAV_ASSERT(framesToRead > 0);
 
@@ -6123,6 +6290,11 @@
                 pWav->msadpcm.cachedFrames[2]  = pWav->msadpcm.prevFrames[0][0];
                 pWav->msadpcm.cachedFrames[3]  = pWav->msadpcm.prevFrames[0][1];
                 pWav->msadpcm.cachedFrameCount = 2;
+
+                /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
+                if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table)) {
+                    return totalFramesRead; /* Invalid file. */
+                }
             } else {
                 /* Stereo. */
                 drwav_uint8 header[14];
@@ -6145,6 +6317,11 @@
                 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
                 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
                 pWav->msadpcm.cachedFrameCount = 2;
+
+                /* The predictor is used as an index into coeff1Table so we'll need to validate to ensure it never overflows. */
+                if (pWav->msadpcm.predictor[0] >= drwav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= drwav_countof(coeff2Table)) {
+                    return totalFramesRead; /* Invalid file. */
+                }
             }
         }
 
@@ -6178,13 +6355,6 @@
             if (pWav->msadpcm.bytesRemainingInBlock == 0) {
                 continue;
             } else {
-                static drwav_int32 adaptationTable[] = {
-                    230, 230, 230, 230, 307, 409, 512, 614,
-                    768, 614, 512, 409, 307, 230, 230, 230
-                };
-                static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460,  392 };
-                static drwav_int32 coeff2Table[] = { 0,  -256, 0, 64,  0,  -208, -232 };
-
                 drwav_uint8 nibbles;
                 drwav_int32 nibble0;
                 drwav_int32 nibble1;
@@ -6317,7 +6487,7 @@
                 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
 
                 if (header[2] >= drwav_countof(stepTable)) {
-                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
+                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
                     pWav->ima.bytesRemainingInBlock = 0;
                     return totalFramesRead; /* Invalid data. */
                 }
@@ -6335,7 +6505,7 @@
                 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
 
                 if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
-                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
+                    pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, DRWAV_SEEK_CUR);
                     pWav->ima.bytesRemainingInBlock = 0;
                     return totalFramesRead; /* Invalid data. */
                 }
@@ -7830,7 +8000,7 @@
     }
 
     for (i = 0; i < sampleCount; ++i) {
-        *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+        *pOut++ = (drwav_int32)(2147483648.0f * pIn[i]);
     }
 }
 
@@ -8003,7 +8173,7 @@
 
 
 
-DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     drwav wav;
 
@@ -8017,14 +8187,14 @@
         *totalFrameCountOut = 0;
     }
 
-    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
         return NULL;
     }
 
     return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
 }
 
-DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     drwav wav;
 
@@ -8038,14 +8208,14 @@
         *totalFrameCountOut = 0;
     }
 
-    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
         return NULL;
     }
 
     return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
 }
 
-DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
 {
     drwav wav;
 
@@ -8059,7 +8229,7 @@
         *totalFrameCountOut = 0;
     }
 
-    if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+    if (!drwav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
         return NULL;
     }
 
@@ -8347,6 +8517,36 @@
 /*
 REVISION HISTORY
 ================
+v0.14.0 - TBD
+  - API CHANGE: Seek origin enums have been renamed to the following:
+    - drwav_seek_origin_start   -> DRWAV_SEEK_SET
+    - drwav_seek_origin_current -> DRWAV_SEEK_CUR
+    - DRWAV_SEEK_END (new)
+  - API CHANGE: A new seek origin has been added to allow seeking from the end of the file. If you implement your own `onSeek` callback, you must now handle `DRWAV_SEEK_END`. If you only use `*_init_file()` or `*_init_memory()`, you need not change anything.
+  - API CHANGE: An `onTell` callback has been added to the following functions:
+    - drwav_init()
+    - drwav_init_ex()
+    - drwav_init_with_metadata()
+    - drwav_open_and_read_pcm_frames_s16()
+    - drwav_open_and_read_pcm_frames_f32()
+    - drwav_open_and_read_pcm_frames_s32()
+  - API CHANGE: The `firstSampleByteOffset`, `lastSampleByteOffset` and `sampleByteOffset` members of `drwav_cue_point` have been renamed to `firstSampleOffset`, `lastSampleOffset` and `sampleOffset`, respectively.
+  - Fix a static analysis warning.
+  - Fix compilation for AIX OS.
+
+v0.13.17 - 2024-12-17
+  - Fix a possible crash when reading from MS-ADPCM encoded files.
+  - Improve detection of ARM64EC
+
+v0.13.16 - 2024-02-27
+  - Fix a Wdouble-promotion warning.
+
+v0.13.15 - 2024-01-23
+  - Relax some unnecessary validation that prevented some files from loading.
+
+v0.13.14 - 2023-12-02
+  - Fix a warning about an unused variable.
+
 v0.13.13 - 2023-11-02
   - Fix a warning when compiling with Clang.
 
diff --git a/raylib/src/external/fix_win32_compatibility.h b/raylib/src/external/fix_win32_compatibility.h
new file mode 100644
--- /dev/null
+++ b/raylib/src/external/fix_win32_compatibility.h
@@ -0,0 +1,63 @@
+/**********************************************************************************************
+*
+*   fix_win32_compatibility.h   Utility to help include windows.h with raylib projects
+*
+*   Useage: #include "fix_win32_compatibility.h" any library that uses windows.h
+*                                     order is critical, this must be done before raylib
+*
+*   LICENSE: MIT
+*
+*   Copyright (c) 2025 Jeffery Myers
+*
+*   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 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.
+*
+**********************************************************************************************/
+
+#pragma once
+
+// move the windows functions to new names
+// note that you can't call these functions or structures from your code, but you should not neeed to
+#define CloseWindow CloseWindowWin32
+#define Rectangle RectangleWin32
+#define ShowCursor ShowCursorWin32
+#define LoadImageA LoadImageAWin32
+#define LoadImageW LoadImageWin32
+#define DrawTextA DrawTextAWin32
+#define DrawTextW DrawTextWin32
+#define DrawTextExA DrawTextExAWin32
+#define DrawTextExW DrawTextExWin32
+#define PlaySoundA PlaySoundAWin32
+// include windows
+#define WIN32_LEAN_AND_MEAN 
+#include <windows.h>
+
+// remove all our redfintions so that raylib can define them properly
+#undef CloseWindow
+#undef Rectangle
+#undef ShowCursor
+#undef LoadImage 
+#undef LoadImageA
+#undef LoadImageW
+#undef DrawText 
+#undef DrawTextA
+#undef DrawTextW
+#undef DrawTextEx 
+#undef DrawTextExA
+#undef DrawTextExW
+#undef PlaySoundA
diff --git a/raylib/src/external/glfw/src/context.c b/raylib/src/external/glfw/src/context.c
--- a/raylib/src/external/glfw/src/context.c
+++ b/raylib/src/external/glfw/src/context.c
@@ -359,7 +359,7 @@
     window->context.source = ctxconfig->source;
     window->context.client = GLFW_OPENGL_API;
 
-    previous = _glfwPlatformGetTls(&_glfw.contextSlot);
+    previous = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
     glfwMakeContextCurrent((GLFWwindow*) window);
     if (_glfwPlatformGetTls(&_glfw.contextSlot) != window)
         return GLFW_FALSE;
@@ -615,12 +615,12 @@
 
 GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle)
 {
-    _GLFWwindow* window = (_GLFWwindow*) handle;
+    _GLFWwindow* window = (_GLFWwindow *) handle;
     _GLFWwindow* previous;
 
     _GLFW_REQUIRE_INIT();
 
-    previous = _glfwPlatformGetTls(&_glfw.contextSlot);
+    previous = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
 
     if (window && window->context.client == GLFW_NO_API)
     {
@@ -642,12 +642,12 @@
 GLFWAPI GLFWwindow* glfwGetCurrentContext(void)
 {
     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
-    return _glfwPlatformGetTls(&_glfw.contextSlot);
+    return (GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
 }
 
 GLFWAPI void glfwSwapBuffers(GLFWwindow* handle)
 {
-    _GLFWwindow* window = (_GLFWwindow*) handle;
+    _GLFWwindow* window = (_GLFWwindow *) handle;
     assert(window != NULL);
 
     _GLFW_REQUIRE_INIT();
@@ -668,7 +668,7 @@
 
     _GLFW_REQUIRE_INIT();
 
-    window = _glfwPlatformGetTls(&_glfw.contextSlot);
+    window = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
     if (!window)
     {
         _glfwInputError(GLFW_NO_CURRENT_CONTEXT,
@@ -686,7 +686,7 @@
 
     _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
 
-    window = _glfwPlatformGetTls(&_glfw.contextSlot);
+    window = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
     if (!window)
     {
         _glfwInputError(GLFW_NO_CURRENT_CONTEXT,
@@ -752,7 +752,7 @@
 
     _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
 
-    window = _glfwPlatformGetTls(&_glfw.contextSlot);
+    window = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
     if (!window)
     {
         _glfwInputError(GLFW_NO_CURRENT_CONTEXT,
@@ -762,4 +762,3 @@
 
     return window->context.getProcAddress(procname);
 }
-
diff --git a/raylib/src/external/glfw/src/egl_context.c b/raylib/src/external/glfw/src/egl_context.c
--- a/raylib/src/external/glfw/src/egl_context.c
+++ b/raylib/src/external/glfw/src/egl_context.c
@@ -118,10 +118,10 @@
         return GLFW_FALSE;
     }
 
-    nativeConfigs = _glfw_calloc(nativeCount, sizeof(EGLConfig));
+    nativeConfigs = (EGLConfig *)_glfw_calloc(nativeCount, sizeof(EGLConfig));
     eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount);
 
-    usableConfigs = _glfw_calloc(nativeCount, sizeof(_GLFWfbconfig));
+    usableConfigs = (_GLFWfbconfig *)_glfw_calloc(nativeCount, sizeof(_GLFWfbconfig));
     usableCount = 0;
 
     for (i = 0;  i < nativeCount;  i++)
@@ -308,7 +308,7 @@
 
 static GLFWglproc getProcAddressEGL(const char* procname)
 {
-    _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
+    _GLFWwindow* window = (_GLFWwindow *)_glfwPlatformGetTls(&_glfw.contextSlot);
     assert(window != NULL);
 
     if (window->context.egl.client)
@@ -883,7 +883,7 @@
 
 GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)
 {
-    _GLFWwindow* window = (_GLFWwindow*) handle;
+    _GLFWwindow* window = (_GLFWwindow *) handle;
     _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT);
 
     if (window->context.source != GLFW_EGL_CONTEXT_API)
@@ -897,7 +897,7 @@
 
 GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle)
 {
-    _GLFWwindow* window = (_GLFWwindow*) handle;
+    _GLFWwindow* window = (_GLFWwindow *) handle;
     _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE);
 
     if (window->context.source != GLFW_EGL_CONTEXT_API)
@@ -908,4 +908,3 @@
 
     return window->context.egl.surface;
 }
-
diff --git a/raylib/src/external/glfw/src/mappings.h b/raylib/src/external/glfw/src/mappings.h
--- a/raylib/src/external/glfw/src/mappings.h
+++ b/raylib/src/external/glfw/src/mappings.h
@@ -61,942 +61,1849 @@
 const char* _glfwDefaultMappings[] =
 {
 #if defined(_GLFW_WIN32)
-"03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
-"03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,",
-"03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,",
-"03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
-"03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,",
-"03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,",
-"03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,",
-"030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
-"03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,",
-"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,",
-"03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,",
-"030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,",
-"030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
-"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
-"03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
-"03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
-"030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,",
-"03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,",
-"030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,",
-"030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,",
-"030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,",
-"030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,",
-"03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
-"030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,",
-"030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,",
-"030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
-"030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
-"030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
-"030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,",
-"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,",
-"030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
-"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,",
-"03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
-"03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,",
-"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,",
-"03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,",
-"030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
-"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,",
-"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
-"0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,",
-"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
-"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,",
-"03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
-"030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,",
-"03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,",
-"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,",
-"03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,",
-"030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000d620000013a7000000000000,NSW wired controller,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,",
-"03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,",
-"03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,",
-"03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
-"03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
-"03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,",
-"030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
-"030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,",
-"03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
-"030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,",
-"03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
-"03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,",
-"03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,",
-"0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
-"0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
-"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
-"03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,",
-"03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,",
-"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
-"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
-"03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
-"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,",
-"0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,",
-"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,",
-"03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,",
-"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
-"030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
-"030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,",
-"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
-"03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
-"03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,",
-"03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows,",
-"030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
-"03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
-"0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,",
-"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
-"03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
-"03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
-"03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
-"03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
-"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
-#endif // _GLFW_WIN32
-
-#if defined(_GLFW_COCOA)
-"030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X,",
-"03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,",
-"03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,",
-"03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,",
-"03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,",
-"03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,",
-"030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,",
-"03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
-"03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
-"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,",
-"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,",
-"0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,",
-"03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
-"03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
-"03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,",
-"03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,",
-"03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,",
-"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000d620000011a7000010050000,Nintendo Switch PowerA Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
-"03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,",
-"030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
-"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
-"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,",
-"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,",
-"03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,",
-"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
-"0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,",
-"030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,",
-"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
-"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
-"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,",
-"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
-"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
-"03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,",
-"030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,",
-"03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,",
-"030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
-"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,",
-"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,",
-"030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000006f0e00000104000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
-"030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,",
-"03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-"03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
-#endif // _GLFW_COCOA
-
-#if defined(GLFW_BUILD_LINUX_JOYSTICK)
-"03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
-"030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,",
-"05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
-"03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
-"05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
-"050000005e040000e002000030110000,8BitDo Zero 2 (XInput),a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
-"05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
-"03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
-"05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
-"03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,",
-"05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,",
-"03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
-"05000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
-"03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,",
-"03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,",
-"03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,",
-"03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
-"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,",
-"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
-"03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
-"030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
-"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,",
-"030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
-"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,",
-"03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,",
-"030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
-"030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,",
-"030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
-"050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
-"03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,",
-"03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,",
-"0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
-"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
-"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
-"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,",
-"050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,",
-"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,",
-"050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,",
-"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,",
-"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,",
-"050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,",
-"0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
-"03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
-"030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
-"030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
-"030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
-"05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
-"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
-"05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
-"03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
-"030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux,",
-"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,",
-"060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
-"060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
-"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,",
-"03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,",
-"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux,",
-"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
-"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
-"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,",
-"050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,",
-"050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
-"05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,",
-"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,",
-"05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,",
-"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,",
-"19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,",
-"030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
-"03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux,",
-"05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
-"05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
-"03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,",
-"03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000d62000000228000001010000,PowerA Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c62400001a54000001010000,PowerA Xbox One Mini Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
-"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,",
-"030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,",
-"030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
-"030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
-"030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
-"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,",
-"0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
-"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,",
-"03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,",
-"03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,",
-"03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,",
-"03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
-"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
-"03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
-"03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,",
-"03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005f140000c501000010010000,SHANWAN Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000004c050000e60c000011810000,Sony DualSense,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"050000004c050000e60c000000810000,Sony DualSense ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
-"03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
-"030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,",
-"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,",
-"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,",
-"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
-"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
-"03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000008f0e00001431000010010000,SZMY-POWER CO. LTD. PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
-"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
-"030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,",
-"030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux,",
-"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,",
-"030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
-"030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
-"03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
-"030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
-"03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,",
-"030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
-"05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
-"030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
-"030000005e040000120b000005050000,XBox Series pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
-"03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
-"03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
-"05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,",
-"03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
-"03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
-"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,",
+"03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,",
+"03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,",
+"03000000d0160000060d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,",
+"03000000d0160000070d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,",
+"03000000d0160000600a000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,",
+"03000000c82d00000031000000000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000531000000000000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000951000000000000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,",
+"03000000008000000210000000000000,8BitDo F30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"030000003512000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000c82d00001028000000000000,8BitDo F30 Arcade Joystick,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d000011ab000000000000,8BitDo F30 Arcade Joystick,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000801000000900000000000000,8BitDo F30 Arcade Stick,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00006a28000000000000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Windows,",
+"03000000c82d00001251000000000000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000451000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightx:a3,righty:a5,start:b11,platform:Windows,",
+"03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d0000e002000000000000,8BitDo N30,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b6,platform:Windows,",
+"03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000290000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,",
+"03000000c82d00003038000000000000,8BitDo N64,+rightx:b9,+righty:b3,-rightx:b4,-righty:b8,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,platform:Windows,",
+"03000000c82d00006928000000000000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,platform:Windows,",
+"03000000c82d00002590000000000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"030000003512000012ab000000000000,8BitDo NES30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Windows,",
+"03000000c82d000012ab000000000000,8BitDo NES30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000022000000090000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000203800000900000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00002038000000000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000751000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000851000000000000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000361000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000660000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000131000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000231000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000331000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000431000000000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00002867000000000000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b10,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000060000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000061000000000000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000102800000900000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00003028000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"030000003512000020ab000000000000,8BitDo SN30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000351000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a2,rightshoulder:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00001230000000000000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001b30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001d30000000000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001530000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001630000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001730000000000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,",
+"03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,",
+"030000008f0e00001200000000000000,Acme GA02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000c01100000355000000000000,Acrux,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000fa190000f0ff000000000000,Acteck AGJ 3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d1180000402c000000000000,ADT1,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a5,x:b3,y:b4,platform:Windows,",
+"030000006f0e00008801000000000000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001301000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00001302000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00001304000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00001413000000000000,Afterglow Xbox Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00003901000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ab1200000103000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000000f9000000000000,Afterglow Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000100000008200000000000000,Akishop Customs PS360,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,",
+"0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000020500000913000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000373500000710000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000373500004610000000000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000190e00000110000000000000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Windows,",
+"03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows,",
+"03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000869800002500000000000000,Astro C40 TR PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000050b00000579000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows,",
+"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b14,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,",
+"030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,",
+"030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,",
+"030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,",
+"03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,",
+"030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ad1b000001f9000000000000,BB 070,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000bc2000005250000000000000,Beitong G3,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a3,righty:a4,start:b15,x:b3,y:b4,platform:Windows,",
+"030000000d0500000208000000000000,Belkin Nostromo N40,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000bc2000000055000000000000,Betop BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000790000000700000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000808300000300000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000bc2000006321000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000006f0e00006401000000000000,BF One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000300f00000202000000000000,Bigben,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a5,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006b1400000209000000000000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000120c0000200e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000210e000000000000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f10e000000000000,Brook PS2 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000120c0000310c000000000000,Brook Super Converter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d81d00000b00000000000000,Buffalo BSGP1601 Series,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,",
+"030000005a1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,",
+"030000005b1c00002400000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,",
+"030000005b1c00002500000000000000,Capcom Home Arcade Controller,a:b3,b:b4,back:b7,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b6,x:b0,y:b1,platform:Windows,",
+"030000006d04000042c2000000000000,ChillStream,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000b0400003365000000000000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,",
+"030000004c050000c505000000000000,CronusMax Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d814000007cd000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d8140000cefa000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,",
+"030000003807000002cb000000000000,Cyborg,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000a306000022f6000000000000,Cyborg V.3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000f806000000a3000000000000,DA Leader,a:b7,b:b6,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b8,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:b3,rightx:a2,righty:a3,start:b12,x:b4,y:b5,platform:Windows,",
+"030000001a1c00000001000000000000,Datel Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000791d00000103000000000000,Dual Box Wii,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000c0160000e105000000000000,Dual Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f040000070f000000000000,Dual Power,a:b8,b:b9,back:b4,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,leftshoulder:b13,leftstick:b6,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b12,rightstick:b7,righttrigger:b15,start:b5,x:b10,y:b11,platform:Windows,",
+"030000004f04000012b3000000000000,Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"030000004f04000020b3000000000000,Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"03000000bd12000002e0000000000000,Dual Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000ff1100003133000000000000,DualForce,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b1,platform:Windows,",
+"030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,",
+"03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
+"03000095090000010000000000000000,Elecom JC-U609,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,",
+"0300004112000000e500000000000000,Elecom JC-U909Z,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,",
+"03000041120000001050000000000000,Elecom JC-U911,a:b1,b:b2,back:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b0,x:b4,y:b5,platform:Windows,",
+"030000006e0500000520000000000000,Elecom P301U PlayStation Controller Adapter,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
+"03000000411200004450000000000000,Elecom U1012,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
+"030000006e0500000320000000000000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
+"030000006e0500000e20000000000000,Elecom U3912T,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
+"030000006e0500000f20000000000000,Elecom U4013S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,",
+"030000006e0500001320000000000000,Elecom U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006e0500001020000000000000,Elecom U4113S,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,",
+"030000006e0500000720000000000000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
+"030000007d0400000640000000000000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000430b00000300000000000000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000062000001801000000000000,EMS TrioLinker Plus II,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Windows,",
+"03000000242f000000b7000000000000,ESM 9110,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000101c0000181c000000000000,Essential,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b4,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000341a00000108000000000000,EXEQ RF Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000006f0e00008401000000000000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00008101000000000000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00008001000000000000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000021000000090000000000000,FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,",
+"0300000011040000c600000000000000,FC801,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000852100000201000000000000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ad1b000028f0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b00002ef0000000000000,Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000038f0000000000000,Fightpad TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,",
+"03005036852100000000000000000000,Final Fantasy XIV Online Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000f806000001a3000000000000,Firestorm,a:b9,b:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b0,leftstick:b10,lefttrigger:b1,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b12,x:b8,y:b4,platform:Windows,",
+"03000000b50700000399000000000000,Firestorm 2,a:b2,b:b4,back:b10,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,righttrigger:b9,start:b11,x:b3,y:b5,platform:Windows,",
+"03000000b50700001302000000000000,Firestorm D3,a:b0,b:b2,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,x:b1,y:b3,platform:Windows,",
+"03000000b40400001024000000000000,Flydigi Apex,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000151900004000000000000000,Flydigi Vader 2,a:b27,b:b26,back:b19,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b23,leftstick:b17,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b22,rightstick:b16,righttrigger:b20,rightx:a3,righty:a4,start:b18,x:b25,y:b24,platform:Windows,",
+"03000000b40400001124000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b14,paddle1:b4,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b2,y:b3,platform:Windows,",
+"03000000b40400001224000000000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"030000008305000000a0000000000000,G08XU,a:b0,b:b1,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b5,x:b2,y:b3,platform:Windows,",
+"0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,",
+"03000000260900002625000000000000,GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000341a000005f7000000000000,GameCube Controller,a:b2,b:b3,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b1,y:b0,platform:Windows,",
+"03000000430b00000500000000000000,GameCube Controller,a:b0,b:b2,dpdown:b10,dpleft:b8,dpright:b9,dpup:b11,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a3,rightx:a5,righty:a2,start:b7,x:b1,y:b3,platform:Windows,",
+"03000000790000004718000000000000,GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"030000008f0e00000d31000000000000,Gamepad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ac0500003d03000000000000,GameSir G3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000ac0500005b05000000000000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000373500009410000000000000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000000d0f00001110000000000000,GameStick Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"0300000047530000616d000000000000,GameStop,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows,",
+"030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000c9110000f055000000000000,GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000300f00000b01000000000000,GGE909 Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000f0250000c283000000000000,Gioteck PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000f025000031c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000f0250000c383000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000f0250000c483000000000000,Gioteck VX2 PlayStation Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d11800000094000000000000,Google Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,",
+"030000004f04000026b3000000000000,GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"0300000079000000d418000000000000,GPD Win,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c6240000025b000000000000,GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000007d0400000840000000000000,Gravis Destroyer Tilt,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,",
+"030000007d0400000540000000000000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000280400000140000000000000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a3,dpup:-a4,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000008f0e00000610000000000000,GreenAsia,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a5,righty:a2,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000ac0500006b05000000000000,GT2a,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000008a2e0000dd10000000000000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Windows,",
+"030000008a2e0000df10000000000000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Windows,",
+"030000000d0f00004900000000000000,Hatsune Miku Sho PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000001008000001e1000000000000,Havit HV G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,",
+"030000000d0f00000c00000000000000,HEXT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d81400000862000000000000,HitBox Edition Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000632500002605000000000000,HJD X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"030000000d0f00000a00000000000000,Hori DOA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00008500000000000000,Hori Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002500000000000000,Hori Fighting Commander 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00008800000000000000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
+"030000000d0f00008700000000000000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f00001000000000000000,Hori Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00003200000000000000,Hori Fightstick 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000c000000000000000,Hori Fightstick 4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00000d00000000000000,Hori Fightstick EX2,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000000d0f00003701000000000000,Hori Fightstick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Windows,",
+"030000000d0f00004000000000000000,Hori Fightstick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002100000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002700000000000000,Hori Fightstick V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000a000000000000000,Hori Grip TAC4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000a500000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000a600000000000000,Hori Miku Project Diva X HD PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f00000101000000000000,Hori Mini Hatsune Miku FT,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00003801000000000000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Windows,",
+"030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002301000000000000,Hori PS4 Controller Light,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"030000000d0f00001100000000000000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00002600000000000000,Hori Real Arcade Pro 3P,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00004b00000000000000,Hori Real Arcade Pro 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00008a00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00008b00000000000000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006f00000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00007000000000000000,Hori Real Arcade Pro 4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00003d00000000000000,Hori Real Arcade Pro N3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b4,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b6,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000ae00000000000000,Hori Real Arcade Pro N4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00008c00000000000000,Hori Real Arcade Pro P4,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f0000aa00000000000000,Hori Real Arcade Pro S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000d800000000000000,Hori Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Windows,",
+"030000000d0f00002200000000000000,Hori Real Arcade Pro V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005b00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005c00000000000000,Hori Real Arcade Pro V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000af00000000000000,Hori Real Arcade Pro VHS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00001b00000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000002f5000000000000,Hori Real Arcade Pro VX,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Windows,",
+"030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000c900000000000000,Hori Taiko Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006400000000000000,Horipad 3TP,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00001300000000000000,Horipad 3W,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006e00000000000000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f00006600000000000000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000000d0f00004200000000000000,Horipad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ad1b000001f5000000000000,Horipad EXT2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f0000ee00000000000000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000c100000000000000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000000d0f0000f600000000000000,Horipad Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000000d0f00000202000000000000,Horipad O Nintendo Switch 2 Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,misc2:b14,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
+"030000000d0f00006700000000000000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000000d0f00009601000000000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc2:b2,paddle1:b5,paddle2:b15,paddle3:b18,paddle4:b19,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"030000000d0f0000dc00000000000000,Horipad Switch,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows,",
+"03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows,",
+"03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,",
+"03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows,",
+"03000000242f00000a20000000000000,Hyperkin Scout,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000242e00000a20000000000000,Hyperkin Scout Premium SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows,",
+"03000000f00300008d04000000000000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:-a2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:+a5,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,",
+"03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows,",
+"03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows,",
+"03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000ef0500000300000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,",
+"03000000fd0500000230000000000000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,",
+"03000000fd0500000030000000000000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows,",
+"03000000fd0500003902000000000000,InterAct Hammerhead,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,rightshoulder:b7,rightstick:b5,righttrigger:b9,start:b10,x:b0,y:b1,platform:Windows,",
+"03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,",
+"03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,",
+"03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows,",
+"03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000491900000204000000000000,Ipega PG9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000491900000304000000000000,Ipega PG9087,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,",
+"030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,",
+"030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000250900000017000000000000,Joypad Adapter,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000bd12000003c0000000000000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ff1100004033000000000000,JPD FFB,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a2,start:b15,x:b3,y:b0,platform:Windows,",
+"03000000242f00002d00000000000000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000242f00008a00000000000000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
+"03000000c4100000c082000000000000,KADE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000828200000180000000000000,Keio,a:b4,b:b5,back:b8,leftshoulder:b2,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000bd12000001e0000000000000,Leadership,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"030000006f0e00000103000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00000104000000000000,Logic3,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000008f0e00001300000000000000,Logic3,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,",
+"030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d0400001ec2000000000000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006d0400001ac2000000000000,Logitech Precision,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d04000009c2000000000000,Logitech WingMan,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,",
+"030000006d0400000bc2000000000000,Logitech WingMan Action Pad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:a5~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:a2~,start:b8,x:b3,y:b4,platform:Windows,",
+"030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,",
+"03000000380700005645000000000000,Lynx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000222200006000000000000000,Macally,a:b1,b:b2,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700003888000000000000,Mad Catz Arcade Fightstick TE S Plus PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008532000000000000,Mad Catz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700006352000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700006652000000000000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700005032000000000000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700005082000000000000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000380700008031000000000000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000003807000038b7000000000000,Mad Catz Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700008433000000000000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008483000000000000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000380700008134000000000000,Mad Catz Fightstick TE2 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008184000000000000,Mad Catz Fightstick TE2 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000380700006252000000000000,Mad Catz Micro CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008232000000000000,Mad Catz PlayStation Brawlpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008731000000000000,Mad Catz PlayStation Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000003807000056a8000000000000,Mad Catz PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700001888000000000000,Mad Catz SFIV Fightstick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000380700008081000000000000,Mad Catz SFV Arcade Fightstick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000380700001847000000000000,Mad Catz Street Fighter 4 Xbox 360 FightStick,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,",
+"030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,",
+"030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000242f00003700000000000000,Mayflash F101,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000003018000000000000,Mayflash F300 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000242f00003900000000000000,Mayflash F300 Elite Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,",
+"0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000242f0000f500000000000000,Mayflash N64 Adapter,a:b2,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,",
+"03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,",
+"03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,",
+"030000008f0e00001030000000000000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000790000000318000000000000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000002418000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"0300000079000000ae18000000000000,Mega Drive Controller,a:b0,b:b1,back:b7,dpdown:b14,dpleft:b15,dpright:b13,dpup:b2,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000c0160000990a000000000000,Mega Drive Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,righttrigger:b2,start:b3,platform:Windows,",
+"030000005e0400002800000000000000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Windows,",
+"030000005e0400000300000000000000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,",
+"030000005e0400000700000000000000,Microsoft SideWinder,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"030000005e0400000e00000000000000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b8,x:b3,y:b4,platform:Windows,",
+"030000005e0400002700000000000000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Windows,",
+"03000000280d00000202000000000000,Miller Lite Cantroller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b5,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,platform:Windows,",
+"03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d6200000ad0d000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c62400002a89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c62400002b89000000000000,Moga XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c62400001a89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c62400001b89000000000000,Moga XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows,",
+"03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,",
+"030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000eb0300000000000000000000,NeGcon Adapter,a:a2,b:b13,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,lefttrigger:a4,leftx:a1,righttrigger:b11,start:b3,x:a3,y:b12,platform:Windows,",
+"0300000038070000efbe000000000000,NEO SE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"0300000092120000474e000000000000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000921200004b46000000000000,NES 2 port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,",
+"03000000000f00000100000000000000,NES Controller,a:b1,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,",
+"03000000921200004346000000000000,NES Controller,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,",
+"03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000050b00000045000000000000,Nexus,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Windows,",
+"03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
+"030000007e0500006920000000000000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Windows,",
+"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,",
+"030000007e0500007320000000000000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Windows,",
+"030000007e0500001920000000000000,NSO N64 Controller,+rightx:b8,+righty:b2,-rightx:b3,-righty:b7,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Windows,",
+"030000007e0500001720000000000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000550900001472000000000000,NVIDIA Controller,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,",
+"03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows,",
+"030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000d62000001d57000000000000,Nyko Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,",
+"030000000d0f00000401000000000000,Onyx,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000008916000001fd000000000000,Onza CE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a3,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,",
+"0300000009120000072f000000000000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:-a2,leftx:a0,lefty:a1,righttrigger:-a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,",
+"03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00008901000000000000,PDP Realmz Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows,",
+"03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d620000013a7000000000000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d62000003340000000000000,PowerA OPS Pro Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000d62000002640000000000000,PowerA OPS Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"0300000062060000d570000000000000,PowerA PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d620000014a7000000000000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d04000084ca000000000000,Precision,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000c62400001a53000000000000,Pro Ex Mini,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000120c0000110e000000000000,Pro5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000250900000088000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000250900006888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b6,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000151a00006222000000000000,PS2 Dual Plus Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000120a00000100000000000000,PS3 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000120c00001307000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000120c00001cf1000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f90e000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000250900000118000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000250900000218000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,",
+"030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,",
+"030000004f1f00000800000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000888800000804000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Windows,",
+"030000008f0e00000300000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000ba2200002010000000000000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000120c00000807000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000111e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000121e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000130e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000150e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000180e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000181e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000191e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c00001e0e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000a957000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000aa57000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f21c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f31c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f41c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f51c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120c0000f70e000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000120e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000160e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000001a1e0000120c000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Windows,",
+"03000000300f00000111000000000000,Qanba 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00000211000000000000,Qanba 2P,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000300f00000011000000000000,Qanba Arcade Stick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,",
+"03000000300f00001611000000000000,Qanba Arcade Stick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,",
+"03000000222c00000025000000000000,Qanba Dragon Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000222c00000020000000000000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00001211000000000000,Qanba Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00001210000000000000,Qanba Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000341a00000104000000000000,Qanba Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,",
+"03000000222c00000223000000000000,Qanba Obsidian Arcade Stick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000222c00000023000000000000,Qanba Obsidian Arcade Stick PS4,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000008a2400006682000000000000,R1 Mobile Controller,a:b3,b:b1,back:b7,leftx:a0,lefty:a1,start:b6,x:b4,y:b0,platform:Windows,",
+"03000000086700006626000000000000,RadioShack,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000ff1100004733000000000000,Ramox FPS Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,",
+"030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,",
+"030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,",
+"030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,",
+"030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows,",
+"030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,",
+"030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,",
+"030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,",
+"030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,",
+"030000009b2800003c00000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,",
+"030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,",
+"030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,",
+"030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,",
+"030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,",
+"030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,",
+"030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,",
+"030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,",
+"030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,",
+"030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,",
+"030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,",
+"03000000790000008f18000000000000,Rapoo Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"0300000032150000a602000000000000,Razer Huntsman V3 Pro,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b12,dpright:b13,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000f8270000bf0b000000000000,Razer Kishi,a:b6,b:b7,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b18,leftshoulder:b12,leftstick:b19,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b13,rightstick:b20,righttrigger:b15,rightx:a3,righty:a4,start:b17,x:b9,y:b10,platform:Windows,",
+"03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000321500000010000000000000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000321500000710000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000a10000000000000,Razer Raiju TE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000410000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000910000000000000,Razer Raiju UE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000321500000011000000000000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows,",
+"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000632500007805000000000000,Retro Fighters Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,",
+"03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,",
+"03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,",
+"0300000000f000000300000000000000,RetroUSB RetroPad,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
+"0300000000f00000f100000000000000,RetroUSB Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,",
+"03000000830500000960000000000000,Revenger,a:b0,b:b1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b5,platform:Windows,",
+"030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006b140000020d000000000000,Revolution Pro Controller 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001f01000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00004601000000000000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c6240000fefa000000000000,Rock Candy Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00008701000000000000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows,",
+"03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"03000000050b00005819000000000000,ROG Chakram Core,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"03000000050b0000181a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,",
+"030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,platform:Windows,",
+"030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e00001311000000000000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows,",
+"03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000a306000023f6000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00001201000000000000,Saitek Dual Analog,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,",
+"03000000a30600000cff000000000000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,",
+"03000000a30600000d5f000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
+"03000000a30600000dff000000000000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b8,x:b0,y:b3,platform:Windows,",
+"03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000a306000018f5000000000000,Saitek P3200,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00001001000000000000,Saitek P480 Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000a30600000901000000000000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b8,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b5,rightx:a3,righty:a2,x:b0,y:b1,platform:Windows,",
+"03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000a30600002106000000000000,Saitek PS1000 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows,",
+"03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,",
+"03000000c01100004150000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"03000000c01100004450000000000000,Sanwa Online Grip,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b14,x:b3,y:b4,platform:Windows,",
+"03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Windows,",
+"03000000830500006120000000000000,Sanwa Smart Grip II,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,x:b1,y:b3,platform:Windows,",
+"03000000c01100000051000000000000,Satechi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,",
+"03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,",
+"0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,",
+"0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows,",
+"03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,",
+"03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,",
+"030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,",
+"03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,",
+"03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,",
+"030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,",
+"03000000921200004653000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,",
+"030000008f0e00000910000000000000,Sony DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000317300000100000000000000,Sony DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000666600006706000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000e30500009605000000000000,Sony PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000fe1400002a23000000000000,Sony PlayStation Adapter,a:b0,b:b1,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,x:b2,y:b3,platform:Windows,",
+"030000004c050000da0c000000000000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000632500002306000000000000,Sony PlayStation Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000f0250000c183000000000000,Sony PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000d9040000160f000000000000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000ff000000cb01000000000000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"030000004c0500003713000000000000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000341a00000208000000000000,Speedlink 6555,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000341a00000908000000000000,Speedlink 6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000380700001722000000000000,Speedlink Competition Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows,",
+"030000008f0e00000800000000000000,Speedlink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000de280000fc11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000de280000ff11000000000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000380700003847000000000000,Street Fighter Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,",
+"030000001f08000001e4000000000000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000790000000418000000000000,Super Famicom Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b33,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000341200001300000000000000,Super Racer,a:b2,b:b3,back:b8,leftshoulder:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b7,x:b0,y:b1,platform:Windows,",
+"03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,",
+"030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,",
+"03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,",
+"03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000ba2200000701000000000000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b2,platform:Windows,",
+"03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,",
+"03000000790000001c18000000000000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000790000002601000000000000,TGZ Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows,",
+"030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f0400000ed0000000000000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f04000008d0000000000000,Thrustmaster Ferrari 150 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,",
+"030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,",
+"030000004f04000003d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000004f04000009d0000000000000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006d04000088ca000000000000,Thunderpad,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000666600000288000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000666600000488000000000000,TigerGame PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"030000004f04000007d0000000000000,TMini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000571d00002100000000000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Windows,",
+"03000000571d00002000000000000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000c01100000055000000000000,Tronsmart,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,",
+"03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,",
+"03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000151900005678000000000000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"030000000b0400003065000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000242f00006e00000000000000,USB Controller,a:b1,b:b4,back:b10,leftshoulder:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b7,rightx:a2,righty:a5,start:b11,x:b0,y:b3,platform:Windows,",
+"03000000300f00000701000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000341a00002308000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000666600000188000000000000,USB Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,",
+"030000006b1400000203000000000000,USB Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000790000000a00000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000b404000081c6000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000b50700001503000000000000,USB Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b0,y:b1,platform:Windows,",
+"03000000bd12000012d0000000000000,USB Controller,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ff1100004133000000000000,USB Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000632500002305000000000000,USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000882800000305000000000000,V5 Game Pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,x:b2,y:b3,platform:Windows,",
+"03000000790000001a18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000790000001b18000000000000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000006f0e00000302000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"030000006f0e00000702000000000000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,",
+"0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,",
+"03000000120c0000ab57000000000000,Warrior Joypad JS083,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"030000007e0500003003000000000000,Wii U Pro,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b6,leftstick:b11,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b12,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,",
+"0300000032150000030a000000000000,Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"0300000032150000140a000000000000,Wolverine,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000002e160000efbe000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,rightshoulder:b5,righttrigger:b11,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700001647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700002045000000000000,Xbox 360 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"03000000380700002644000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a5,start:b8,x:b2,y:b3,platform:Windows,",
+"03000000380700002647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000003807000026b7000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700003647000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a7,righty:a5,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400001907000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400009102000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000000fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000001fd000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b000016f0000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000ad1b00008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c62400000053000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c6240000fdfa000000000000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700002847000000000000,Xbox 360 Fightpad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000a102000000000000,Xbox 360 Wireless Receiver,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000120c00000a88000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000120c00001088000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2~,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5~,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000002a0600002000000000000000,Xbox Controller,a:b0,b:b1,back:b13,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b5,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b15,righttrigger:b7,rightx:a2,righty:a5,start:b12,x:b2,y:b3,platform:Windows,",
+"03000000380700001645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000380700002645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000380700003645000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"03000000380700008645000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400000202000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000005e0400008502000000000000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e0400008702000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000005e0400008902000000000000,Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b10,leftstick:b8,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b9,righttrigger:b4,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,",
+"030000005e0400000c0b000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000fd02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000ff02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e0000a802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000006f0e0000c802000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000c62400003a54000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,",
+"03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,",
+"030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,",
+"03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows,",
+"03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,",
+"030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,",
+"030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows,",
+"03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,",
+"03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,",
+"03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,",
+"78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+"78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,",
+#endif // _GLFW_WIN32
+
+#if defined(_GLFW_COCOA)
+"030000008f0e00000300000009010000,2 In 1 Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000c82d00000031000001000000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00000531000000020000,8BitDo Adapter 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,",
+"03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Mac OS X,",
+"03000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001251000000020000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,",
+"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,",
+"03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,",
+"03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000022000000090000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000190000001000000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00004028000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000260000001000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001d30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001530000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001630000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001730000001000000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001130000000020000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001330000000020000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001330000001000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000a00500003232000009010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,",
+"0300000008100000e501000019040000,Anbernic Handheld,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"03000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,",
+"03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,",
+"03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,",
+"03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,",
+"03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,",
+"030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,",
+"030000008a3500000402000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000008a3500000302000000010000,Backbone One PlayStation Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,",
+"03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,",
+"03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000120c0000200e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000120c0000210e000000010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000d8140000cecf000000000000,Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"03000000a306000022f6000001030000,Cyborg V3 Rumble Pad PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000791d00000103000009010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000006e0500000720000010020000,Elecom JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Mac OS X,",
+"030000006f0e00008401000003010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000151900004000000001000000,Flydigi Vader 2,a:b14,b:b15,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Mac OS X,",
+"03000000b40400001124000001040000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000b40400001224000003030000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,",
+"03000000ac0500001a06000002020000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000373500000411000023000000,GameSir X4A Xbox Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000ad1b000001f9000000000000,Gamestop BB070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000006f0e00000102000000000000,GameStop Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000ff1100003133000007010000,GameWare PC Control Pad,a:b2,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Mac OS X,",
+"03000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"030000007d0400000540000001010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000280400000140000000020000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000008f0e00000300000007010000,GreenAsia Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,",
+"030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00005f00000000010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00005e00000000010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00008400000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00008500000000010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000341a00000302000014010000,Hori Fighting Stick Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00008800000000010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00008700000000010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00004d00000000000000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,",
+"030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000000d0f00000002000017010000,Hori Split Pad Fit,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,",
+"03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,",
+"03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,",
+"03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,",
+"03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X,",
+"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000242f00002d00000007010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000006d04000019c2000000000000,Logitech Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000016c2000000000000,Logitech F310,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006d0400001fc2000000000000,Logitech F710,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000006d04000018c2000000010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000380700005032000000010000,Mad Catz PS3 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000380700005082000000010000,Mad Catz PS4 Fightpad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000380700008483000000010000,Mad Catz PS4 Fightstick TE S Plus,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"0300000049190000020400001b010000,Manba One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000008f0e00001330000011010000,Mayflash Controller Adapter,a:b2,b:b4,back:b16,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b12,lefttrigger:b16,leftx:a0,lefty:a2,rightshoulder:b14,rightx:a6~,righty:a4,start:b18,x:b0,y:b6,platform:Mac OS X,",
+"03000000790000004318000000010000,Mayflash GameCube Adapter,a:b4,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,",
+"03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,",
+"0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000008f0e00001030000011010000,Mayflash Saturn Adapter,a:b0,b:b2,dpdown:b28,dpleft:b30,dpright:b26,dpup:b24,leftshoulder:b10,lefttrigger:b14,rightshoulder:b12,righttrigger:b4,start:b18,x:b6,y:b8,platform:Mac OS X,",
+"0300000025090000e803000000000000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,",
+"03000000790000000318000000010000,Mayflash Wii DolphinBar,a:b8,b:b12,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b44,leftshoulder:b16,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b4,platform:Mac OS X,",
+"03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
+"03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,",
+"030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,",
+"030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X,",
+"030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X,",
+"030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,",
+"030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,",
+"03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000853200008906000000010000,Nacon Revolution X Unlimited,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000632500007505000000020000,NeoGeo mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,",
+"030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000007e0500006920000001010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Mac OS X,",
+"030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,",
+"050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,",
+"030000007e0500007320000001010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Mac OS X,",
+"030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X,",
+"030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,",
+"030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"0300000009120000072f000000010000,OrangeFox86 DreamPicoPort,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a2,leftx:a0,lefty:a1,righttrigger:a5,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000d620000011a7000000020000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000d620000011a7000010050000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
+"030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
+"030000004c0500006802000072050000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,",
+"030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"03000000222c00000225000000010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000222c00000020000000010000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X,",
+"030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X,",
+"030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000321500000010000000010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000321500000011000000010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000632500008005000000010000,Redgear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000632500002305000000010000,Redragon Saturn,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,platform:Mac OS X,",
+"03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Mac OS X,",
+"03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X,",
+"030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X,",
+"030000006f0e00008701000005010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X,",
+"03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,",
+"03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,",
+"03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,",
+"03000000b40400000a01000000000000,Sega Saturn,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,",
+"030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,",
+"0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,",
+"030000004c050000a00b000000000000,Sony DualShock 4 Adapter,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"03000000666600006706000088020000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,",
+"030000004c050000da0c000000010000,Sony PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"030000004c0500003713000000010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
+"03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
+"03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,",
+"05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,",
+"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,",
+"03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
+"03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,",
+"030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,",
+"03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,",
+"03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,",
+"03000000790000001c18000000010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X,",
+"030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,",
+"030000004f0400000ed0000000020000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,",
+"03000000571d00002100000021000000,Tomee NES Controller Adapter,a:b1,b:b0,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,start:b3,platform:Mac OS X,",
+"03000000bd12000015d0000000010000,Tomee Retro Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000bd12000015d0000000000000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000571d00002000000021000000,Tomee SNES Controller Adapter,a:b0,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"030000005f140000c501000000020000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,",
+"03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,",
+"03000000632500002605000000010000,Uberwith Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000151900005678000010010000,Uniplay U6,a:b3,b:b6,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,leftstick:b31,lefttrigger:b21,leftx:a1,lefty:a3,rightshoulder:b19,rightstick:b33,righttrigger:b23,rightx:a4,righty:a5,start:b27,x:b11,y:b13,platform:Mac OS X,",
+"030000006f0e00000302000025040000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"030000006f0e00000702000003060000,Victrix PS4 Pro Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,",
+"050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,",
+"050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,",
+"030000005e0400008e02000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4~,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000006f0e00000104000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"03000000c6240000045d000000000000,Xbox 360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000050b000003090000,Xbox Elite Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,",
+"03000000c62400003a54000000000000,Xbox One PowerA Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,",
+"030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000007050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000130b000022050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,",
+"03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+"03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,",
+#endif // _GLFW_COCOA
+
+#if defined(GLFW_BUILD_LINUX_JOYSTICK)
+"03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000631000000010000,8BitDo Adapter 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,",
+"03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux,",
+"03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000a20000000020000,8BitDo M30 Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,",
+"03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00006928000011010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux,",
+"05000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Linux,",
+"05000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000008000000210000011010000,8BitDo NES30,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000022000000090000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000190000011010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000203800000900000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000231000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000331000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000431000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000060000011010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000061000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"030000003512000012ab000010010000,8BitDo SFC30,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,",
+"030000003512000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000102800000900000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00003028000000010000,8BitDo SFC30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00000260000011010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001130000011010000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000631000010010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00000631000014010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,",
+"05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00003901000013020000,Afterglow Prismatic Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00001302000000010000,Afterglow Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00003901000020060000,Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000100000008200000011010000,Akishop Customs PS360,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000007c1800000006000010010000,Alienware Dual Compatible Game PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
+"05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
+"0300000008100000e501000001010000,Anbernic Handheld,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux,",
+"03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
+"05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,",
+"03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,",
+"03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,",
+"05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,",
+"05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,",
+"05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,",
+"03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,",
+"03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,",
+"030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000bc2000004d50000011010000,Beitong A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000120c0000300e000011010000,Brook Audio Fighting Board PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000120c0000310e000011010000,Brook Audio Fighting Board PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d81d00000b00000010010000,Buffalo BSGP1601,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Linux,",
+"03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,",
+"030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,",
+"03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
+"030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000c11100000191000011010000,EasySMX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000242f00009100000000010000,EasySMX ESM-9101,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006e0500000320000010010000,Elecom U3613M,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
+"030000006e0500000720000010010000,Elecom W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,",
+"030000007d0400000640000010010000,Eliminator AfterShock,a:b1,b:b2,back:b9,dpdown:+a3,dpleft:-a5,dpright:+a5,dpup:-a3,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a4,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
+"03000000430b00000300000000010000,EMS Production PS2 Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"030000006f0e00008401000011010000,Faceoff Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00008101000011010000,Faceoff Deluxe Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00008001000011010000,Faceoff Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000852100000201000010010000,FF GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"05000000b40400001224000001010000,Flydigi APEX 4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b20,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,",
+"19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000ac0500005b05000010010000,GameSir G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000373500009410000010010000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000008f0e00000800000010010000,Gasia PlayStation Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000f0250000c283000010010000,Gioteck VX2 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"190000004b4800000010000000010000,GO-Advance Controller,a:b1,b:b0,back:b10,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,leftshoulder:b4,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b13,start:b15,x:b2,y:b3,platform:Linux,",
+"190000004b4800000010000001010000,GO-Advance Controller,a:b1,b:b0,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b13,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b16,righttrigger:b15,start:b17,x:b2,y:b3,platform:Linux,",
+"190000004b4800000011000000010000,GO-Super Gamepad,a:b0,b:b1,back:b12,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b3,y:b2,platform:Linux,",
+"03000000f0250000c183000010010000,Goodbetterbest Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d11800000094000011010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000d11800000094000000010000,Google Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000001010000,GPD Win Max 2 6800U Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000007d0400000540000000010000,Gravis Eliminator Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000008f0e00000610000000010000,GreenAsia Electronics Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,",
+"030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux,",
+"030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux,",
+"06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,",
+"03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00005f00000011010000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00005e00000011010000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000000d0f00005001000009040000,Hori Fighting Commander Octa Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f00008500000010010000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00008600000002010000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000000d0f00003701000013010000,Hori Fighting Stick Mini,a:b1,b:b0,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b3,y:b2,platform:Linux,",
+"030000000d0f00008800000011010000,Hori Fighting Stick mini 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00008700000011010000,Hori Fighting Stick mini 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,rightstick:b11,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000000d0f00001000000011010000,Hori Fightstick 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000ad1b000003f5000033050000,Hori Fightstick VX,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b8,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,",
+"030000000d0f00004d00000011010000,Hori Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f00003801000011010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Linux,",
+"030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00001100000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00002200000011010000,Hori Real Arcade Pro 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,",
+"030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000000d0f00008501000017010000,Hori Split Pad Fit,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f0000c100000011010000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f00006700000001010000,Horipad One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f0000ab01000011010000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000000d0f00009601000091000000,Horipad Steam,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc2:b2,paddle1:b19,paddle2:b18,paddle3:b15,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000000d0f0000f600000001000000,Horipad Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000341a000005f7000010010000,HuiJia GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
+"05000000242e00000b20000001000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Linux,",
+"03000000242e0000ff0b000011010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Linux,",
+"03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux,",
+"03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
+"030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,",
+"050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,",
+"03000000120c00000500000010010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,",
+"03000000ef0500000300000000010000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,",
+"03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux,",
+"03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,",
+"0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000300f00001101000010010000,Jess Tech Colour Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000ba2200002010000001010000,Jess Technology Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,",
+"050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,",
+"030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,",
+"050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000242f00002d00000011010000,JYS Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,",
+"030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d0400001dc2000014400000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d0400001ec2000019200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d0400001ec2000020200000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d04000019c2000011010000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d0400001fc2000005030000,Logitech F710,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,",
+"030000006d0400000ac2000010010000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,",
+"05000000380700006652000025010000,Mad Catz CTRLR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700008532000010010000,Mad Catz Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700005032000011010000,Mad Catz Fightpad Pro PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700005082000011010000,Mad Catz Fightpad Pro PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000380700008031000011010000,Mad Catz FightStick Alpha PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700008081000011010000,Mad Catz FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700008034000011010000,Mad Catz Fightstick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700008084000011010000,Mad Catz Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000380700008433000011010000,Mad Catz Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700008483000011010000,Mad Catz Fightstick TE S PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000380700001888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700003888000010010000,Mad Catz Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000380700001647000010040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000380700003847000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000120c00000500000000010000,Manta DualShock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"030000008f0e00001330000010010000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000790000004318000010010000,Mayflash GameCube Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,",
+"0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000242f0000f700000001010000,Mayflash Magic S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000008f0e00001030000010010000,Mayflash Saturn Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,",
+"0300000025090000e803000001010000,Mayflash Wii Classic Adapter,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000790000000318000011010000,Mayflash Wii DolphinBar,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000790000000018000011010000,Mayflash Wii U Pro Adapter,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000b50700004f00000000010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,",
+"030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,",
+"030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,",
+"030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,",
+"030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,",
+"030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
+"030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
+"030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000004010000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000056210000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000062230000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000016050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000017050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b000001050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,",
+"03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
+"05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
+"05000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,",
+"03000000c62400002b89000011010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000c62400001a89000000010000,MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000250900006688000000010000,MP8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
+"030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Linux,",
+"030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006b140000010c000010010000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000853200000706000012010000,Nacon GC-100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000853200000503000000010000,Nacon MG-X Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,",
+"060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
+"030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,",
+"03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,",
+"030000007e0500006920000011010000,Nintendo Switch 2 Pro Controller,a:b0,b:b1,back:b14,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b12,leftstick:b15,lefttrigger:b13,leftx:a0,lefty:a1~,misc1:b17,misc2:b20,paddle1:b18,paddle2:b19,rightshoulder:b4,rightstick:b7,righttrigger:b5,rightx:a2,righty:a3~,start:b6,x:b2,y:b3,platform:Linux,",
+"060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
+"060000007e0500000620000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
+"060000007e0500000820000000000000,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
+"050000004c69632050726f20436f6e00,Nintendo Switch Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b16,b:b15,back:b4,leftshoulder:b6,leftstick:b12,leftx:a1,lefty:a0~,rightshoulder:b8,start:b9,x:b14,y:b17,platform:Linux,",
+"030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux,",
+"030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
+"050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,",
+"050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,",
+"05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
+"050000005a1d00000218000003000000,Nokia GC 5000,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,",
+"030000007e0500007320000011010000,NSO GameCube Controller,a:b1,b:b3,dpdown:b8,dpleft:b10,dpright:b9,dpup:b11,guide:b16,leftshoulder:b13,lefttrigger:b12,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b4,rightx:a2,righty:a3~,start:b6,x:b0,y:b2,platform:Linux,",
+"030000007e0500001920000011810000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux,",
+"050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux,",
+"050000007e0500001920000001800000,NSO N64 Controller,+rightx:b2,+righty:b3,-rightx:b4,-righty:b10,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b5,rightshoulder:b7,righttrigger:b9,start:b11,platform:Linux,",
+"030000007e0500001e20000011810000,NSO Sega Genesis Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,misc1:b3,rightshoulder:b2,righttrigger:b4,start:b5,platform:Linux,",
+"030000007e0500001720000011810000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
+"050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux,",
+"050000007e0500001720000001800000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000550900001472000011010000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,",
+"05000000550900001472000001000000,NVIDIA Controller,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b8,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,",
+"030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,",
+"19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,",
+"05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
+"05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,",
+"05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux,",
+"03000000830500005020000010010000,Padix Rockfire PlayStation Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,",
+"03000000ff1100003133000010010000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000006f0e0000b802000001010000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000b802000013020000,PDP Afterglow Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000d702000006640000,PDP Black Camo Wired Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b13,dpup:b14,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00008501000011010000,PDP Fightpad Pro Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00002f01000011010000,PDP Wired PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000f102000000000000,PDP Xbox Atomic,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e0000ef02000007640000,PDP Xbox Series Kinetic Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000540000001010000,PowerA Advantage Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000c62400001a53000000010000,PowerA Mini Pro Ex,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d620000013a7000011010000,PowerA Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000d620000014a7000011010000,PowerA Spectra Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000c62400001a58000001010000,PowerA Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000220000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
+"03000000d62000000228000001010000,PowerA Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c62400001a54000001010000,PowerA Xbox One Mini Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000240000001010000,PowerA Xbox One Spectra Infinity,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000520000050010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000b20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000250900000017000010010000,PS/SS/N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux,",
+"03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000005f1400003102000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,",
+"030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000004c050000f20d000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"050000004c050000e60c000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"050000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"050000004c050000f20d000000810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,",
+"03000000222c00000225000011010000,Qanba Dragon Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000222c00000025000011010000,Qanba Dragon Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick PS5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000222c00000023000011010000,Qanba Obsidian Arcade Joystick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000009b2800000300000001010000,Raphnet 4nes4snes,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
+"030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,",
+"0300132d9b2800006500000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
+"0300132d9b2800006500000001010000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
+"030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
+"030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,",
+"030000009b2800006100000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux,",
+"030000009b2800006400000001010000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Linux,",
+"030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,",
+"030000009b2800008000000001010000,Raphnet Wii Classic Adapter V3,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,",
+"03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000321500000104000011010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000321500000810000011010000,Razer Panthera PS4 Evo Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000321500000010000011010000,Razer Raiju,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000321500000a10000001000000,Razer Raiju Tournament Edition,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000321500000011000011010000,Razer Raion PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"0300000032150000140a000001010000,Razer Wolverine Ultimate Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000000d0f0000c100000010010000,Retro Bit Legacy16,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b12,leftshoulder:b4,lefttrigger:b6,misc1:b13,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,",
+"030000000d0f0000c100000072056800,Retro Bit Legacy16,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b5,leftshoulder:b9,lefttrigger:+a4,misc1:b11,rightshoulder:b10,righttrigger:+a5,start:b6,x:b3,y:b2,platform:Linux,",
+"03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,",
+"0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,",
+"190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,",
+"0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
+"00000000526574726f53746f6e653200,RetroStone 2 Controller,a:b1,b:b0,back:b10,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Linux,",
+"03000000341200000400000000010000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Linux,",
+"030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00008701000011010000,Rock Candy Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000c6240000fefa000000010000,Rock Candy Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00001311000011010000,Saffun Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Linux,",
+"03000000a306000023f6000011010000,Saitek Cyborg PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,",
+"03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,",
+"03000000a30600000cff000010010000,Saitek P2500 Force Rumble,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,",
+"03000000a30600000d5f000010010000,Saitek P2600,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
+"03000000a30600000c04000011010000,Saitek P2900,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,",
+"03000000a306000018f5000010010000,Saitek P3200 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,",
+"03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,",
+"03000000a30600000b04000000010000,Saitek P990 Dual Analog,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,",
+"03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
+"05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,",
+"03000000952e00004b43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000952e00004d43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000952e00004e43000011010000,Scuf Envision,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,",
+"03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000790000002201000011010000,Sega Saturn,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b6,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,",
+"03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,",
+"03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000632500002605000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000632500007505000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"030000004b2900000430000011000000,Snakebyte Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"03000000666600006706000000010000,Sony PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,",
+"030000004c050000da0c000011010000,Sony PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000d9040000160f000000010000,Sony PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,",
+"030000004c0500003713000011010000,Sony PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000250900000500000000010000,Sony PS2 pad with SmartJoy Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
+"030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000de2800000112000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,",
+"03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,",
+"03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,paddle1:b16,paddle2:b15,rightshoulder:b7,righttrigger:a6,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,",
+"03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,",
+"03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,",
+"03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,",
+"03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000ad1b000038f0000090040000,Street Fighter IV Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000003b07000004a1000000010000,Suncom SFX Plus,a:b0,b:b2,back:b7,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,",
+"030000001f08000001e4000010010000,Super Famicom Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,",
+"0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,",
+"030000008f0e00000d31000010010000,SZMY Power 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000457500000401000011010000,SZMY Power DS4 Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,",
+"03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,",
+"03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,",
+"03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,",
+"030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
+"030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
+"030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
+"030000004f04000023b3000000010000,Thrustmaster Dual Trigger PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004f0400000ed0000011010000,Thrustmaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,",
+"030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux,",
+"030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,",
+"030000004f04000004b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
+"030000004f04000026b3000002040000,Thrustmaster GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c6240000025b000002020000,Thrustmaster GPX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000004f04000008d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004f04000009d0000000010000,Thrustmaster Run N Drive PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004f04000007d0000000010000,Thrustmaster T Mini,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"030000004f04000012b3000010010000,Thrustmaster Vibrating Gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,",
+"03000000571d00002000000010010000,Tomee SNES Adapter,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000bd12000015d0000010010000,Tomee SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000d814000007cd000011010000,Toodles 2008 Chimp PC PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,",
+"030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000680a00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,",
+"03000000780300000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,",
+"03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,",
+"03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,",
+"030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,",
+"06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000100800000100000010010000,Twin PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,",
+"03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,",
+"03000000790000001a18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000790000001b18000011010000,Venom PS4 Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"030000006f0e00000302000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"030000006f0e00000702000011010000,Victrix Pro Fightstick PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,",
+"05000000ac0500003232000001000000,VR Box Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,",
+"05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,",
+"030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000010010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000014010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000a102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000a102000030060000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00001503000000020000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400008e02000000010000,Xbox 360 EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"0000000058626f782047616d65706100,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
+"030000005e0400008e02000072050000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000006f0e00001304000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,",
+"030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,",
+"030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea02000001030000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000dd02000003020000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea02000015050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000ea02000017050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000ea02000016050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000011050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000014050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000120b000015050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000130b000017050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"060000005e040000120b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"050000005e040000130b000022050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000200b000023050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,",
+"05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,",
+"03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,",
+"030000005e0400008e02000020010000,XInput Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,",
+"03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
+"03000000120c0000182e000011010000,Zeroplus PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,",
 #endif // GLFW_BUILD_LINUX_JOYSTICK
 };
 
diff --git a/raylib/src/external/jar_mod.h b/raylib/src/external/jar_mod.h
--- a/raylib/src/external/jar_mod.h
+++ b/raylib/src/external/jar_mod.h
@@ -1130,7 +1130,7 @@
     {
         if( modctx )
         {
-            memcopy(&(modctx->song.title),modmemory,1084);
+            memcopy(&(modctx->song), modmemory, 1084);
 
             i = 0;
             modctx->number_of_channels = 0;
@@ -1148,7 +1148,7 @@
             {
                 // 15 Samples modules support
                 // Shift the whole datas to make it look likes a standard 4 channels mod.
-                memcopy(&(modctx->song.signature), "M.K.", 4);
+                memcopy(&(modctx->song.signature), (void *)"M.K.", 4);
                 memcopy(&(modctx->song.length), &(modctx->song.samples[15]), 130);
                 memclear(&(modctx->song.samples[15]), 0, 480);
                 modmemory += 600;
@@ -1535,13 +1535,13 @@
         
         if(fsize && fsize < 32*1024*1024)
         {
-            modctx->modfile = JARMOD_MALLOC(fsize);
+            modctx->modfile = (muchar *) JARMOD_MALLOC(fsize);
             modctx->modfilesize = fsize;
             memset(modctx->modfile, 0, fsize);
             fread(modctx->modfile, fsize, 1, f);
             fclose(f);
-            
-            if(!jar_mod_load(modctx, (void*)modctx->modfile, fsize)) fsize = 0;
+
+            if(!jar_mod_load(modctx, (void *)modctx->modfile, fsize)) fsize = 0;
         } else fsize = 0;
     }
     return fsize;
diff --git a/raylib/src/external/jar_xm.h b/raylib/src/external/jar_xm.h
--- a/raylib/src/external/jar_xm.h
+++ b/raylib/src/external/jar_xm.h
@@ -123,7 +123,7 @@
 // * @param output buffer of 2*numsamples elements (A left and right value for each sample)
 // * @param numsamples number of samples to generate
 void jar_xm_generate_samples_16bit(jar_xm_context_t* ctx, short* output, size_t numsamples) {
-    float* musicBuffer = JARXM_MALLOC((2*numsamples)*sizeof(float));
+    float* musicBuffer = (float *)JARXM_MALLOC((2*numsamples)*sizeof(float));
     jar_xm_generate_samples(ctx, musicBuffer, numsamples);
 
     if(output){
@@ -136,7 +136,7 @@
 // * @param output buffer of 2*numsamples elements (A left and right value for each sample)
 // * @param numsamples number of samples to generate
 void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t numsamples) {
-    float* musicBuffer = JARXM_MALLOC((2*numsamples)*sizeof(float));
+    float* musicBuffer = (float *)JARXM_MALLOC((2*numsamples)*sizeof(float));
     jar_xm_generate_samples(ctx, musicBuffer, numsamples);
 
     if(output){
@@ -232,6 +232,13 @@
 #include <limits.h>
 #include <string.h>
 
+#ifdef DEBUG
+    // Undefine DEBUG to avoid external redefinition warnings/conflicts
+    // This is probably a common definition for
+    // many external build systems' debug configurations
+    #undef DEBUG
+#endif
+
 #if JAR_XM_DEBUG            //JAR_XM_DEBUG defined as 0
 #include <stdio.h>
 #define DEBUG(fmt, ...) do {                                        \
@@ -536,7 +543,7 @@
 #endif
 
     bytes_needed = jar_xm_get_memory_needed_for_context(moddata, moddata_length);
-    mempool = JARXM_MALLOC(bytes_needed);
+    mempool = (char *)JARXM_MALLOC(bytes_needed);
     if(mempool == NULL && bytes_needed > 0) { /* JARXM_MALLOC() failed, trouble ahead */
         DEBUG("call to JARXM_MALLOC() failed, returned %p", (void*)mempool);
         return 2;
@@ -551,11 +558,11 @@
 
     ctx->rate = rate;
     mempool = jar_xm_load_module(ctx, moddata, moddata_length, mempool);
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
 
     ctx->channels = (jar_xm_channel_context_t*)mempool;
     mempool += ctx->module.num_channels * sizeof(jar_xm_channel_context_t);
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
 
     ctx->default_global_volume = 1.f;
     ctx->global_volume = ctx->default_global_volume;
@@ -576,7 +583,7 @@
         ch->actual_panning = .5f;
     }
 
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
     ctx->row_loop_count = (uint8_t *)mempool;
     mempool += MAX_NUM_ROWS * sizeof(uint8_t);
 
@@ -674,14 +681,14 @@
 
 //* Bound reader macros.
 //* If we attempt to read the buffer out-of-bounds, pretend that the buffer is infinitely padded with zeroes.
-#define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t*)(moddata + (offset))) : 0)
+#define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t *)(moddata + (offset))) : 0)
 #define READ_U16(offset) ((uint16_t)READ_U8(offset) | ((uint16_t)READ_U8((offset) + 1) << 8))
 #define READ_U32(offset) ((uint32_t)READ_U16(offset) | ((uint32_t)READ_U16((offset) + 2) << 16))
 #define READ_MEMCPY(ptr, offset, length) memcpy_pad(ptr, length, moddata, moddata_length, offset)
 
 static void memcpy_pad(void *dst, size_t dst_len, const void *src, size_t src_len, size_t offset) {
-    uint8_t *dst_c = dst;
-    const uint8_t *src_c = src;
+    uint8_t *dst_c = (uint8_t *)dst;
+    const uint8_t *src_c = (uint8_t *)src;
 
     /* how many bytes can be copied without overrunning `src` */
     size_t copy_bytes = (src_len >= offset) ? (src_len - offset) : 0;
@@ -801,10 +808,10 @@
     mod->linear_interpolation = 1; // Linear interpolation can be set after loading
     mod->ramping = 1; // ramping can be set after loading
     mempool += mod->num_patterns * sizeof(jar_xm_pattern_t);
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
     mod->instruments = (jar_xm_instrument_t*)mempool;
     mempool += mod->num_instruments * sizeof(jar_xm_instrument_t);
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
     uint16_t flags = READ_U32(offset + 14);
     mod->frequency_type = (flags & (1 << 0)) ? jar_xm_LINEAR_FREQUENCIES : jar_xm_AMIGA_FREQUENCIES;
     ctx->default_tempo = READ_U16(offset + 16);
@@ -877,7 +884,7 @@
 
         offset += packed_patterndata_size;
     }
-    mempool = ALIGN_PTR(mempool, 16);
+    mempool = (char *)ALIGN_PTR(mempool, 16);
 
     /* Read instruments */
     for(uint16_t i = 0; i < ctx->module.num_instruments; ++i) {
@@ -921,11 +928,11 @@
             instr->panning_envelope.enabled = flags & (1 << 0);
             instr->panning_envelope.sustain_enabled = flags & (1 << 1);
             instr->panning_envelope.loop_enabled = flags & (1 << 2);
-            instr->vibrato_type = READ_U8(offset + 235);
+            instr->vibrato_type = (jar_xm_waveform_type_t)READ_U8(offset + 235);
             if(instr->vibrato_type == 2) {
-                instr->vibrato_type = 1;
+                instr->vibrato_type = (jar_xm_waveform_type_t)1;
             } else if(instr->vibrato_type == 1) {
-                instr->vibrato_type = 2;
+                instr->vibrato_type = (jar_xm_waveform_type_t)2;
             }
             instr->vibrato_sweep = READ_U8(offset + 236);
             instr->vibrato_depth = READ_U8(offset + 237);
@@ -969,7 +976,7 @@
             sample->panning = (float)READ_U8(offset + 15) / 255.f;
             sample->relative_note = (int8_t)READ_U8(offset + 16);
             READ_MEMCPY(sample->name, 18, SAMPLE_NAME_LENGTH);
-            sample->data = (float*)mempool;
+            sample->data = (float *)mempool;
             if(sample->bits == 16) {
                 /* 16 bit sample */
                 mempool += sample->length * (sizeof(float) >> 1);
@@ -1468,7 +1475,7 @@
             jar_xm_pitch_slide(ctx, ch, ch->fine_portamento_down_param);
             break;
         case 4: /* E4y: Set vibrato control */
-            ch->vibrato_waveform = s->effect_param & 3;
+            ch->vibrato_waveform = (jar_xm_waveform_type_t)(s->effect_param & 3);
             ch->vibrato_waveform_retrigger = !((s->effect_param >> 2) & 1);
             break;
         case 5: /* E5y: Set finetune */
@@ -1495,7 +1502,7 @@
             }
             break;
         case 7: /* E7y: Set tremolo control */
-            ch->tremolo_waveform = s->effect_param & 3;
+            ch->tremolo_waveform = (jar_xm_waveform_type_t)(s->effect_param & 3);
             ch->tremolo_waveform_retrigger = !((s->effect_param >> 2) & 1);
             break;
         case 0xA: /* EAy: Fine volume slide up */
@@ -2216,7 +2223,7 @@
         return 4;
     }
 
-    char* data = JARXM_MALLOC(size + 1);
+    char* data = (char *)JARXM_MALLOC(size + 1);
     if(!data || fread(data, 1, size, xmf) < size) {
         fclose(xmf);
         DEBUG_ERR(data ? "fread() failed" : "JARXM_MALLOC() failed");
diff --git a/raylib/src/external/miniaudio.h b/raylib/src/external/miniaudio.h
# file too large to diff: raylib/src/external/miniaudio.h
diff --git a/raylib/src/external/msf_gif.h b/raylib/src/external/msf_gif.h
deleted file mode 100644
--- a/raylib/src/external/msf_gif.h
+++ /dev/null
@@ -1,717 +0,0 @@
-/*
-HOW TO USE:
-
-    In exactly one translation unit (.c or .cpp file), #define MSF_GIF_IMPL before including the header, like so:
-
-    #define MSF_GIF_IMPL
-    #include "msf_gif.h"
-
-    Everywhere else, just include the header like normal.
-
-
-USAGE EXAMPLE:
-
-    int width = 480, height = 320, centisecondsPerFrame = 5, bitDepth = 16;
-    MsfGifState gifState = {};
-    // msf_gif_bgra_flag = true; //optionally, set this flag if your pixels are in BGRA format instead of RGBA
-    // msf_gif_alpha_threshold = 128; //optionally, enable transparency (see function documentation below for details)
-    msf_gif_begin(&gifState, width, height);
-    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 1
-    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 2
-    msf_gif_frame(&gifState, ..., centisecondsPerFrame, bitDepth, width * 4); //frame 3, etc...
-    MsfGifResult result = msf_gif_end(&gifState);
-    if (result.data) {
-        FILE * fp = fopen("MyGif.gif", "wb");
-        fwrite(result.data, result.dataSize, 1, fp);
-        fclose(fp);
-    }
-    msf_gif_free(result);
-
-Detailed function documentation can be found in the header section below.
-
-
-ERROR HANDLING:
-
-    If memory allocation fails, the functions will signal the error via their return values.
-    If one function call fails, the library will free all of its allocations,
-    and all subsequent calls will safely no-op and return 0 until the next call to `msf_gif_begin()`.
-    Therefore, it's safe to check only the return value of `msf_gif_end()`.
-
-
-REPLACING MALLOC:
-
-    This library uses malloc+realloc+free internally for memory allocation.
-    To facilitate integration with custom memory allocators, these calls go through macros, which can be redefined.
-    The expected function signature equivalents of the macros are as follows:
-
-    void * MSF_GIF_MALLOC(void * context, size_t newSize)
-    void * MSF_GIF_REALLOC(void * context, void * oldMemory, size_t oldSize, size_t newSize)
-    void MSF_GIF_FREE(void * context, void * oldMemory, size_t oldSize)
-
-    If your allocator needs a context pointer, you can set the `customAllocatorContext` field of the MsfGifState struct
-    before calling msf_gif_begin(), and it will be passed to all subsequent allocator macro calls.
-
-    The maximum number of bytes the library will allocate to encode a single gif is bounded by the following formula:
-    `(2 * 1024 * 1024) + (width * height * 8) + ((1024 + width * height * 1.5) * 3 * frameCount)`
-    The peak heap memory usage in bytes, if using a general-purpose heap allocator, is bounded by the following formula:
-    `(2 * 1024 * 1024) + (width * height * 9.5) + 1024 + (16 * frameCount) + (2 * sizeOfResultingGif)
-
-
-See end of file for license information.
-*/
-
-//version 2.2
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// HEADER                                                                                                           ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#ifndef MSF_GIF_H
-#define MSF_GIF_H
-
-#include <stdint.h>
-#include <stddef.h>
-
-typedef struct {
-    void * data;
-    size_t dataSize;
-
-    size_t allocSize; //internal use
-    void * contextPointer; //internal use
-} MsfGifResult;
-
-typedef struct { //internal use
-    uint32_t * pixels;
-    int depth, count, rbits, gbits, bbits;
-} MsfCookedFrame;
-
-typedef struct MsfGifBuffer {
-    struct MsfGifBuffer * next;
-    size_t size;
-    uint8_t data[1];
-} MsfGifBuffer;
-
-typedef size_t (* MsfGifFileWriteFunc) (const void * buffer, size_t size, size_t count, void * stream);
-typedef struct {
-    MsfGifFileWriteFunc fileWriteFunc;
-    void * fileWriteData;
-    MsfCookedFrame previousFrame;
-    MsfCookedFrame currentFrame;
-    int16_t * lzwMem;
-    MsfGifBuffer * listHead;
-    MsfGifBuffer * listTail;
-    int width, height;
-    void * customAllocatorContext;
-    int framesSubmitted; //needed for transparency to work correctly (because we reach into the previous frame)
-} MsfGifState;
-
-#ifdef __cplusplus
-extern "C" {
-#endif //__cplusplus
-
-/**
- * @param width                Image width in pixels.
- * @param height               Image height in pixels.
- * @return                     Non-zero on success, 0 on error.
- */
-int msf_gif_begin(MsfGifState * handle, int width, int height);
-
-/**
- * @param pixelData            Pointer to raw framebuffer data. Rows must be contiguous in memory, in RGBA8 format
- *                             (or BGRA8 if you have set `msf_gif_bgra_flag = true`).
- *                             Note: This function does NOT free `pixelData`. You must free it yourself afterwards.
- * @param centiSecondsPerFrame How many hundredths of a second this frame should be displayed for.
- *                             Note: This being specified in centiseconds is a limitation of the GIF format.
- * @param maxBitDepth          Limits how many bits per pixel can be used when quantizing the gif.
- *                             The actual bit depth chosen for a given frame will be less than or equal to
- *                             the supplied maximum, depending on the variety of colors used in the frame.
- *                             `maxBitDepth` will be clamped between 1 and 16. The recommended default is 16.
- *                             Lowering this value can result in faster exports and smaller gifs,
- *                             but the quality may suffer.
- *                             Please experiment with this value to find what works best for your application.
- * @param pitchInBytes         The number of bytes from the beginning of one row of pixels to the beginning of the next.
- *                             If you want to flip the image, just pass in a negative pitch.
- * @return                     Non-zero on success, 0 on error.
- */
-int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
-
-/**
- * @return                     A block of memory containing the gif file data, or NULL on error.
- *                             You are responsible for freeing this via `msf_gif_free()`.
- */
-MsfGifResult msf_gif_end(MsfGifState * handle);
-
-/**
- * @param result                The MsfGifResult struct, verbatim as it was returned from `msf_gif_end()`.
- */
-void msf_gif_free(MsfGifResult result);
-
-//The gif format only supports 1-bit transparency, meaning a pixel will either be fully transparent or fully opaque.
-//Pixels with an alpha value less than the alpha threshold will be treated as transparent.
-//To enable exporting transparent gifs, set it to a value between 1 and 255 (inclusive) before calling msf_gif_frame().
-//Setting it to 0 causes the alpha channel to be ignored. Its initial value is 0.
-extern int msf_gif_alpha_threshold;
-
-//Set `msf_gif_bgra_flag = true` before calling `msf_gif_frame()` if your pixels are in BGRA byte order instead of RBGA.
-extern int msf_gif_bgra_flag;
-
-
-
-//TO-FILE FUNCTIONS
-//These functions are equivalent to the ones above, but they write results to a file incrementally,
-//instead of building a buffer in memory. This can result in lower memory usage when saving large gifs,
-//because memory usage is bounded by only the size of a single frame, and is not dependent on the number of frames.
-//There is currently no reason to use these unless you are on a memory-constrained platform.
-//If in doubt about which API to use, for now you should use the normal (non-file) functions above.
-//The signature of MsfGifFileWriteFunc matches fwrite for convenience, so that you can use the C file API like so:
-//  FILE * fp = fopen("MyGif.gif", "wb");
-//  msf_gif_begin_to_file(&handle, width, height, (MsfGifFileWriteFunc) fwrite, (void *) fp);
-//  msf_gif_frame_to_file(...)
-//  msf_gif_end_to_file(&handle);
-//  fclose(fp);
-//If you use a custom file write function, you must take care to return the same values that fwrite() would return.
-//Note that all three functions will potentially write to the file.
-int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer);
-int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes);
-int msf_gif_end_to_file(MsfGifState * handle); //returns 0 on error and non-zero on success
-
-#ifdef __cplusplus
-}
-#endif //__cplusplus
-
-#endif //MSF_GIF_H
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// IMPLEMENTATION                                                                                                   ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSF_GIF_IMPL
-#ifndef MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
-#define MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
-
-//ensure the library user has either defined all of malloc/realloc/free, or none
-#if defined(MSF_GIF_MALLOC) && defined(MSF_GIF_REALLOC) && defined(MSF_GIF_FREE) //ok
-#elif !defined(MSF_GIF_MALLOC) && !defined(MSF_GIF_REALLOC) && !defined(MSF_GIF_FREE) //ok
-#else
-#error "You must either define all of MSF_GIF_MALLOC, MSF_GIF_REALLOC, and MSF_GIF_FREE, or define none of them"
-#endif
-
-//provide default allocator definitions that redirect to the standard global allocator
-#if !defined(MSF_GIF_MALLOC)
-#include <stdlib.h> //malloc, etc.
-#define MSF_GIF_MALLOC(contextPointer, newSize) malloc(newSize)
-#define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) realloc(oldMemory, newSize)
-#define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) free(oldMemory)
-#endif
-
-//instrumentation for capturing profiling traces (useless for the library user, but useful for the library author)
-#ifdef MSF_GIF_ENABLE_TRACING
-#define MsfTimeFunc TimeFunc
-#define MsfTimeLoop TimeLoop
-#define msf_init_profiling_thread init_profiling_thread
-#else
-#define MsfTimeFunc
-#define MsfTimeLoop(name)
-#define msf_init_profiling_thread()
-#endif //MSF_GIF_ENABLE_TRACING
-
-#include <string.h> //memcpy
-
-//TODO: use compiler-specific notation to force-inline functions currently marked inline
-#if defined(__GNUC__) //gcc, clang
-static inline int msf_bit_log(int i) { return 32 - __builtin_clz(i); }
-#elif defined(_MSC_VER) //msvc
-#include <intrin.h>
-static inline int msf_bit_log(int i) { unsigned long idx; _BitScanReverse(&idx, i); return idx + 1; }
-#else //fallback implementation for other compilers
-//from https://stackoverflow.com/a/31718095/3064745 - thanks!
-static inline int msf_bit_log(int i) {
-    static const int MultiplyDeBruijnBitPosition[32] = {
-        0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
-        8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31,
-    };
-    i |= i >> 1;
-    i |= i >> 2;
-    i |= i >> 4;
-    i |= i >> 8;
-    i |= i >> 16;
-    return MultiplyDeBruijnBitPosition[(uint32_t)(i * 0x07C4ACDDU) >> 27] + 1;
-}
-#endif
-static inline int msf_imin(int a, int b) { return a < b? a : b; }
-static inline int msf_imax(int a, int b) { return b < a? a : b; }
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// Frame Cooking                                                                                                    ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
-#include <emmintrin.h>
-#endif
-
-int msf_gif_alpha_threshold = 0;
-int msf_gif_bgra_flag = 0;
-
-static void msf_cook_frame(MsfCookedFrame * frame, uint8_t * raw, uint8_t * used,
-                           int width, int height, int pitch, int depth)
-{ MsfTimeFunc
-    //bit depth for each channel
-    static const int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 };
-    static const int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 };
-    static const int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 };
-    //this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers
-    //in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays.
-    const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray;
-    const int * gdepths =                                   gdepthsArray;
-    const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray;
-
-    static const int ditherKernel[16] = {
-         0 << 12,  8 << 12,  2 << 12, 10 << 12,
-        12 << 12,  4 << 12, 14 << 12,  6 << 12,
-         3 << 12, 11 << 12,  1 << 12,  9 << 12,
-        15 << 12,  7 << 12, 13 << 12,  5 << 12,
-    };
-
-    uint32_t * cooked = frame->pixels;
-    int count = 0;
-    MsfTimeLoop("do") do {
-        int rbits = rdepths[depth], gbits = gdepths[depth], bbits = bdepths[depth];
-        int paletteSize = (1 << (rbits + gbits + bbits)) + 1;
-        memset(used, 0, paletteSize * sizeof(uint8_t));
-
-        //TODO: document what this math does and why it's correct
-        int rdiff = (1 << (8 - rbits)) - 1;
-        int gdiff = (1 << (8 - gbits)) - 1;
-        int bdiff = (1 << (8 - bbits)) - 1;
-        short rmul = (short) ((255.0f - rdiff) / 255.0f * 257);
-        short gmul = (short) ((255.0f - gdiff) / 255.0f * 257);
-        short bmul = (short) ((255.0f - bdiff) / 255.0f * 257);
-
-        int gmask = ((1 << gbits) - 1) << rbits;
-        int bmask = ((1 << bbits) - 1) << rbits << gbits;
-
-        MsfTimeLoop("cook") for (int y = 0; y < height; ++y) {
-            int x = 0;
-
-            #if (defined (__SSE2__) || defined (_M_X64) || _M_IX86_FP == 2) && !defined(MSF_GIF_NO_SSE2)
-                __m128i k = _mm_loadu_si128((__m128i *) &ditherKernel[(y & 3) * 4]);
-                __m128i k2 = _mm_or_si128(_mm_srli_epi32(k, rbits), _mm_slli_epi32(_mm_srli_epi32(k, bbits), 16));
-                for (; x < width - 3; x += 4) {
-                    uint8_t * pixels = &raw[y * pitch + x * 4];
-                    __m128i p = _mm_loadu_si128((__m128i *) pixels);
-
-                    __m128i rb = _mm_and_si128(p, _mm_set1_epi32(0x00FF00FF));
-                    __m128i rb1 = _mm_mullo_epi16(rb, _mm_set_epi16(bmul, rmul, bmul, rmul, bmul, rmul, bmul, rmul));
-                    __m128i rb2 = _mm_adds_epu16(rb1, k2);
-                    __m128i r3 = _mm_srli_epi32(_mm_and_si128(rb2, _mm_set1_epi32(0x0000FFFF)), 16 - rbits);
-                    __m128i b3 = _mm_and_si128(_mm_srli_epi32(rb2, 32 - rbits - gbits - bbits), _mm_set1_epi32(bmask));
-
-                    __m128i g = _mm_and_si128(_mm_srli_epi32(p, 8), _mm_set1_epi32(0x000000FF));
-                    __m128i g1 = _mm_mullo_epi16(g, _mm_set1_epi32(gmul));
-                    __m128i g2 = _mm_adds_epu16(g1, _mm_srli_epi32(k, gbits));
-                    __m128i g3 = _mm_and_si128(_mm_srli_epi32(g2, 16 - rbits - gbits), _mm_set1_epi32(gmask));
-
-                    __m128i out = _mm_or_si128(_mm_or_si128(r3, g3), b3);
-
-                    //mask in transparency based on threshold
-                    //NOTE: we can theoretically do a sub instead of srli by doing an unsigned compare via bias
-                    //      to maybe save a TINY amount of throughput? but lol who cares maybe I'll do it later -m
-                    __m128i invAlphaMask = _mm_cmplt_epi32(_mm_srli_epi32(p, 24), _mm_set1_epi32(msf_gif_alpha_threshold));
-                    out = _mm_or_si128(_mm_and_si128(invAlphaMask, _mm_set1_epi32(paletteSize - 1)), _mm_andnot_si128(invAlphaMask, out));
-
-                    //TODO: does storing this as a __m128i then reading it back as a uint32_t violate strict aliasing?
-                    uint32_t * c = &cooked[y * width + x];
-                    _mm_storeu_si128((__m128i *) c, out);
-                }
-            #endif
-
-            //scalar cleanup loop
-            for (; x < width; ++x) {
-                uint8_t * p = &raw[y * pitch + x * 4];
-
-                //transparent pixel if alpha is low
-                if (p[3] < msf_gif_alpha_threshold) {
-                    cooked[y * width + x] = paletteSize - 1;
-                    continue;
-                }
-
-                int dx = x & 3, dy = y & 3;
-                int k = ditherKernel[dy * 4 + dx];
-                cooked[y * width + x] =
-                    (msf_imin(65535, p[2] * bmul + (k >> bbits)) >> (16 - rbits - gbits - bbits) & bmask) |
-                    (msf_imin(65535, p[1] * gmul + (k >> gbits)) >> (16 - rbits - gbits        ) & gmask) |
-                     msf_imin(65535, p[0] * rmul + (k >> rbits)) >> (16 - rbits                );
-            }
-        }
-
-        count = 0;
-        MsfTimeLoop("mark") for (int i = 0; i < width * height; ++i) {
-            used[cooked[i]] = 1;
-        }
-
-        //count used colors, transparent is ignored
-        MsfTimeLoop("count") for (int j = 0; j < paletteSize - 1; ++j) {
-            count += used[j];
-        }
-    } while (count >= 256 && --depth);
-
-    MsfCookedFrame ret = { cooked, depth, count, rdepths[depth], gdepths[depth], bdepths[depth] };
-    *frame = ret;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// Frame Compression                                                                                                ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-static inline void msf_put_code(uint8_t * * writeHead, uint32_t * blockBits, int len, uint32_t code) {
-    //insert new code into block buffer
-    int idx = *blockBits / 8;
-    int bit = *blockBits % 8;
-    (*writeHead)[idx + 0] |= code <<       bit ;
-    (*writeHead)[idx + 1] |= code >> ( 8 - bit);
-    (*writeHead)[idx + 2] |= code >> (16 - bit);
-    *blockBits += len;
-
-    //prep the next block buffer if the current one is full
-    if (*blockBits >= 256 * 8) {
-        *blockBits -= 255 * 8;
-        (*writeHead) += 256;
-        (*writeHead)[2] = (*writeHead)[1];
-        (*writeHead)[1] = (*writeHead)[0];
-        (*writeHead)[0] = 255;
-        memset((*writeHead) + 4, 0, 256);
-    }
-}
-
-typedef struct {
-    int16_t * data;
-    int len;
-    int stride;
-} MsfStridedList;
-
-static inline void msf_lzw_reset(MsfStridedList * lzw, int tableSize, int stride) { MsfTimeFunc
-    memset(lzw->data, 0xFF, 4096 * stride * sizeof(int16_t));
-    lzw->len = tableSize + 2;
-    lzw->stride = stride;
-}
-
-static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int height, int centiSeconds,
-                                         MsfCookedFrame frame, MsfGifState * handle, uint8_t * used, int16_t * lzwMem)
-{ MsfTimeFunc
-    //NOTE: we reserve enough memory for theoretical the worst case upfront because it's a reasonable amount,
-    //      and prevents us from ever having to check size or realloc during compression
-    int maxBufSize = offsetof(MsfGifBuffer, data) + 32 + 256 * 3 + width * height * 3 / 2; //headers + color table + data
-    MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize);
-    if (!buffer) { return NULL; }
-    uint8_t * writeHead = buffer->data;
-    MsfStridedList lzw = { lzwMem, 0, 0 };
-
-    //allocate tlb
-    int totalBits = frame.rbits + frame.gbits + frame.bbits;
-    int tlbSize = (1 << totalBits) + 1;
-    uint8_t tlb[(1 << 16) + 1]; //only 64k, so stack allocating is fine
-
-    //generate palette
-    typedef struct { uint8_t r, g, b; } Color3;
-    Color3 table[256] = { {0} };
-    int tableIdx = 1; //we start counting at 1 because 0 is the transparent color
-    //transparent is always last in the table
-    tlb[tlbSize-1] = 0;
-    MsfTimeLoop("table") for (int i = 0; i < tlbSize-1; ++i) {
-        if (used[i]) {
-            tlb[i] = tableIdx;
-            int rmask = (1 << frame.rbits) - 1;
-            int gmask = (1 << frame.gbits) - 1;
-            //isolate components
-            int r = i & rmask;
-            int g = i >> frame.rbits & gmask;
-            int b = i >> (frame.rbits + frame.gbits);
-            //shift into highest bits
-            r <<= 8 - frame.rbits;
-            g <<= 8 - frame.gbits;
-            b <<= 8 - frame.bbits;
-            table[tableIdx].r = r | r >> frame.rbits | r >> (frame.rbits * 2) | r >> (frame.rbits * 3);
-            table[tableIdx].g = g | g >> frame.gbits | g >> (frame.gbits * 2) | g >> (frame.gbits * 3);
-            table[tableIdx].b = b | b >> frame.bbits | b >> (frame.bbits * 2) | b >> (frame.bbits * 3);
-            if (msf_gif_bgra_flag) {
-                uint8_t temp = table[tableIdx].r;
-                table[tableIdx].r = table[tableIdx].b;
-                table[tableIdx].b = temp;
-            }
-            ++tableIdx;
-        }
-    }
-    int hasTransparentPixels = used[tlbSize-1];
-
-    //SPEC: "Because of some algorithmic constraints however, black & white images which have one color bit
-    //       must be indicated as having a code size of 2."
-    int tableBits = msf_imax(2, msf_bit_log(tableIdx - 1));
-    int tableSize = 1 << tableBits;
-    //NOTE: we don't just compare `depth` field here because it will be wrong for the first frame and we will segfault
-    MsfCookedFrame previous = handle->previousFrame;
-    int hasSamePal = frame.rbits == previous.rbits && frame.gbits == previous.gbits && frame.bbits == previous.bbits;
-    int framesCompatible = hasSamePal && !hasTransparentPixels;
-
-    //NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
-    char headerBytes[19] = "\x21\xF9\x04\x05\0\0\0\0" "\x2C\0\0\0\0\0\0\0\0\x80";
-    //NOTE: we need to check the frame number because if we reach into the buffer prior to the first frame,
-    //      we'll just clobber the file header instead, which is a bug
-    if (hasTransparentPixels && handle->framesSubmitted > 0) {
-        handle->listTail->data[3] = 0x09; //set the previous frame's disposal to background, so transparency is possible
-    }
-    memcpy(&headerBytes[4], &centiSeconds, 2);
-    memcpy(&headerBytes[13], &width, 2);
-    memcpy(&headerBytes[15], &height, 2);
-    headerBytes[17] |= tableBits - 1;
-    memcpy(writeHead, headerBytes, 18);
-    writeHead += 18;
-
-    //local color table
-    memcpy(writeHead, table, tableSize * sizeof(Color3));
-    writeHead += tableSize * sizeof(Color3);
-    *writeHead++ = tableBits;
-
-    //prep block
-    memset(writeHead, 0, 260);
-    writeHead[0] = 255;
-    uint32_t blockBits = 8; //relative to block.head
-
-    //SPEC: "Encoders should output a Clear code as the first code of each image data stream."
-    msf_lzw_reset(&lzw, tableSize, tableIdx);
-    msf_put_code(&writeHead, &blockBits, msf_bit_log(lzw.len - 1), tableSize);
-
-    int lastCode = framesCompatible && frame.pixels[0] == previous.pixels[0]? 0 : tlb[frame.pixels[0]];
-    MsfTimeLoop("compress") for (int i = 1; i < width * height; ++i) {
-        //PERF: branching vs. branchless version of this line is observed to have no discernable impact on speed
-        int color = framesCompatible && frame.pixels[i] == previous.pixels[i]? 0 : tlb[frame.pixels[i]];
-        int code = (&lzw.data[lastCode * lzw.stride])[color];
-        if (code < 0) {
-            //write to code stream
-            int codeBits = msf_bit_log(lzw.len - 1);
-            msf_put_code(&writeHead, &blockBits, codeBits, lastCode);
-
-            if (lzw.len > 4095) {
-                //reset buffer code table
-                msf_put_code(&writeHead, &blockBits, codeBits, tableSize);
-                msf_lzw_reset(&lzw, tableSize, tableIdx);
-            } else {
-                (&lzw.data[lastCode * lzw.stride])[color] = lzw.len;
-                ++lzw.len;
-            }
-
-            lastCode = color;
-        } else {
-            lastCode = code;
-        }
-    }
-
-    //write code for leftover index buffer contents, then the end code
-    msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len - 1)), lastCode);
-    msf_put_code(&writeHead, &blockBits, msf_imin(12, msf_bit_log(lzw.len)), tableSize + 1);
-
-    //flush remaining data
-    if (blockBits > 8) {
-        int bytes = (blockBits + 7) / 8; //round up
-        writeHead[0] = bytes - 1;
-        writeHead += bytes;
-    }
-    *writeHead++ = 0; //terminating block
-
-    //fill in buffer header and shrink buffer to fit data
-    buffer->next = NULL;
-    buffer->size = writeHead - buffer->data;
-    MsfGifBuffer * moved =
-        (MsfGifBuffer *) MSF_GIF_REALLOC(allocContext, buffer, maxBufSize, offsetof(MsfGifBuffer, data) + buffer->size);
-    if (!moved) { MSF_GIF_FREE(allocContext, buffer, maxBufSize); return NULL; }
-    return moved;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// To-memory API                                                                                                    ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-static const int lzwAllocSize = 4096 * 256 * sizeof(int16_t);
-
-//NOTE: by C standard library conventions, freeing NULL should be a no-op,
-//      but just in case the user's custom free doesn't follow that rule, we do null checks on our end as well.
-static void msf_free_gif_state(MsfGifState * handle) {
-    if (handle->previousFrame.pixels) MSF_GIF_FREE(handle->customAllocatorContext, handle->previousFrame.pixels,
-                                                   handle->width * handle->height * sizeof(uint32_t));
-    if (handle->currentFrame.pixels)  MSF_GIF_FREE(handle->customAllocatorContext, handle->currentFrame.pixels,
-                                                   handle->width * handle->height * sizeof(uint32_t));
-    if (handle->lzwMem) MSF_GIF_FREE(handle->customAllocatorContext, handle->lzwMem, lzwAllocSize);
-    for (MsfGifBuffer * node = handle->listHead; node;) {
-        MsfGifBuffer * next = node->next; //NOTE: we have to copy the `next` pointer BEFORE freeing the node holding it
-        MSF_GIF_FREE(handle->customAllocatorContext, node, offsetof(MsfGifBuffer, data) + node->size);
-        node = next;
-    }
-    handle->listHead = NULL; //this implicitly marks the handle as invalid until the next msf_gif_begin() call
-}
-
-int msf_gif_begin(MsfGifState * handle, int width, int height) { MsfTimeFunc
-    //NOTE: we cannot stomp the entire struct to zero because we must preserve `customAllocatorContext`.
-    MsfCookedFrame empty = {0}; //god I hate MSVC...
-    handle->previousFrame = empty;
-    handle->currentFrame = empty;
-    handle->width = width;
-    handle->height = height;
-    handle->framesSubmitted = 0;
-
-    //allocate memory for LZW buffer
-    //NOTE: Unfortunately we can't just use stack memory for the LZW table because it's 2MB,
-    //      which is more stack space than most operating systems give by default,
-    //      and we can't realistically expect users to be willing to override that just to use our library,
-    //      so we have to allocate this on the heap.
-    handle->lzwMem = (int16_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, lzwAllocSize);
-    handle->previousFrame.pixels =
-        (uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
-    handle->currentFrame.pixels =
-        (uint32_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, handle->width * handle->height * sizeof(uint32_t));
-
-    //setup header buffer header (lol)
-    handle->listHead = (MsfGifBuffer *) MSF_GIF_MALLOC(handle->customAllocatorContext, offsetof(MsfGifBuffer, data) + 32);
-    if (!handle->listHead || !handle->lzwMem || !handle->previousFrame.pixels || !handle->currentFrame.pixels) {
-        msf_free_gif_state(handle);
-        return 0;
-    }
-    handle->listTail = handle->listHead;
-    handle->listHead->next = NULL;
-    handle->listHead->size = 32;
-
-    //NOTE: because __attribute__((__packed__)) is annoyingly compiler-specific, we do this unreadable weirdness
-    char headerBytes[33] = "GIF89a\0\0\0\0\x70\0\0" "\x21\xFF\x0BNETSCAPE2.0\x03\x01\0\0\0";
-    memcpy(&headerBytes[6], &width, 2);
-    memcpy(&headerBytes[8], &height, 2);
-    memcpy(handle->listHead->data, headerBytes, 32);
-    return 1;
-}
-
-int msf_gif_frame(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes)
-{ MsfTimeFunc
-    if (!handle->listHead) { return 0; }
-
-    maxBitDepth = msf_imax(1, msf_imin(16, maxBitDepth));
-    if (pitchInBytes == 0) pitchInBytes = handle->width * 4;
-    if (pitchInBytes < 0) pixelData -= pitchInBytes * (handle->height - 1);
-
-    uint8_t used[(1 << 16) + 1]; //only 64k, so stack allocating is fine
-    msf_cook_frame(&handle->currentFrame, pixelData, used, handle->width, handle->height, pitchInBytes,
-        msf_imin(maxBitDepth, handle->previousFrame.depth + 160 / msf_imax(1, handle->previousFrame.count)));
-
-    MsfGifBuffer * buffer = msf_compress_frame(handle->customAllocatorContext, handle->width, handle->height,
-        centiSecondsPerFame, handle->currentFrame, handle, used, handle->lzwMem);
-    if (!buffer) { msf_free_gif_state(handle); return 0; }
-    handle->listTail->next = buffer;
-    handle->listTail = buffer;
-
-    //swap current and previous frames
-    MsfCookedFrame tmp = handle->previousFrame;
-    handle->previousFrame = handle->currentFrame;
-    handle->currentFrame = tmp;
-
-    handle->framesSubmitted += 1;
-    return 1;
-}
-
-MsfGifResult msf_gif_end(MsfGifState * handle) { MsfTimeFunc
-    if (!handle->listHead) { MsfGifResult empty = {0}; return empty; }
-
-    //first pass: determine total size
-    size_t total = 1; //1 byte for trailing marker
-    for (MsfGifBuffer * node = handle->listHead; node; node = node->next) { total += node->size; }
-
-    //second pass: write data
-    uint8_t * buffer = (uint8_t *) MSF_GIF_MALLOC(handle->customAllocatorContext, total);
-    if (buffer) {
-        uint8_t * writeHead = buffer;
-        for (MsfGifBuffer * node = handle->listHead; node; node = node->next) {
-            memcpy(writeHead, node->data, node->size);
-            writeHead += node->size;
-        }
-        *writeHead++ = 0x3B;
-    }
-
-    //third pass: free buffers
-    msf_free_gif_state(handle);
-
-    MsfGifResult ret = { buffer, total, total, handle->customAllocatorContext };
-    return ret;
-}
-
-void msf_gif_free(MsfGifResult result) { MsfTimeFunc
-    if (result.data) { MSF_GIF_FREE(result.contextPointer, result.data, result.allocSize); }
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/// To-file API                                                                                                      ///
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-int msf_gif_begin_to_file(MsfGifState * handle, int width, int height, MsfGifFileWriteFunc func, void * filePointer) {
-    handle->fileWriteFunc = func;
-    handle->fileWriteData = filePointer;
-    return msf_gif_begin(handle, width, height);
-}
-
-int msf_gif_frame_to_file(MsfGifState * handle, uint8_t * pixelData, int centiSecondsPerFame, int maxBitDepth, int pitchInBytes) {
-    if (!msf_gif_frame(handle, pixelData, centiSecondsPerFame, maxBitDepth, pitchInBytes)) { return 0; }
-
-    //NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
-    MsfGifBuffer * head = handle->listHead;
-    if (!handle->fileWriteFunc(head->data, head->size, 1, handle->fileWriteData)) { msf_free_gif_state(handle); return 0; }
-    handle->listHead = head->next;
-    MSF_GIF_FREE(handle->customAllocatorContext, head, offsetof(MsfGifBuffer, data) + head->size);
-    return 1;
-}
-
-int msf_gif_end_to_file(MsfGifState * handle) {
-    //NOTE: this is a somewhat hacky implementation which is not perfectly efficient, but it's good enough for now
-    MsfGifResult result = msf_gif_end(handle);
-    int ret = (int) handle->fileWriteFunc(result.data, result.dataSize, 1, handle->fileWriteData);
-    msf_gif_free(result);
-    return ret;
-}
-
-#endif //MSF_GIF_ALREADY_IMPLEMENTED_IN_THIS_TRANSLATION_UNIT
-#endif //MSF_GIF_IMPL
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2021 Miles Fogle
-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 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.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-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 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.
-------------------------------------------------------------------------------
-*/
diff --git a/raylib/src/external/qoa.h b/raylib/src/external/qoa.h
--- a/raylib/src/external/qoa.h
+++ b/raylib/src/external/qoa.h
@@ -366,22 +366,7 @@
 	), bytes, &p);
 
 	
-	for (int c = 0; c < channels; c++) {
-		/* If the weights have grown too large, reset them to 0. This may happen
-		with certain high-frequency sounds. This is a last resort and will 
-		introduce quite a bit of noise, but should at least prevent pops/clicks */
-		int weights_sum = 
-			qoa->lms[c].weights[0] * qoa->lms[c].weights[0] + 
-			qoa->lms[c].weights[1] * qoa->lms[c].weights[1] + 
-			qoa->lms[c].weights[2] * qoa->lms[c].weights[2] + 
-			qoa->lms[c].weights[3] * qoa->lms[c].weights[3];
-		if (weights_sum > 0x2fffffff) {
-			qoa->lms[c].weights[0] = 0;
-			qoa->lms[c].weights[1] = 0;
-			qoa->lms[c].weights[2] = 0;
-			qoa->lms[c].weights[3] = 0;
-		}
-
+	for (unsigned int c = 0; c < channels; c++) {
 		/* Write the current LMS state */
 		qoa_uint64_t weights = 0;
 		qoa_uint64_t history = 0;
@@ -395,9 +380,9 @@
 
 	/* We encode all samples with the channels interleaved on a slice level.
 	E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/
-	for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
+	for (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
 
-		for (int c = 0; c < channels; c++) {
+		for (unsigned int c = 0; c < channels; c++) {
 			int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
 			int slice_start = sample_index * channels + c;
 			int slice_end = (sample_index + slice_len) * channels + c;			
@@ -405,10 +390,13 @@
 			/* Brute for search for the best scalefactor. Just go through all
 			16 scalefactors, encode all samples for the current slice and 
 			meassure the total squared error. */
-			qoa_uint64_t best_error = -1;
-			qoa_uint64_t best_slice;
+			qoa_uint64_t best_rank = -1;
+			#ifdef QOA_RECORD_TOTAL_ERROR
+				qoa_uint64_t best_error = -1;
+			#endif
+			qoa_uint64_t best_slice = 0;
 			qoa_lms_t best_lms;
-			int best_scalefactor;
+			int best_scalefactor = 0;
 
 			for (int sfi = 0; sfi < 16; sfi++) {
 				/* There is a strong correlation between the scalefactors of
@@ -421,7 +409,10 @@
 				state when encoding. */
 				qoa_lms_t lms = qoa->lms[c];
 				qoa_uint64_t slice = scalefactor;
-				qoa_uint64_t current_error = 0;
+				qoa_uint64_t current_rank = 0;
+				#ifdef QOA_RECORD_TOTAL_ERROR
+					qoa_uint64_t current_error = 0;
+				#endif
 
 				for (int si = slice_start; si < slice_end; si += channels) {
 					int sample = sample_data[si];
@@ -434,9 +425,27 @@
 					int dequantized = qoa_dequant_tab[scalefactor][quantized];
 					int reconstructed = qoa_clamp_s16(predicted + dequantized);
 
+
+					/* If the weights have grown too large, we introduce a penalty
+					here. This prevents pops/clicks in certain problem cases */
+					int weights_penalty = ((
+						lms.weights[0] * lms.weights[0] + 
+						lms.weights[1] * lms.weights[1] + 
+						lms.weights[2] * lms.weights[2] + 
+						lms.weights[3] * lms.weights[3]
+					) >> 18) - 0x8ff;
+					if (weights_penalty < 0) {
+						weights_penalty = 0;
+					}
+
 					long long error = (sample - reconstructed);
-					current_error += error * error;
-					if (current_error > best_error) {
+					qoa_uint64_t error_sq = error * error;
+
+					current_rank += error_sq + weights_penalty * weights_penalty;
+					#ifdef QOA_RECORD_TOTAL_ERROR
+						current_error += error_sq;
+					#endif
+					if (current_rank > best_rank) {
 						break;
 					}
 
@@ -444,8 +453,11 @@
 					slice = (slice << 3) | quantized;
 				}
 
-				if (current_error < best_error) {
-					best_error = current_error;
+				if (current_rank < best_rank) {
+					best_rank = current_rank;
+					#ifdef QOA_RECORD_TOTAL_ERROR
+						best_error = current_error;
+					#endif
 					best_slice = slice;
 					best_lms = lms;
 					best_scalefactor = scalefactor;
@@ -488,9 +500,9 @@
 		num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */
 		num_slices * 8 * qoa->channels;                /* 8 byte slices */
 
-	unsigned char *bytes = QOA_MALLOC(encoded_size);
+	unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size);
 
-	for (int c = 0; c < qoa->channels; c++) {
+	for (unsigned int c = 0; c < qoa->channels; c++) {
 		/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the 
 		prediction of the first few ms of a file. */
 		qoa->lms[c].weights[0] = 0;
@@ -513,7 +525,7 @@
 	#endif
 
 	int frame_len = QOA_FRAME_LEN;
-	for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
+	for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
 		frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);		
 		const short *frame_samples = sample_data + sample_index * qoa->channels;
 		unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
@@ -576,14 +588,14 @@
 
 	/* Read and verify the frame header */
 	qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
-	int channels   = (frame_header >> 56) & 0x0000ff;
-	int samplerate = (frame_header >> 32) & 0xffffff;
-	int samples    = (frame_header >> 16) & 0x00ffff;
-	int frame_size = (frame_header      ) & 0x00ffff;
+	unsigned int channels   = (frame_header >> 56) & 0x0000ff;
+	unsigned int samplerate = (frame_header >> 32) & 0xffffff;
+	unsigned int samples    = (frame_header >> 16) & 0x00ffff;
+	unsigned int frame_size = (frame_header      ) & 0x00ffff;
 
-	int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
-	int num_slices = data_size / 8;
-	int max_total_samples = num_slices * QOA_SLICE_LEN;
+	unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
+	unsigned int num_slices = data_size / 8;
+	unsigned int max_total_samples = num_slices * QOA_SLICE_LEN;
 
 	if (
 		channels != qoa->channels || 
@@ -596,7 +608,7 @@
 
 
 	/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
-	for (int c = 0; c < channels; c++) {
+	for (unsigned int c = 0; c < channels; c++) {
 		qoa_uint64_t history = qoa_read_u64(bytes, &p);
 		qoa_uint64_t weights = qoa_read_u64(bytes, &p);
 		for (int i = 0; i < QOA_LMS_LEN; i++) {
@@ -609,17 +621,19 @@
 
 
 	/* Decode all slices for all channels in this frame */
-	for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
-		for (int c = 0; c < channels; c++) {
+	for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
+		for (unsigned int c = 0; c < channels; c++) {
 			qoa_uint64_t slice = qoa_read_u64(bytes, &p);
 
 			int scalefactor = (slice >> 60) & 0xf;
+			slice <<= 4;
+
 			int slice_start = sample_index * channels + c;
 			int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
 
 			for (int si = slice_start; si < slice_end; si += channels) {
 				int predicted = qoa_lms_predict(&qoa->lms[c]);
-				int quantized = (slice >> 57) & 0x7;
+				int quantized = (slice >> 61) & 0x7;
 				int dequantized = qoa_dequant_tab[scalefactor][quantized];
 				int reconstructed = qoa_clamp_s16(predicted + dequantized);
 				
@@ -643,7 +657,7 @@
 
 	/* Calculate the required size of the sample buffer and allocate */
 	int total_samples = qoa->samples * qoa->channels;
-	short *sample_data = QOA_MALLOC(total_samples * sizeof(short));
+	short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short));
 
 	unsigned int sample_index = 0;
 	unsigned int frame_len;
@@ -719,7 +733,7 @@
 	bytes_read = fread(data, 1, size, f);
 	fclose(f);
 
-	sample_data = qoa_decode(data, bytes_read, qoa);
+	sample_data = qoa_decode((const unsigned char *)data, bytes_read, qoa);
 	QOA_FREE(data);
 	return sample_data;
 }
diff --git a/raylib/src/external/qoaplay.c b/raylib/src/external/qoaplay.c
--- a/raylib/src/external/qoaplay.c
+++ b/raylib/src/external/qoaplay.c
@@ -46,7 +46,6 @@
     unsigned int sample_position;   // Current streaming sample position
 
     unsigned char *buffer;          // Buffer used to read samples from file/memory (used on decoding)
-    unsigned int buffer_len;        // Buffer length to read samples for streaming
 
     short *sample_data;             // Sample data decoded
     unsigned int sample_data_len;   // Sample data decoded length
@@ -105,13 +104,13 @@
     // + a buffer to hold one frame of encoded data
     unsigned int buffer_size = qoa_max_frame_size(&qoa);
     unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
-    qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
+    qoaplay_desc *qoa_ctx = (qoaplay_desc *)QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
     memset(qoa_ctx, 0, sizeof(qoaplay_desc));
 
     qoa_ctx->file = file;
     qoa_ctx->file_data = NULL;
     qoa_ctx->file_data_size = 0;
-    qoa_ctx->file_data_offset = 0;
+    qoa_ctx->file_data_offset = first_frame_pos;
     qoa_ctx->first_frame_pos = first_frame_pos;
 
     // Setup data pointers to previously allocated data
@@ -128,34 +127,30 @@
 // Open QOA file from memory, no FILE pointer required
 qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size)
 {
-    // Read and decode the file header
-    unsigned char header[QOA_MIN_FILESIZE];
-    memcpy(header, data, QOA_MIN_FILESIZE);
-
     qoa_desc qoa;
-    unsigned int first_frame_pos = qoa_decode_header(header, QOA_MIN_FILESIZE, &qoa);
+    if (data_size < QOA_MIN_FILESIZE) return NULL;
+    unsigned int first_frame_pos = qoa_decode_header(data, QOA_MIN_FILESIZE, &qoa);
     if (!first_frame_pos) return NULL;
 
     // Allocate one chunk of memory for the qoaplay_desc struct
     // + the sample data for one frame
     // + a buffer to hold one frame of encoded data
-    unsigned int buffer_size = qoa_max_frame_size(&qoa);
     unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2;
-    qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size);
+    qoaplay_desc *qoa_ctx = (qoaplay_desc *)QOA_MALLOC(sizeof(qoaplay_desc) + sample_data_size + data_size);
     memset(qoa_ctx, 0, sizeof(qoaplay_desc));
 
     qoa_ctx->file = NULL;
 
     // Keep a copy of file data provided to be managed internally
-    qoa_ctx->file_data = (unsigned char *)QOA_MALLOC(data_size);
+    qoa_ctx->file_data = (((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + sample_data_size);
     memcpy(qoa_ctx->file_data, data, data_size);
     qoa_ctx->file_data_size = data_size;
-    qoa_ctx->file_data_offset = 0;
+    qoa_ctx->file_data_offset = first_frame_pos;
     qoa_ctx->first_frame_pos = first_frame_pos;
 
     // Setup data pointers to previously allocated data
-    qoa_ctx->buffer = ((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc);
-    qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc) + buffer_size);
+    qoa_ctx->buffer = NULL;
+    qoa_ctx->sample_data = (short *)(((unsigned char *)qoa_ctx) + sizeof(qoaplay_desc));
 
     qoa_ctx->info.channels = qoa.channels;
     qoa_ctx->info.samplerate = qoa.samplerate;
@@ -169,11 +164,7 @@
 {
     if (qoa_ctx->file) fclose(qoa_ctx->file);
 
-    if ((qoa_ctx->file_data) && (qoa_ctx->file_data_size > 0))
-    {
-        QOA_FREE(qoa_ctx->file_data);
-        qoa_ctx->file_data_size = 0;
-    }
+    qoa_ctx->file_data_size = 0;
 
     QOA_FREE(qoa_ctx);
 }
@@ -181,16 +172,23 @@
 // Decode one frame from QOA data
 unsigned int qoaplay_decode_frame(qoaplay_desc *qoa_ctx)
 {
-    if (qoa_ctx->file) qoa_ctx->buffer_len = fread(qoa_ctx->buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file);
+    unsigned char *buffer;
+    unsigned int buffer_len;
+
+    if (qoa_ctx->file)
+    {
+        buffer = qoa_ctx->buffer;
+        buffer_len = fread(buffer, 1, qoa_max_frame_size(&qoa_ctx->info), qoa_ctx->file);
+    }
     else
     {
-        qoa_ctx->buffer_len = qoa_max_frame_size(&qoa_ctx->info);
-        memcpy(qoa_ctx->buffer, qoa_ctx->file_data + qoa_ctx->file_data_offset, qoa_ctx->buffer_len);
-        qoa_ctx->file_data_offset += qoa_ctx->buffer_len;
+        buffer = qoa_ctx->file_data + qoa_ctx->file_data_offset;
+        buffer_len = qoa_max_frame_size(&qoa_ctx->info);
+        qoa_ctx->file_data_offset += buffer_len;
     }
 
     unsigned int frame_len;
-    qoa_decode_frame(qoa_ctx->buffer, qoa_ctx->buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len);
+    qoa_decode_frame(buffer, buffer_len, &qoa_ctx->info, qoa_ctx->sample_data, &frame_len);
     qoa_ctx->sample_data_pos = 0;
     qoa_ctx->sample_data_len = frame_len;
 
@@ -201,7 +199,7 @@
 void qoaplay_rewind(qoaplay_desc *qoa_ctx)
 {
     if (qoa_ctx->file) fseek(qoa_ctx->file, qoa_ctx->first_frame_pos, SEEK_SET);
-    else qoa_ctx->file_data_offset = 0;
+    else qoa_ctx->file_data_offset = qoa_ctx->first_frame_pos;
 
     qoa_ctx->sample_position = 0;
     qoa_ctx->sample_data_len = 0;
diff --git a/raylib/src/external/rl_gputex.h b/raylib/src/external/rl_gputex.h
--- a/raylib/src/external/rl_gputex.h
+++ b/raylib/src/external/rl_gputex.h
@@ -1,826 +1,1550 @@
 /**********************************************************************************************
 *
-*   rl_gputex v1.0 - GPU compressed textures loading and saving
-*
-*   DESCRIPTION:
-*
-*     Load GPU compressed image data from image files provided as memory data arrays,
-*     data is loaded compressed, ready to be loaded into GPU.
-*
-*     Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage.
-*     In those cases data is loaded uncompressed and format is returned.
-*
-*   TODO:
-*     - Implement raylib function: rlGetGlTextureFormats(), required by rl_save_ktx_to_memory()
-*     - Review rl_load_ktx_from_memory() to support KTX v2.2 specs
-*
-*   CONFIGURATION:
-*
-*   #define RL_GPUTEX_SUPPORT_DDS
-*   #define RL_GPUTEX_SUPPORT_PKM
-*   #define RL_GPUTEX_SUPPORT_KTX
-*   #define RL_GPUTEX_SUPPORT_PVR
-*   #define RL_GPUTEX_SUPPORT_ASTC
-*       Define desired file formats to be supported
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#ifndef RL_GPUTEX_H
-#define RL_GPUTEX_H
-
-#ifndef RLAPI
-    #define RLAPI       // Functions defined as 'extern' by default (implicit specifiers)
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-#if defined(__cplusplus)
-extern "C" {            // Prevents name mangling of functions
-#endif
-
-// Load image data from memory data files
-RLAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
-RLAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
-RLAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
-RLAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
-RLAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
-
-RLAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps);  // Save image data as KTX file
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif // RL_GPUTEX_H
-
-
-/***********************************************************************************
-*
-*   RL_GPUTEX IMPLEMENTATION
-*
-************************************************************************************/
-
-#if defined(RL_GPUTEX_IMPLEMENTATION)
-
-// Simple log system to avoid RPNG_LOG() calls if required
-// NOTE: Avoiding those calls, also avoids const strings memory usage
-#define RL_GPUTEX_SHOW_LOG_INFO
-#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(LOG)
-#define LOG(...) printf(__VA_ARGS__)
-#else
-#define LOG(...)
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Declaration
-//----------------------------------------------------------------------------------
-// Get pixel data size in bytes for certain pixel format
-static int get_pixel_data_size(int width, int height, int format);
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-#if defined(RL_GPUTEX_SUPPORT_DDS)
-// Loading DDS from memory image data (compressed or uncompressed)
-void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
-{
-    void *image_data = NULL;        // Image data pointer
-    int image_pixel_size = 0;       // Image pixel size
-
-    unsigned char *file_data_ptr = (unsigned char *)file_data;
-
-    // Required extension:
-    // GL_EXT_texture_compression_s3tc
-
-    // Supported tokens (defined by extensions)
-    // GL_COMPRESSED_RGB_S3TC_DXT1_EXT      0x83F0
-    // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT     0x83F1
-    // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT     0x83F2
-    // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT     0x83F3
-
-    #define FOURCC_DXT1 0x31545844  // Equivalent to "DXT1" in ASCII
-    #define FOURCC_DXT3 0x33545844  // Equivalent to "DXT3" in ASCII
-    #define FOURCC_DXT5 0x35545844  // Equivalent to "DXT5" in ASCII
-
-    // DDS Pixel Format
-    typedef struct {
-        unsigned int size;
-        unsigned int flags;
-        unsigned int fourcc;
-        unsigned int rgb_bit_count;
-        unsigned int r_bit_mask;
-        unsigned int g_bit_mask;
-        unsigned int b_bit_mask;
-        unsigned int a_bit_mask;
-    } dds_pixel_format;
-
-    // DDS Header (124 bytes)
-    typedef struct {
-        unsigned int size;
-        unsigned int flags;
-        unsigned int height;
-        unsigned int width;
-        unsigned int pitch_or_linear_size;
-        unsigned int depth;
-        unsigned int mipmap_count;
-        unsigned int reserved1[11];
-        dds_pixel_format ddspf;
-        unsigned int caps;
-        unsigned int caps2;
-        unsigned int caps3;
-        unsigned int caps4;
-        unsigned int reserved2;
-    } dds_header;
-
-    if (file_data_ptr != NULL)
-    {
-        // Verify the type of file
-        unsigned char *dds_header_id = file_data_ptr;
-        file_data_ptr += 4;
-
-        if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' '))
-        {
-            LOG("WARNING: IMAGE: DDS file data not valid");
-        }
-        else
-        {
-            dds_header *header = (dds_header *)file_data_ptr;
-
-            file_data_ptr += sizeof(dds_header);        // Skip header
-
-            *width = header->width;
-            *height = header->height;
-
-            if (*width % 4 != 0) LOG("WARNING: IMAGE: DDS file width must be multiple of 4. Image will not display correctly");
-            if (*height % 4 != 0) LOG("WARNING: IMAGE: DDS file height must be multiple of 4. Image will not display correctly");
-
-            image_pixel_size = header->width*header->height;
-
-            if (header->mipmap_count == 0) *mips = 1;   // Parameter not used
-            else *mips = header->mipmap_count;
-
-            if (header->ddspf.rgb_bit_count == 16)      // 16bit mode, no compressed
-            {
-                if (header->ddspf.flags == 0x40)        // No alpha channel
-                {
-                    int data_size = image_pixel_size*sizeof(unsigned short);
-                    if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
-                    image_data = RL_MALLOC(data_size);
-
-                    memcpy(image_data, file_data_ptr, data_size);
-
-                    *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
-                }
-                else if (header->ddspf.flags == 0x41)           // With alpha channel
-                {
-                    if (header->ddspf.a_bit_mask == 0x8000)     // 1bit alpha
-                    {
-                        int data_size = image_pixel_size*sizeof(unsigned short);
-                        if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
-                        image_data = RL_MALLOC(data_size);
-
-                        memcpy(image_data, file_data_ptr, data_size);
-
-                        unsigned char alpha = 0;
-
-                        // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
-                        for (int i = 0; i < image_pixel_size; i++)
-                        {
-                            alpha = ((unsigned short *)image_data)[i] >> 15;
-                            ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
-                            ((unsigned short *)image_data)[i] += alpha;
-                        }
-
-                        *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
-                    }
-                    else if (header->ddspf.a_bit_mask == 0xf000)   // 4bit alpha
-                    {
-                        int data_size = image_pixel_size*sizeof(unsigned short);
-                        if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
-                        image_data = RL_MALLOC(data_size);
-
-                        memcpy(image_data, file_data_ptr, data_size);
-
-                        unsigned char alpha = 0;
-
-                        // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
-                        for (int i = 0; i < image_pixel_size; i++)
-                        {
-                            alpha = ((unsigned short *)image_data)[i] >> 12;
-                            ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
-                            ((unsigned short *)image_data)[i] += alpha;
-                        }
-
-                        *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
-                    }
-                }
-            }
-            else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24))   // DDS_RGB, no compressed
-            {
-                int data_size = image_pixel_size*3*sizeof(unsigned char);
-                if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
-                image_data = RL_MALLOC(data_size);
-
-                memcpy(image_data, file_data_ptr, data_size);
-
-                *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
-            }
-            else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
-            {
-                int data_size = image_pixel_size*4*sizeof(unsigned char);
-                if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
-                image_data = RL_MALLOC(data_size);
-
-                memcpy(image_data, file_data_ptr, data_size);
-
-                unsigned char blue = 0;
-
-                // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
-                // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
-                // So, we must realign B8G8R8A8 to R8G8B8A8
-                for (int i = 0; i < image_pixel_size*4; i += 4)
-                {
-                    blue = ((unsigned char *)image_data)[i];
-                    ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2];
-                    ((unsigned char *)image_data)[i + 2] = blue;
-                }
-
-                *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
-            }
-            else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
-            {
-                int data_size = 0;
-
-                // Calculate data size, including all mipmaps
-                if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size + header->pitch_or_linear_size / 3;
-                else data_size = header->pitch_or_linear_size;
-
-                image_data = RL_MALLOC(data_size*sizeof(unsigned char));
-
-                memcpy(image_data, file_data_ptr, data_size);
-
-                switch (header->ddspf.fourcc)
-                {
-                    case FOURCC_DXT1:
-                    {
-                        if (header->ddspf.flags == 0x04) *format = PIXELFORMAT_COMPRESSED_DXT1_RGB;
-                        else *format = PIXELFORMAT_COMPRESSED_DXT1_RGBA;
-                    } break;
-                    case FOURCC_DXT3: *format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break;
-                    case FOURCC_DXT5: *format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break;
-                    default: break;
-                }
-            }
-        }
-    }
-
-    return image_data;
-}
-#endif
-
-#if defined(RL_GPUTEX_SUPPORT_PKM)
-// Loading PKM image data (ETC1/ETC2 compression)
-// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
-// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
-void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
-{
-    void *image_data = NULL;        // Image data pointer
-
-    unsigned char *file_data_ptr = (unsigned char *)file_data;
-
-    // Required extensions:
-    // GL_OES_compressed_ETC1_RGB8_texture  (ETC1) (OpenGL ES 2.0)
-    // GL_ARB_ES3_compatibility  (ETC2/EAC) (OpenGL ES 3.0)
-
-    // Supported tokens (defined by extensions)
-    // GL_ETC1_RGB8_OES                 0x8D64
-    // GL_COMPRESSED_RGB8_ETC2          0x9274
-    // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
-
-    // PKM file (ETC1) Header (16 bytes)
-    typedef struct {
-        char id[4];                 // "PKM "
-        char version[2];            // "10" or "20"
-        unsigned short format;      // Data format (big-endian) (Check list below)
-        unsigned short width;       // Texture width (big-endian) (orig_width rounded to multiple of 4)
-        unsigned short height;      // Texture height (big-endian) (orig_height rounded to multiple of 4)
-        unsigned short orig_width;   // Original width (big-endian)
-        unsigned short orig_height;  // Original height (big-endian)
-    } pkm_header;
-
-    // Formats list
-    // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
-    // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
-
-    // NOTE: The extended width and height are the widths rounded up to a multiple of 4.
-    // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
-
-    if (file_data_ptr != NULL)
-    {
-        pkm_header *header = (pkm_header *)file_data_ptr;
-
-        if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' '))
-        {
-            LOG("WARNING: IMAGE: PKM file data not valid");
-        }
-        else
-        {
-            file_data_ptr += sizeof(pkm_header);   // Skip header
-
-            // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
-            header->format = ((header->format & 0x00FF) << 8) | ((header->format & 0xFF00) >> 8);
-            header->width = ((header->width & 0x00FF) << 8) | ((header->width & 0xFF00) >> 8);
-            header->height = ((header->height & 0x00FF) << 8) | ((header->height & 0xFF00) >> 8);
-
-            *width = header->width;
-            *height = header->height;
-            *mips = 1;
-
-            int bpp = 4;
-            if (header->format == 3) bpp = 8;
-
-            int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
-
-            image_data = RL_MALLOC(data_size*sizeof(unsigned char));
-
-            memcpy(image_data, file_data_ptr, data_size);
-
-            if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
-            else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
-            else if (header->format == 3) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
-        }
-    }
-
-    return image_data;
-}
-#endif
-
-#if defined(RL_GPUTEX_SUPPORT_KTX)
-// Load KTX compressed image data (ETC1/ETC2 compression)
-// TODO: Review KTX loading, many things changed!
-void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
-{
-    void *image_data = NULL;        // Image data pointer
-
-    unsigned char *file_data_ptr = (unsigned char *)file_data;
-
-    // Required extensions:
-    // GL_OES_compressed_ETC1_RGB8_texture  (ETC1)
-    // GL_ARB_ES3_compatibility  (ETC2/EAC)
-
-    // Supported tokens (defined by extensions)
-    // GL_ETC1_RGB8_OES                 0x8D64
-    // GL_COMPRESSED_RGB8_ETC2          0x9274
-    // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
-
-    // KTX file Header (64 bytes)
-    // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
-    // v2.0 - http://github.khronos.org/KTX-Specification/
-
-    // KTX 1.1 Header
-    // TODO: Support KTX 2.2 specs!
-    typedef struct {
-        char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"
-        unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
-        unsigned int gl_type;                   // For compressed textures, glType must equal 0
-        unsigned int gl_type_size;              // For compressed texture data, usually 1
-        unsigned int gl_format;                 // For compressed textures is 0
-        unsigned int gl_internal_format;        // Compressed internal format
-        unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)
-        unsigned int width;                     // Texture image width in pixels
-        unsigned int height;                    // Texture image height in pixels
-        unsigned int depth;                     // For 2D textures is 0
-        unsigned int elements;                  // Number of array elements, usually 0
-        unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
-        unsigned int mipmap_levels;             // Non-mipmapped textures = 1
-        unsigned int key_value_data_size;       // Used to encode any arbitrary data...
-    } ktx_header;
-
-    // NOTE: Before start of every mipmap data block, we have: unsigned int data_size
-
-    if (file_data_ptr != NULL)
-    {
-        ktx_header *header = (ktx_header *)file_data_ptr;
-
-        if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') ||
-            (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1'))
-        {
-            LOG("WARNING: IMAGE: KTX file data not valid");
-        }
-        else
-        {
-            file_data_ptr += sizeof(ktx_header);           // Move file data pointer
-
-            *width = header->width;
-            *height = header->height;
-            *mips = header->mipmap_levels;
-
-            file_data_ptr += header->key_value_data_size; // Skip value data size
-
-            int data_size = ((int *)file_data_ptr)[0];
-            file_data_ptr += sizeof(int);
-
-            image_data = RL_MALLOC(data_size*sizeof(unsigned char));
-
-            memcpy(image_data, file_data_ptr, data_size);
-
-            if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
-            else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
-            else if (header->gl_internal_format == 0x9278) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
-
-            // TODO: Support uncompressed data formats? Right now it returns format = 0!
-        }
-    }
-
-    return image_data;
-}
-
-// Save image data as KTX file
-// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
-// TODO: Review KTX saving, many things changed!
-int rl_save_ktx(const char *file_name, void *data, int width, int height, int format, int mipmaps)
-{
-    // KTX file Header (64 bytes)
-    // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
-    // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18
-    typedef struct {
-        char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"         // KTX 2.0: "«KTX 22»\r\n\x1A\n"
-        unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
-        unsigned int gl_type;                   // For compressed textures, glType must equal 0
-        unsigned int gl_type_size;              // For compressed texture data, usually 1
-        unsigned int gl_format;                 // For compressed textures is 0
-        unsigned int gl_internal_format;        // Compressed internal format
-        unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)   // KTX 2.0: UInt32 vkFormat
-        unsigned int width;                     // Texture image width in pixels
-        unsigned int height;                    // Texture image height in pixels
-        unsigned int depth;                     // For 2D textures is 0
-        unsigned int elements;                  // Number of array elements, usually 0
-        unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
-        unsigned int mipmap_levels;             // Non-mipmapped textures = 1
-        unsigned int key_value_data_size;       // Used to encode any arbitrary data...     // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
-                                                                                            // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
-        // KTX 2.0 defines additional header elements...
-    } ktx_header;
-
-    // Calculate file data_size required
-    int data_size = sizeof(ktx_header);
-
-    for (int i = 0, w = width, h = height; i < mipmaps; i++)
-    {
-        data_size += get_pixel_data_size(w, h, format);
-        w /= 2; h /= 2;
-    }
-
-    unsigned char *file_data = RL_CALLOC(data_size, 1);
-    unsigned char *file_data_ptr = file_data;
-
-    ktx_header header = { 0 };
-
-    // KTX identifier (v1.1)
-    //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
-    //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
-
-    const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
-
-    // Get the image header
-    memcpy(header.id, ktx_identifier, 12);  // KTX 1.1 signature
-    header.endianness = 0;
-    header.gl_type = 0;                     // Obtained from format
-    header.gl_type_size = 1;
-    header.gl_format = 0;                   // Obtained from format
-    header.gl_internal_format = 0;          // Obtained from format
-    header.gl_base_internal_format = 0;
-    header.width = width;
-    header.height = height;
-    header.depth = 0;
-    header.elements = 0;
-    header.faces = 1;
-    header.mipmap_levels = mipmaps;         // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
-    header.key_value_data_size = 0;         // No extra data after the header
-
-    rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type);   // rlgl module function
-    header.gl_base_internal_format = header.gl_format;    // KTX 1.1 only
-
-    // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
-
-    if (header.gl_format == -1) LOG("WARNING: IMAGE: GL format not supported for KTX export (%i)", header.gl_format);
-    else
-    {
-        memcpy(file_data_ptr, &header, sizeof(ktx_header));
-        file_data_ptr += sizeof(ktx_header);
-
-        int temp_width = width;
-        int temp_height = height;
-        int data_offset = 0;
-
-        // Save all mipmaps data
-        for (int i = 0; i < mipmaps; i++)
-        {
-            unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format);
-
-            memcpy(file_data_ptr, &data_size, sizeof(unsigned int));
-            memcpy(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size);
-
-            temp_width /= 2;
-            temp_height /= 2;
-            data_offset += data_size;
-            file_data_ptr += (4 + data_size);
-        }
-    }
-
-    // Save file data to file
-    int success = false;
-    FILE *file = fopen(file_name, "wb");
-
-    if (file != NULL)
-    {
-        unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file);
-
-        if (count == 0) LOG("WARNING: FILEIO: [%s] Failed to write file", file_name);
-        else if (count != data_size) LOG("WARNING: FILEIO: [%s] File partially written", file_name);
-        else LOG("INFO: FILEIO: [%s] File saved successfully", file_name);
-
-        int result = fclose(file);
-        if (result == 0) success = true;
-    }
-    else LOG("WARNING: FILEIO: [%s] Failed to open file", file_name);
-
-    RL_FREE(file_data);    // Free file data buffer
-
-    // If all data has been written correctly to file, success = 1
-    return success;
-}
-#endif
-
-#if defined(RL_GPUTEX_SUPPORT_PVR)
-// Loading PVR image data (uncompressed or PVRT compression)
-// NOTE: PVR v2 not supported, use PVR v3 instead
-void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
-{
-    void *image_data = NULL;        // Image data pointer
-
-    unsigned char *file_data_ptr = (unsigned char *)file_data;
-
-    // Required extension:
-    // GL_IMG_texture_compression_pvrtc
-
-    // Supported tokens (defined by extensions)
-    // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG       0x8C00
-    // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG      0x8C02
-
-#if 0   // Not used...
-    // PVR file v2 Header (52 bytes)
-    typedef struct {
-        unsigned int headerLength;
-        unsigned int height;
-        unsigned int width;
-        unsigned int numMipmaps;
-        unsigned int flags;
-        unsigned int dataLength;
-        unsigned int bpp;
-        unsigned int bitmaskRed;
-        unsigned int bitmaskGreen;
-        unsigned int bitmaskBlue;
-        unsigned int bitmaskAlpha;
-        unsigned int pvrTag;
-        unsigned int numSurfs;
-    } PVRHeaderV2;
-#endif
-
-    // PVR file v3 Header (52 bytes)
-    // NOTE: After it could be metadata (15 bytes?)
-    typedef struct {
-        char id[4];
-        unsigned int flags;
-        unsigned char channels[4];      // pixelFormat high part
-        unsigned char channel_depth[4];  // pixelFormat low part
-        unsigned int color_space;
-        unsigned int channel_type;
-        unsigned int height;
-        unsigned int width;
-        unsigned int depth;
-        unsigned int num_surfaces;
-        unsigned int num_faces;
-        unsigned int num_mipmaps;
-        unsigned int metadata_size;
-    } pvr_header;
-
-#if 0   // Not used...
-    // Metadata (usually 15 bytes)
-    typedef struct {
-        unsigned int devFOURCC;
-        unsigned int key;
-        unsigned int data_size;      // Not used?
-        unsigned char *data;        // Not used?
-    } PVRMetadata;
-#endif
-
-    if (file_data_ptr != NULL)
-    {
-        // Check PVR image version
-        unsigned char pvr_version = file_data_ptr[0];
-
-        // Load different PVR data formats
-        if (pvr_version == 0x50)
-        {
-            pvr_header *header = (pvr_header *)file_data_ptr;
-
-            if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3))
-            {
-                LOG("WARNING: IMAGE: PVR file data not valid");
-            }
-            else
-            {
-                file_data_ptr += sizeof(pvr_header);   // Skip header
-
-                *width = header->width;
-                *height = header->height;
-                *mips = header->num_mipmaps;
-
-                // Check data format
-                if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
-                else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
-                else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b'))
-                {
-                    if (header->channels[3] == 'a')
-                    {
-                        if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
-                        else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
-                        else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
-                    }
-                    else if (header->channels[3] == 0)
-                    {
-                        if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
-                        else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
-                    }
-                }
-                else if (header->channels[0] == 2) *format = PIXELFORMAT_COMPRESSED_PVRT_RGB;
-                else if (header->channels[0] == 3) *format = PIXELFORMAT_COMPRESSED_PVRT_RGBA;
-
-                file_data_ptr += header->metadata_size;    // Skip meta data header
-
-                // Calculate data size (depends on format)
-                int bpp = 0;
-                switch (*format)
-                {
-                    case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
-                    case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
-                    case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
-                    case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
-                    case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
-                    case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
-                    case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
-                    case PIXELFORMAT_COMPRESSED_PVRT_RGB:
-                    case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
-                    default: break;
-                }
-
-                int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
-                image_data = RL_MALLOC(data_size*sizeof(unsigned char));
-
-                memcpy(image_data, file_data_ptr, data_size);
-            }
-        }
-        else if (pvr_version == 52) LOG("INFO: IMAGE: PVRv2 format not supported, update your files to PVRv3");
-    }
-
-    return image_data;
-}
-#endif
-
-#if defined(RL_GPUTEX_SUPPORT_ASTC)
-// Load ASTC compressed image data (ASTC compression)
-void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
-{
-    void *image_data = NULL;        // Image data pointer
-
-    unsigned char *file_data_ptr = (unsigned char *)file_data;
-
-    // Required extensions:
-    // GL_KHR_texture_compression_astc_hdr
-    // GL_KHR_texture_compression_astc_ldr
-
-    // Supported tokens (defined by extensions)
-    // GL_COMPRESSED_RGBA_ASTC_4x4_KHR      0x93b0
-    // GL_COMPRESSED_RGBA_ASTC_8x8_KHR      0x93b7
-
-    // ASTC file Header (16 bytes)
-    typedef struct {
-        unsigned char id[4];        // Signature: 0x13 0xAB 0xA1 0x5C
-        unsigned char blockX;       // Block X dimensions
-        unsigned char blockY;       // Block Y dimensions
-        unsigned char blockZ;       // Block Z dimensions (1 for 2D images)
-        unsigned char width[3];     // void *width in pixels (24bit value)
-        unsigned char height[3];    // void *height in pixels (24bit value)
-        unsigned char length[3];    // void *Z-size (1 for 2D images)
-    } astc_header;
-
-    if (file_data_ptr != NULL)
-    {
-        astc_header *header = (astc_header *)file_data_ptr;
-
-        if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13))
-        {
-            LOG("WARNING: IMAGE: ASTC file data not valid");
-        }
-        else
-        {
-            file_data_ptr += sizeof(astc_header);   // Skip header
-
-            // NOTE: Assuming Little Endian (could it be wrong?)
-            *width = 0x00000000 | ((int)header->width[2] << 16) | ((int)header->width[1] << 8) | ((int)header->width[0]);
-            *height = 0x00000000 | ((int)header->height[2] << 16) | ((int)header->height[1] << 8) | ((int)header->height[0]);
-            *mips = 1;      // NOTE: ASTC format only contains one mipmap level
-
-            // NOTE: Each block is always stored in 128bit so we can calculate the bpp
-            int bpp = 128/(header->blockX*header->blockY);
-
-            // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
-            if ((bpp == 8) || (bpp == 2))
-            {
-                int data_size = (*width)*(*height)*bpp/8;  // Data size in bytes
-
-                image_data = RL_MALLOC(data_size*sizeof(unsigned char));
-
-                memcpy(image_data, file_data_ptr, data_size);
-
-                if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
-                else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
-            }
-            else LOG("WARNING: IMAGE: ASTC block size configuration not supported");
-        }
-    }
-
-    return image_data;
-}
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Definition
-//----------------------------------------------------------------------------------
-// Get pixel data size in bytes for certain pixel format
-static int get_pixel_data_size(int width, int height, int format)
-{
-    int data_size = 0;       // Size in bytes
-    int bpp = 0;            // Bits per pixel
-
-    switch (format)
-    {
-        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
-        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
-        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
-        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
-        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
-        case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
-        case PIXELFORMAT_COMPRESSED_DXT1_RGB:
-        case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
-        case PIXELFORMAT_COMPRESSED_ETC1_RGB:
-        case PIXELFORMAT_COMPRESSED_ETC2_RGB:
-        case PIXELFORMAT_COMPRESSED_PVRT_RGB:
-        case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
-        case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
-        case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
-        case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
-        case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
-        case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
-        default: break;
-    }
-
-    data_size = width*height*bpp/8;  // Total data size in bytes
-
-    // Most compressed formats works on 4x4 blocks,
-    // if texture is smaller, minimum dataSize is 8 or 16
-    if ((width < 4) && (height < 4))
-    {
-        if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8;
-        else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16;
-    }
-
-    return data_size;
-}
-#endif // RL_GPUTEX_IMPLEMENTATION
+*   rl_gputex v1.1 - GPU compressed textures loading and saving
+*
+*   DESCRIPTION:
+*
+*     Load GPU compressed image data from image files provided as memory data arrays,
+*     data is loaded compressed, ready to be loaded into GPU
+*
+*     Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage.
+*     In those cases data is loaded uncompressed and format is returned
+*
+*   FIXME: This library still depends on Raylib due to the following reasons:
+*     - rl_save_ktx_to_memory() requires rlGetGlTextureFormats() from rlgl.h
+*         though this is not a problem, if you don't need KTX support
+*
+*   TODO:
+*     - Review rl_load_ktx_from_memory() to support KTX v2.2 specs
+*
+*   CONFIGURATION:
+*
+*   #define RL_GPUTEX_SUPPORT_DDS
+*   #define RL_GPUTEX_SUPPORT_PKM
+*   #define RL_GPUTEX_SUPPORT_KTX
+*   #define RL_GPUTEX_SUPPORT_PVR
+*   #define RL_GPUTEX_SUPPORT_ASTC
+*       Define desired file formats to be supported
+*
+*   #define RL_GPUTEX_SHOW_LOG_INFO
+*       Define, if you wish to see warnings generated by the library
+*       This will include <stdio.h> unless you provide your own RL_GPUTEX_LOG
+*   #define RL_GPUTEX_LOG
+*       Define, if you wish to provide your own warning function
+*       Make sure that this macro puts newline character '\n' at the end
+*
+*   #define RL_GPUTEX_MALLOC
+*   #define RL_GPUTEX_FREE
+*       Define those macros in order to provide your own libc-compliant allocator;
+*       not doing so will include <stdlib.h>
+*       If you're providing any of those, you must provide ALL of them,
+*       otherwise the code will (most likely) crash...
+*
+*   #define RL_GPUTEX_NULL
+*       Define in order to provide your own libc-compliant NULL pointer;
+*       not doing so will include <stdlib.h>
+*
+*   #define RL_GPUTEX_MEMCPY
+*       Define in order to provide your own libc-compliant 'memcpy' function;
+*       not doing so will include <string.h>
+*
+*   #define RLGPUTEXAPI
+*       Define to compiler-specific intrinsic, if you wish to export public functions
+*       There is no need to do so when statically linking
+*
+*   VERSIONS HISTORY:
+*       1.1 (15-Jul-2025) Several minor fixes related to specific image formats; some work has been done
+*           in order to decouple the library from Raylib by introducing few new macros; library still
+*           requires Raylib in order to function properly
+*
+*       1.0 (17-Sep-2022) First version has been created by migrating part of compressed-texture-loading
+*           functionality from Raylib src/rtextures.c into self-contained library
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RL_GPUTEX_H
+#define RL_GPUTEX_H
+
+#ifndef RLGPUTEXAPI
+    #define RLGPUTEXAPI  // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+// Texture pixel formats
+// NOTE: Support depends on OpenGL version
+// WARNING: Enum values aligned with raylib/rlgl equivalent PixelFormat/rlPixelFormat enum,
+// to avoid value mapping between the 3 libraries format values
+typedef enum {
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1,     // 8 bit per pixel (no alpha)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,        // 8*2 bpp (2 channels)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5,            // 16 bpp
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8,            // 24 bpp
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,          // 16 bpp (1 bit alpha)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4,          // 16 bpp (4 bit alpha)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,          // 32 bpp
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32,               // 32 bpp (1 channel - float)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32,         // 32*3 bpp (3 channels - float)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32,      // 32*4 bpp (4 channels - float)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16,               // 16 bpp (1 channel - half float)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16,         // 16*3 bpp (3 channels - half float)
+    RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16,      // 16*4 bpp (4 channels - half float)
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB,            // 4 bpp (no alpha)
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA,           // 4 bpp (1 bit alpha)
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA,           // 8 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA,           // 8 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB,            // 4 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB,            // 4 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA,       // 8 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB,            // 4 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA,           // 4 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA,       // 8 bpp
+    RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA        // 2 bpp
+} rlGpuTexPixelFormat;
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+#if defined(__cplusplus)
+extern "C" {            // Prevents name mangling of functions
+#endif
+
+// Load image data from memory data files
+RLGPUTEXAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
+RLGPUTEXAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
+RLGPUTEXAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
+RLGPUTEXAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
+RLGPUTEXAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
+
+RLGPUTEXAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps);  // Save image data as KTX file
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // RL_GPUTEX_H
+
+/***********************************************************************************
+*
+*   RL_GPUTEX IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RL_GPUTEX_IMPLEMENTATION)
+
+#if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(RL_GPUTEX_LOG)
+    #include <stdio.h>          // Required for: printf()
+#endif
+#if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_NULL)
+    #include <stdlib.h>         // Required for: NULL, malloc(), calloc(), free()
+#endif
+#if !defined(RL_GPUTEX_MEMCPY)
+    #include <string.h>         // Required for: memcpy()
+#endif
+
+#if defined(RL_GPUTEX_MALLOC) || defined(RL_GPUTEX_FREE)
+    #if !defined(RL_GPUTEX_MALLOC) || !defined(RL_GPUTEX_FREE)
+        #warning "RL_GPUTEX_MALLOC and RL_GPUTEX_FREE allocation functions are required to be provided"
+    #endif
+#endif
+#if !defined(RL_GPUTEX_MALLOC)
+    #define RL_GPUTEX_MALLOC(sz)        malloc(sz)
+    #define RL_GPUTEX_FREE(ptr)         free(ptr)
+#endif
+
+#if !defined(RL_GPUTEX_NULL)
+    #define RL_GPUTEX_NULL NULL
+#endif
+#if !defined(RL_GPUTEX_MEMCPY)
+    #define RL_GPUTEX_MEMCPY(dest, src, num)  memcpy(dest, src, num)
+#endif
+
+// Simple warning logging system to avoid LOG() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+// WARN: This macro expects that newline character is added automatically
+//       in order to match the functionality of Raylib's TRACELOG()
+#if defined(RL_GPUTEX_SHOW_LOG_INFO)
+    #if !defined(RL_GPUTEX_LOG)
+        #define RL_GPUTEX_LOG(...) (void)(printf("RL_GPUTEX: WARNING: " __VA_ARGS__), printf("\n"))
+    #endif
+#else
+    #if defined(RL_GPUTEX_LOG)
+        // Undefine it first in order to supress warnings about macro redefinition
+        #undef RL_GPUTEX_LOG
+    #endif
+    #define RL_GPUTEX_LOG(...)
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+// Get pixel data size in bytes for certain pixel format
+static int get_pixel_data_size(int width, int height, int format);
+
+// Get OpenGL internal formats and data type from rlGpuTexPixelFormat
+void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsigned int *gl_format, unsigned int *gl_type);
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+#if defined(RL_GPUTEX_SUPPORT_DDS)
+// Loading DDS from memory image data (compressed or uncompressed)
+void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
+{
+    void *image_data = RL_GPUTEX_NULL;  // Image data pointer
+    int image_pixel_size = 0;           // Image pixel size
+
+    unsigned char *file_data_ptr = (unsigned char *)file_data;
+
+    // Required extension:
+    // GL_EXT_texture_compression_s3tc
+
+    // Supported tokens (defined by extensions)
+    // GL_COMPRESSED_RGB_S3TC_DXT1_EXT      0x83F0
+    // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT     0x83F1
+    // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT     0x83F2
+    // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT     0x83F3
+
+    #define FOURCC_DXT1 0x31545844  // Equivalent to "DXT1" in ASCII
+    #define FOURCC_DXT3 0x33545844  // Equivalent to "DXT3" in ASCII
+    #define FOURCC_DXT5 0x35545844  // Equivalent to "DXT5" in ASCII
+
+    // DDS Pixel Format
+    typedef struct {
+        unsigned int size;
+        unsigned int flags;
+        unsigned int fourcc;
+        unsigned int rgb_bit_count;
+        unsigned int r_bit_mask;
+        unsigned int g_bit_mask;
+        unsigned int b_bit_mask;
+        unsigned int a_bit_mask;
+    } dds_pixel_format;
+
+    // DDS Header (124 bytes)
+    typedef struct {
+        unsigned int size;
+        unsigned int flags;
+        unsigned int height;
+        unsigned int width;
+        unsigned int pitch_or_linear_size;
+        unsigned int depth;
+        unsigned int mipmap_count;
+        unsigned int reserved1[11];
+        dds_pixel_format ddspf;
+        unsigned int caps;
+        unsigned int caps2;
+        unsigned int caps3;
+        unsigned int caps4;
+        unsigned int reserved2;
+    } dds_header;
+
+    if (file_data_ptr != RL_GPUTEX_NULL)
+    {
+        // Verify the type of file
+        unsigned char *dds_header_id = file_data_ptr;
+        file_data_ptr += 4;
+
+        if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' '))
+        {
+            RL_GPUTEX_LOG("DDS file data not valid");
+        }
+        else
+        {
+            dds_header *header = (dds_header *)file_data_ptr;
+
+            file_data_ptr += sizeof(dds_header);        // Skip header
+
+            *width = header->width;
+            *height = header->height;
+
+            if (*width % 4 != 0) RL_GPUTEX_LOG("DDS file width must be multiple of 4. Image will not display correctly");
+            if (*height % 4 != 0) RL_GPUTEX_LOG("DDS file height must be multiple of 4. Image will not display correctly");
+
+            image_pixel_size = header->width*header->height;
+
+            if (header->mipmap_count == 0) *mips = 1;   // Parameter not used
+            else *mips = header->mipmap_count;
+
+            if (header->ddspf.rgb_bit_count == 16)      // 16bit mode, no compressed
+            {
+                if (header->ddspf.flags == 0x40)        // No alpha channel
+                {
+                    int data_size = image_pixel_size*sizeof(unsigned short);
+                    if (header->mipmap_count > 1) data_size = data_size + data_size/3;
+                    image_data = RL_GPUTEX_MALLOC(data_size);
+
+                    RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                    *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5;
+                }
+                else if (header->ddspf.flags == 0x41)           // With alpha channel
+                {
+                    if (header->ddspf.a_bit_mask == 0x8000)     // 1bit alpha
+                    {
+                        int data_size = image_pixel_size*sizeof(unsigned short);
+                        if (header->mipmap_count > 1) data_size = data_size + data_size/3;
+                        image_data = RL_GPUTEX_MALLOC(data_size);
+
+                        RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                        unsigned char alpha = 0;
+
+                        // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
+                        for (int i = 0; i < data_size/sizeof(unsigned short); i++)
+                        {
+                            alpha = ((unsigned short *)image_data)[i] >> 15;
+                            ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
+                            ((unsigned short *)image_data)[i] += alpha;
+                        }
+
+                        *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
+                    }
+                    else if (header->ddspf.a_bit_mask == 0xf000)   // 4bit alpha
+                    {
+                        int data_size = image_pixel_size*sizeof(unsigned short);
+                        if (header->mipmap_count > 1) data_size = data_size + data_size/3;
+                        image_data = RL_GPUTEX_MALLOC(data_size);
+
+                        RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                        unsigned char alpha = 0;
+
+                        // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
+                        for (int i = 0; i < data_size/sizeof(unsigned short); i++)
+                        {
+                            alpha = ((unsigned short *)image_data)[i] >> 12;
+                            ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
+                            ((unsigned short *)image_data)[i] += alpha;
+                        }
+
+                        *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
+                    }
+                }
+            }
+            else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24)) // DDS_RGB, no compressed
+            {
+                int data_size = image_pixel_size*3*sizeof(unsigned char);
+                if (header->mipmap_count > 1) data_size = data_size + data_size/3;
+                image_data = RL_GPUTEX_MALLOC(data_size);
+
+                RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8;
+            }
+            else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
+            {
+                int data_size = image_pixel_size*4*sizeof(unsigned char);
+                if (header->mipmap_count > 1) data_size = data_size + data_size/3;
+                image_data = RL_GPUTEX_MALLOC(data_size);
+
+                RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                unsigned char blue = 0;
+
+                // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
+                // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
+                // So, we must realign B8G8R8A8 to R8G8B8A8
+                for (int i = 0; i < data_size; i += 4)
+                {
+                    blue = ((unsigned char *)image_data)[i];
+                    ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2];
+                    ((unsigned char *)image_data)[i + 2] = blue;
+                }
+
+                *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+            }
+            else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
+            {
+                int data_size = 0;
+
+                // Calculate data size, including all mipmaps
+                if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size + header->pitch_or_linear_size/3;
+                else data_size = header->pitch_or_linear_size;
+
+                image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char));
+
+                RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                switch (header->ddspf.fourcc)
+                {
+                    case FOURCC_DXT1:
+                    {
+                        if (header->ddspf.flags == 0x04) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB;
+                        else *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA;
+                    } break;
+                    case FOURCC_DXT3: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA; break;
+                    case FOURCC_DXT5: *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA; break;
+                    default: break;
+                }
+            }
+        }
+    }
+
+    return image_data;
+}
+#endif
+
+#if defined(RL_GPUTEX_SUPPORT_PKM)
+// Loading PKM image data (ETC1/ETC2 compression)
+// NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
+// PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
+void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
+{
+    void *image_data = RL_GPUTEX_NULL;        // Image data pointer
+
+    unsigned char *file_data_ptr = (unsigned char *)file_data;
+
+    // Required extensions:
+    // GL_OES_compressed_ETC1_RGB8_texture  (ETC1) (OpenGL ES 2.0)
+    // GL_ARB_ES3_compatibility  (ETC2/EAC) (OpenGL ES 3.0)
+
+    // Supported tokens (defined by extensions)
+    // GL_ETC1_RGB8_OES                 0x8D64
+    // GL_COMPRESSED_RGB8_ETC2          0x9274
+    // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
+
+    // PKM file (ETC1) Header (16 bytes)
+    typedef struct {
+        char id[4];                 // "PKM "
+        char version[2];            // "10" or "20"
+        unsigned short format;      // Data format (big-endian) (Check list below)
+        unsigned short width;       // Texture width (big-endian) (orig_width rounded to multiple of 4)
+        unsigned short height;      // Texture height (big-endian) (orig_height rounded to multiple of 4)
+        unsigned short orig_width;   // Original width (big-endian)
+        unsigned short orig_height;  // Original height (big-endian)
+    } pkm_header;
+
+    // Formats list
+    // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
+    // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
+
+    // NOTE: The extended width and height are the widths rounded up to a multiple of 4
+    // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
+
+    if (file_data_ptr != RL_GPUTEX_NULL)
+    {
+        pkm_header *header = (pkm_header *)file_data_ptr;
+
+        if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' '))
+        {
+            RL_GPUTEX_LOG("PKM file data not valid");
+        }
+        else
+        {
+            file_data_ptr += sizeof(pkm_header);   // Skip header
+
+            // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
+            header->format = ((header->format & 0x00FF) << 8) | ((header->format & 0xFF00) >> 8);
+            header->width = ((header->width & 0x00FF) << 8) | ((header->width & 0xFF00) >> 8);
+            header->height = ((header->height & 0x00FF) << 8) | ((header->height & 0xFF00) >> 8);
+
+            *width = header->width;
+            *height = header->height;
+            *mips = 1;
+
+            int bpp = 4;
+            if (header->format == 3) bpp = 8;
+
+            int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
+
+            image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char));
+
+            RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+            if (header->format == 0) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB;
+            else if (header->format == 1) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB;
+            else if (header->format == 3) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
+        }
+    }
+
+    return image_data;
+}
+#endif
+
+#if defined(RL_GPUTEX_SUPPORT_KTX)
+// Load KTX compressed image data (ETC1/ETC2 compression)
+// TODO: Review KTX loading, many things changed!
+void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
+{
+    void *image_data = RL_GPUTEX_NULL;        // Image data pointer
+
+    unsigned char *file_data_ptr = (unsigned char *)file_data;
+
+    // Required extensions:
+    // GL_OES_compressed_ETC1_RGB8_texture  (ETC1)
+    // GL_ARB_ES3_compatibility  (ETC2/EAC)
+
+    // Supported tokens (defined by extensions)
+    // GL_ETC1_RGB8_OES                 0x8D64
+    // GL_COMPRESSED_RGB8_ETC2          0x9274
+    // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
+
+    // KTX file Header (64 bytes)
+    // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+    // v2.0 - http://github.khronos.org/KTX-Specification/
+
+    // KTX 1.1 Header
+    // TODO: Support KTX 2.2 specs!
+    typedef struct {
+        char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"
+        unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
+        unsigned int gl_type;                   // For compressed textures, glType must equal 0
+        unsigned int gl_type_size;              // For compressed texture data, usually 1
+        unsigned int gl_format;                 // For compressed textures is 0
+        unsigned int gl_internal_format;        // Compressed internal format
+        unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)
+        unsigned int width;                     // Texture image width in pixels
+        unsigned int height;                    // Texture image height in pixels
+        unsigned int depth;                     // For 2D textures is 0
+        unsigned int elements;                  // Number of array elements, usually 0
+        unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
+        unsigned int mipmap_levels;             // Non-mipmapped textures = 1
+        unsigned int key_value_data_size;       // Used to encode any arbitrary data...
+    } ktx_header;
+
+    // NOTE: Before start of every mipmap data block, we have: unsigned int data_size
+
+    if (file_data_ptr != RL_GPUTEX_NULL)
+    {
+        ktx_header *header = (ktx_header *)file_data_ptr;
+
+        if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') ||
+            (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1'))
+        {
+            RL_GPUTEX_LOG("KTX file data not valid");
+        }
+        else
+        {
+            file_data_ptr += sizeof(ktx_header);           // Move file data pointer
+
+            *width = header->width;
+            *height = header->height;
+            *mips = header->mipmap_levels;
+
+            file_data_ptr += header->key_value_data_size; // Skip value data size
+
+            int data_size = ((int *)file_data_ptr)[0];
+            file_data_ptr += sizeof(int);
+
+            image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char));
+
+            RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+            if (header->gl_internal_format == 0x8D64) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB;
+            else if (header->gl_internal_format == 0x9274) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB;
+            else if (header->gl_internal_format == 0x9278) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
+
+            // TODO: Support uncompressed data formats? Right now it returns format = 0!
+        }
+    }
+
+    return image_data;
+}
+
+// Save image data as KTX file
+// NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
+// TODO: Review KTX saving, many things changed!
+int rl_save_ktx(const char *file_name, void *data, int width, int height, int format, int mipmaps)
+{
+    // KTX file Header (64 bytes)
+    // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+    // v2.0 - https://github.khronos.org/KTX-Specification/ktxspec.v2.html
+    typedef struct {
+        char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"         // KTX 2.0: "«KTX 20»\r\n\x1A\n"
+        unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
+        unsigned int gl_type;                   // For compressed textures, glType must equal 0
+        unsigned int gl_type_size;              // For compressed texture data, usually 1
+        unsigned int gl_format;                 // For compressed textures is 0
+        unsigned int gl_internal_format;        // Compressed internal format
+        unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)   // KTX 2.0: UInt32 vkFormat
+        unsigned int width;                     // Texture image width in pixels
+        unsigned int height;                    // Texture image height in pixels
+        unsigned int depth;                     // For 2D textures is 0
+        unsigned int elements;                  // Number of array elements, usually 0
+        unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
+        unsigned int mipmap_levels;             // Non-mipmapped textures = 1
+        unsigned int key_value_data_size;       // Used to encode any arbitrary data...     // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
+                                                                                            // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
+        // KTX 2.0 defines additional header elements...
+    } ktx_header;
+
+    /*
+    Byte[12] identifier
+    UInt32 vkFormat
+    UInt32 typeSize
+    UInt32 pixelWidth
+    UInt32 pixelHeight
+    UInt32 pixelDepth
+    UInt32 layerCount
+    UInt32 faceCount
+    UInt32 levelCount
+    UInt32 supercompressionScheme
+    */
+
+    // Calculate file data_size required
+    int data_size = sizeof(ktx_header);
+
+    for (int i = 0, w = width, h = height; i < mipmaps; i++)
+    {
+        data_size += get_pixel_data_size(w, h, format);
+        w /= 2; h /= 2;
+    }
+
+    unsigned char *file_data = RL_GPUTEX_MALLOC(data_size);
+    unsigned char *file_data_ptr = file_data;
+
+    ktx_header header = { 0 };
+
+    // KTX identifier (v1.1)
+    //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
+    //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+    const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
+
+    // Get the image header
+    RL_GPUTEX_MEMCPY(header.id, ktx_identifier, 12);  // KTX 1.1 signature
+    header.endianness = 0;
+    header.gl_type = 0;                     // Obtained from format
+    header.gl_type_size = 1;
+    header.gl_format = 0;                   // Obtained from format
+    header.gl_internal_format = 0;          // Obtained from format
+    header.gl_base_internal_format = 0;
+    header.width = width;
+    header.height = height;
+    header.depth = 0;
+    header.elements = 0;
+    header.faces = 1;
+    header.mipmap_levels = mipmaps;         // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
+    header.key_value_data_size = 0;         // No extra data after the header
+
+    // TODO: WARNING: Function dependant on rlgl library!
+    rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type); // rlgl module function
+
+    header.gl_base_internal_format = header.gl_format; // TODO: WARNING: KTX 1.1 only
+
+    // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
+
+    if (header.gl_format == -1) RL_GPUTEX_LOG("RL_GPUTEX: GL format not supported for KTX export (%i)", header.gl_format);
+    else
+    {
+        RL_GPUTEX_MEMCPY(file_data_ptr, &header, sizeof(ktx_header));
+        file_data_ptr += sizeof(ktx_header);
+
+        int temp_width = width;
+        int temp_height = height;
+        int data_offset = 0;
+
+        // Save all mipmaps data
+        for (int i = 0; i < mipmaps; i++)
+        {
+            unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format);
+
+            RL_GPUTEX_MEMCPY(file_data_ptr, &data_size, sizeof(unsigned int));
+            RL_GPUTEX_MEMCPY(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size);
+
+            temp_width /= 2;
+            temp_height /= 2;
+            data_offset += data_size;
+            file_data_ptr += (4 + data_size);
+        }
+    }
+
+    // Save file data to file
+    int success = false;
+    FILE *file = fopen(file_name, "wb");
+
+    if (file != RL_GPUTEX_NULL)
+    {
+        unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file);
+
+        if (count == 0) RL_GPUTEX_LOG("FILEIO: [%s] Failed to write file", file_name);
+        else if (count != data_size) RL_GPUTEX_LOG("FILEIO: [%s] File partially written", file_name);
+        else (void)0; // WARN: this branch is handled by Raylib, since rl_gputex only prints warnings
+
+        int result = fclose(file);
+        if (result != 0) RL_GPUTEX_LOG("FILEIO: [%s] Failed to close file", file_name);
+
+        if (result == 0 && count == data_size) success = true;
+    }
+    else RL_GPUTEX_LOG("FILEIO: [%s] Failed to open file", file_name);
+
+    RL_GPUTEX_FREE(file_data);    // Free file data buffer
+
+    // If all data has been written correctly to file, success = 1
+    return success;
+}
+#endif
+
+#if defined(RL_GPUTEX_SUPPORT_PVR)
+// Loading PVR image data (uncompressed or PVRT compression)
+// NOTE: PVR v2 not supported, use PVR v3 instead
+void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
+{
+    void *image_data = RL_GPUTEX_NULL;        // Image data pointer
+
+    unsigned char *file_data_ptr = (unsigned char *)file_data;
+
+    // Required extension:
+    // GL_IMG_texture_compression_pvrtc
+
+    // Supported tokens (defined by extensions)
+    // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG       0x8C00
+    // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG      0x8C02
+
+#if 0   // Not used...
+    // PVR file v2 Header (52 bytes)
+    typedef struct {
+        unsigned int headerLength;
+        unsigned int height;
+        unsigned int width;
+        unsigned int numMipmaps;
+        unsigned int flags;
+        unsigned int dataLength;
+        unsigned int bpp;
+        unsigned int bitmaskRed;
+        unsigned int bitmaskGreen;
+        unsigned int bitmaskBlue;
+        unsigned int bitmaskAlpha;
+        unsigned int pvrTag;
+        unsigned int numSurfs;
+    } PVRHeaderV2;
+#endif
+
+    // PVR file v3 Header (52 bytes)
+    // NOTE: After it could be metadata (15 bytes?)
+    typedef struct {
+        char id[4];
+        unsigned int flags;
+        unsigned char channels[4];      // pixelFormat high part
+        unsigned char channel_depth[4];  // pixelFormat low part
+        unsigned int color_space;
+        unsigned int channel_type;
+        unsigned int height;
+        unsigned int width;
+        unsigned int depth;
+        unsigned int num_surfaces;
+        unsigned int num_faces;
+        unsigned int num_mipmaps;
+        unsigned int metadata_size;
+    } pvr_header;
+
+#if 0   // Not used...
+    // Metadata (usually 15 bytes)
+    typedef struct {
+        unsigned int devFOURCC;
+        unsigned int key;
+        unsigned int data_size;      // Not used?
+        unsigned char *data;        // Not used?
+    } PVRMetadata;
+#endif
+
+    if (file_data_ptr != RL_GPUTEX_NULL)
+    {
+        // Check PVR image version
+        unsigned char pvr_version = file_data_ptr[0];
+
+        // Load different PVR data formats
+        if (pvr_version == 0x50)
+        {
+            pvr_header *header = (pvr_header *)file_data_ptr;
+
+            if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3))
+            {
+                RL_GPUTEX_LOG("PVR file data not valid");
+            }
+            else
+            {
+                file_data_ptr += sizeof(pvr_header);   // Skip header
+
+                *width = header->width;
+                *height = header->height;
+                *mips = header->num_mipmaps;
+
+                // Check data format
+                if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+                else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
+                else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b'))
+                {
+                    if (header->channels[3] == 'a')
+                    {
+                        if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
+                        else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
+                        else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+                    }
+                    else if (header->channels[3] == 0)
+                    {
+                        if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5;
+                        else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8;
+                    }
+                }
+                else if (header->channels[0] == 2) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB;
+                else if (header->channels[0] == 3) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA;
+
+                file_data_ptr += header->metadata_size;    // Skip meta data header
+
+                // Calculate data size (depends on format)
+                int bpp = 0;
+                switch (*format)
+                {
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5:
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
+                    case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
+                    case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB:
+                    case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
+                    default: break;
+                }
+
+                int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
+                image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char));
+
+                RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+            }
+        }
+        else if (pvr_version == 52) RL_GPUTEX_LOG("PVRv2 format not supported, update your files to PVRv3");
+    }
+
+    return image_data;
+}
+#endif
+
+#if defined(RL_GPUTEX_SUPPORT_ASTC)
+// Load ASTC compressed image data (ASTC compression)
+void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
+{
+    void *image_data = RL_GPUTEX_NULL;        // Image data pointer
+
+    unsigned char *file_data_ptr = (unsigned char *)file_data;
+
+    // Required extensions:
+    // GL_KHR_texture_compression_astc_hdr
+    // GL_KHR_texture_compression_astc_ldr
+
+    // Supported tokens (defined by extensions)
+    // GL_COMPRESSED_RGBA_ASTC_4x4_KHR      0x93b0
+    // GL_COMPRESSED_RGBA_ASTC_8x8_KHR      0x93b7
+
+    // ASTC file Header (16 bytes)
+    typedef struct {
+        unsigned char id[4];        // Signature: 0x13 0xAB 0xA1 0x5C
+        unsigned char blockX;       // Block X dimensions
+        unsigned char blockY;       // Block Y dimensions
+        unsigned char blockZ;       // Block Z dimensions (1 for 2D images)
+        unsigned char width[3];     // void *width in pixels (24bit value)
+        unsigned char height[3];    // void *height in pixels (24bit value)
+        unsigned char length[3];    // void *Z-size (1 for 2D images)
+    } astc_header;
+
+    if (file_data_ptr != RL_GPUTEX_NULL)
+    {
+        astc_header *header = (astc_header *)file_data_ptr;
+
+        if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13))
+        {
+            RL_GPUTEX_LOG("ASTC file data not valid");
+        }
+        else
+        {
+            file_data_ptr += sizeof(astc_header);   // Skip header
+
+            // NOTE: Assuming Little Endian (could it be wrong?)
+            *width = 0x00000000 | ((int)header->width[2] << 16) | ((int)header->width[1] << 8) | ((int)header->width[0]);
+            *height = 0x00000000 | ((int)header->height[2] << 16) | ((int)header->height[1] << 8) | ((int)header->height[0]);
+            *mips = 1;      // NOTE: ASTC format only contains one mipmap level
+
+            // NOTE: Each block is always stored in 128bit so we can calculate the bpp
+            int bpp = 128/(header->blockX*header->blockY);
+
+            // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
+            if ((bpp == 8) || (bpp == 2))
+            {
+                int data_size = (*width)*(*height)*bpp/8;  // Data size in bytes
+
+                image_data = RL_GPUTEX_MALLOC(data_size*sizeof(unsigned char));
+
+                RL_GPUTEX_MEMCPY(image_data, file_data_ptr, data_size);
+
+                if (bpp == 8) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
+                else if (bpp == 2) *format = RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
+            }
+            else RL_GPUTEX_LOG("ASTC block size configuration not supported");
+        }
+    }
+
+    return image_data;
+}
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Get pixel data size in bytes for certain pixel format
+static int get_pixel_data_size(int width, int height, int format)
+{
+    int data_size = 0;       // Size in bytes
+    int bpp = 0;            // Bits per pixel
+
+    switch (format)
+    {
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5:
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
+        default: break;
+    }
+
+    data_size = width*height*bpp/8;  // Total data size in bytes
+
+    // Most compressed formats works on 4x4 blocks,
+    // if texture is smaller, minimum dataSize is 8 or 16
+    if ((width < 4) && (height < 4))
+    {
+        if ((format >= RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8;
+        else if ((format >= RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16;
+    }
+
+    return data_size;
+}
+
+// Get OpenGL internal formats and data type from rlGpuTexPixelFormat
+void get_gl_texture_formats(int format, unsigned int *gl_internal_format, unsigned int *gl_format, unsigned int *gl_type)
+{
+    // KTX 1.1 uses OpenGL formats on header info but KTX 2.0 uses Vulkan texture formats,
+    // if this library is being improved to support KTX 2.0, it requires those formats to be provided -> View list at the end!
+
+    /*
+    *gl_internal_format = 0;
+    *gl_format = 0;
+    *gl_type = 0;
+
+    switch (format)
+    {
+    #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_21) || defined(GRAPHICS_API_OPENGL_ES2)
+        // NOTE: on OpenGL ES 2.0 (WebGL), internalFormat must match format and options allowed are: GL_LUMINANCE, GL_RGB, GL_RGBA
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_LUMINANCE_ALPHA; *gl_format = GL_LUMINANCE_ALPHA; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_BYTE; break;
+        #if !defined(GRAPHICS_API_OPENGL_11)
+        #if defined(GRAPHICS_API_OPENGL_ES3)
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F_EXT; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F_EXT; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F_EXT; *gl_format = GL_RED_EXT; *gl_type = GL_HALF_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F_EXT; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA16F_EXT; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT; break;
+        #else
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_FLOAT; break;            // NOTE: Requires extension OES_texture_float
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break;                  // NOTE: Requires extension OES_texture_float
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break;             // NOTE: Requires extension OES_texture_float
+        #if defined(GRAPHICS_API_OPENGL_21)
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_HALF_FLOAT_ARB; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT_ARB; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT_ARB; break;
+        #else // defined(GRAPHICS_API_OPENGL_ES2)
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_LUMINANCE; *gl_format = GL_LUMINANCE; *gl_type = GL_HALF_FLOAT_OES; break;   // NOTE: Requires extension OES_texture_half_float
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT_OES; break;         // NOTE: Requires extension OES_texture_half_float
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT_OES; break;    // NOTE: Requires extension OES_texture_half_float
+        #endif
+        #endif
+        #endif
+    #elif defined(GRAPHICS_API_OPENGL_33)
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *gl_internal_format = GL_R8; *gl_format = GL_RED; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *gl_internal_format = GL_RG8; *gl_format = GL_RG; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G6B5: *gl_internal_format = GL_RGB565; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_SHORT_5_6_5; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8: *gl_internal_format = GL_RGB8; *gl_format = GL_RGB; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: *gl_internal_format = GL_RGB5_A1; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_5_5_5_1; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *gl_internal_format = GL_RGBA4; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_SHORT_4_4_4_4; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *gl_internal_format = GL_RGBA8; *gl_format = GL_RGBA; *gl_type = GL_UNSIGNED_BYTE; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32: *gl_internal_format = GL_R32F; *gl_format = GL_RED; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32: *gl_internal_format = GL_RGB32F; *gl_format = GL_RGB; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: *gl_internal_format = GL_RGBA32F; *gl_format = GL_RGBA; *gl_type = GL_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16: *gl_internal_format = GL_R16F; *gl_format = GL_RED; *gl_type = GL_HALF_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16: *gl_internal_format = GL_RGB16F; *gl_format = GL_RGB; *gl_type = GL_HALF_FLOAT; break;
+        case RL_GPUTEX_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: *gl_internal_format = GL_RGBA16F; *gl_format = GL_RGBA; *gl_type = GL_HALF_FLOAT; break;
+    #endif
+    #if !defined(GRAPHICS_API_OPENGL_11)
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGB: *gl_internal_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT1_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT3_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_DXT5_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break;
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC1_RGB: *gl_internal_format = GL_ETC1_RGB8_OES; break;                      // NOTE: Requires OpenGL ES 2.0 or OpenGL 4.3
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_RGB: *gl_internal_format = GL_COMPRESSED_RGB8_ETC2; break;               // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA8_ETC2_EAC; break;     // NOTE: Requires OpenGL ES 3.0 or OpenGL 4.3
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGB: *gl_internal_format = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; break;    // NOTE: Requires PowerVR GPU
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_PVRT_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; break;  // NOTE: Requires PowerVR GPU
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_ASTC_4x4_KHR; break;  // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+        case RL_GPUTEX_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: *gl_internal_format = GL_COMPRESSED_RGBA_ASTC_8x8_KHR; break;  // NOTE: Requires OpenGL ES 3.1 or OpenGL 4.3
+    #endif
+        default: RL_GPUTEX_LOG("GPUTEX: Current format not supported (%i)", format); break;
+    }
+    */
+}
+#endif // RL_GPUTEX_IMPLEMENTATION
+
+/*
+// OpenGL texture data formats
+// NOTE: Those values can be useful for KTX 1.1 saving,
+// probably only the latest OpenGL ones, not the extensions ones
+// So, there is no need to include full OpenGL headers
+#define GL_UNSIGNED_BYTE                      0x1401
+#define GL_UNSIGNED_SHORT_5_6_5               0x8363
+#define GL_UNSIGNED_SHORT_4_4_4_4             0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1             0x8034
+#define GL_FLOAT                              0x1406
+#define GL_HALF_FLOAT                         0x140b
+#define GL_HALF_FLOAT_ARB                     0x140b
+#define GL_HALF_FLOAT_OES                     0x8d61
+
+#define GL_RED_EXT                            0x1903
+#define GL_RGB                                0x1907
+#define GL_RGBA                               0x1908
+#define GL_LUMINANCE                          0x1909
+#define GL_R8                                 0x8229
+#define GL_RG8                                0x822b
+#define GL_RGB565                             0x8d62
+#define GL_RGB8                               0x8051
+#define GL_RGB5_A1                            0x8057
+#define GL_RGBA4                              0x8056
+#define GL_RGBA8                              0x8058
+#define GL_R16F                               0x822d
+#define GL_RGB16F                             0x881b
+#define GL_RGBA16F                            0x881a
+#define GL_R32F                               0x822e
+#define GL_RGB32F                             0x8815
+#define GL_RGBA32F                            0x8814
+
+#define GL_R32F_EXT                           0x822e
+#define GL_RGB32F_EXT                         0x8815
+#define GL_RGBA32F_EXT                        0x8814
+#define GL_R16F_EXT                           0x822d
+#define GL_RGB16F_EXT                         0x881b
+#define GL_RGBA16F_EXT                        0x881a
+
+// S3TC (DXT) compression
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT       0x83f0
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT      0x83f1
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT      0x83f2
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT      0x83f3
+
+// ETC formats
+#define GL_ETC1_RGB8_OES                      0x8d64
+#define GL_COMPRESSED_RGB8_ETC2               0x9274
+#define GL_COMPRESSED_RGBA8_ETC2_EAC          0x9278
+
+// PVRTC
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG    0x8c00
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG   0x8c02
+
+// ASTC
+#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR       0x93b0
+#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR       0x93b7
+*/
+
+/*
+// Vulkan texture formats
+typedef enum VkFormat {
+    // Provided by VK_VERSION_1_0
+    VK_FORMAT_UNDEFINED = 0,
+    VK_FORMAT_R4G4_UNORM_PACK8 = 1,
+    VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2,
+    VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3,
+    VK_FORMAT_R5G6B5_UNORM_PACK16 = 4,
+    VK_FORMAT_B5G6R5_UNORM_PACK16 = 5,
+    VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6,
+    VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7,
+    VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8,
+    VK_FORMAT_R8_UNORM = 9,
+    VK_FORMAT_R8_SNORM = 10,
+    VK_FORMAT_R8_USCALED = 11,
+    VK_FORMAT_R8_SSCALED = 12,
+    VK_FORMAT_R8_UINT = 13,
+    VK_FORMAT_R8_SINT = 14,
+    VK_FORMAT_R8_SRGB = 15,
+    VK_FORMAT_R8G8_UNORM = 16,
+    VK_FORMAT_R8G8_SNORM = 17,
+    VK_FORMAT_R8G8_USCALED = 18,
+    VK_FORMAT_R8G8_SSCALED = 19,
+    VK_FORMAT_R8G8_UINT = 20,
+    VK_FORMAT_R8G8_SINT = 21,
+    VK_FORMAT_R8G8_SRGB = 22,
+    VK_FORMAT_R8G8B8_UNORM = 23,
+    VK_FORMAT_R8G8B8_SNORM = 24,
+    VK_FORMAT_R8G8B8_USCALED = 25,
+    VK_FORMAT_R8G8B8_SSCALED = 26,
+    VK_FORMAT_R8G8B8_UINT = 27,
+    VK_FORMAT_R8G8B8_SINT = 28,
+    VK_FORMAT_R8G8B8_SRGB = 29,
+    VK_FORMAT_B8G8R8_UNORM = 30,
+    VK_FORMAT_B8G8R8_SNORM = 31,
+    VK_FORMAT_B8G8R8_USCALED = 32,
+    VK_FORMAT_B8G8R8_SSCALED = 33,
+    VK_FORMAT_B8G8R8_UINT = 34,
+    VK_FORMAT_B8G8R8_SINT = 35,
+    VK_FORMAT_B8G8R8_SRGB = 36,
+    VK_FORMAT_R8G8B8A8_UNORM = 37,
+    VK_FORMAT_R8G8B8A8_SNORM = 38,
+    VK_FORMAT_R8G8B8A8_USCALED = 39,
+    VK_FORMAT_R8G8B8A8_SSCALED = 40,
+    VK_FORMAT_R8G8B8A8_UINT = 41,
+    VK_FORMAT_R8G8B8A8_SINT = 42,
+    VK_FORMAT_R8G8B8A8_SRGB = 43,
+    VK_FORMAT_B8G8R8A8_UNORM = 44,
+    VK_FORMAT_B8G8R8A8_SNORM = 45,
+    VK_FORMAT_B8G8R8A8_USCALED = 46,
+    VK_FORMAT_B8G8R8A8_SSCALED = 47,
+    VK_FORMAT_B8G8R8A8_UINT = 48,
+    VK_FORMAT_B8G8R8A8_SINT = 49,
+    VK_FORMAT_B8G8R8A8_SRGB = 50,
+    VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51,
+    VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52,
+    VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53,
+    VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54,
+    VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55,
+    VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56,
+    VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57,
+    VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58,
+    VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59,
+    VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60,
+    VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61,
+    VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62,
+    VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63,
+    VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64,
+    VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65,
+    VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66,
+    VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67,
+    VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68,
+    VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69,
+    VK_FORMAT_R16_UNORM = 70,
+    VK_FORMAT_R16_SNORM = 71,
+    VK_FORMAT_R16_USCALED = 72,
+    VK_FORMAT_R16_SSCALED = 73,
+    VK_FORMAT_R16_UINT = 74,
+    VK_FORMAT_R16_SINT = 75,
+    VK_FORMAT_R16_SFLOAT = 76,
+    VK_FORMAT_R16G16_UNORM = 77,
+    VK_FORMAT_R16G16_SNORM = 78,
+    VK_FORMAT_R16G16_USCALED = 79,
+    VK_FORMAT_R16G16_SSCALED = 80,
+    VK_FORMAT_R16G16_UINT = 81,
+    VK_FORMAT_R16G16_SINT = 82,
+    VK_FORMAT_R16G16_SFLOAT = 83,
+    VK_FORMAT_R16G16B16_UNORM = 84,
+    VK_FORMAT_R16G16B16_SNORM = 85,
+    VK_FORMAT_R16G16B16_USCALED = 86,
+    VK_FORMAT_R16G16B16_SSCALED = 87,
+    VK_FORMAT_R16G16B16_UINT = 88,
+    VK_FORMAT_R16G16B16_SINT = 89,
+    VK_FORMAT_R16G16B16_SFLOAT = 90,
+    VK_FORMAT_R16G16B16A16_UNORM = 91,
+    VK_FORMAT_R16G16B16A16_SNORM = 92,
+    VK_FORMAT_R16G16B16A16_USCALED = 93,
+    VK_FORMAT_R16G16B16A16_SSCALED = 94,
+    VK_FORMAT_R16G16B16A16_UINT = 95,
+    VK_FORMAT_R16G16B16A16_SINT = 96,
+    VK_FORMAT_R16G16B16A16_SFLOAT = 97,
+    VK_FORMAT_R32_UINT = 98,
+    VK_FORMAT_R32_SINT = 99,
+    VK_FORMAT_R32_SFLOAT = 100,
+    VK_FORMAT_R32G32_UINT = 101,
+    VK_FORMAT_R32G32_SINT = 102,
+    VK_FORMAT_R32G32_SFLOAT = 103,
+    VK_FORMAT_R32G32B32_UINT = 104,
+    VK_FORMAT_R32G32B32_SINT = 105,
+    VK_FORMAT_R32G32B32_SFLOAT = 106,
+    VK_FORMAT_R32G32B32A32_UINT = 107,
+    VK_FORMAT_R32G32B32A32_SINT = 108,
+    VK_FORMAT_R32G32B32A32_SFLOAT = 109,
+    VK_FORMAT_R64_UINT = 110,
+    VK_FORMAT_R64_SINT = 111,
+    VK_FORMAT_R64_SFLOAT = 112,
+    VK_FORMAT_R64G64_UINT = 113,
+    VK_FORMAT_R64G64_SINT = 114,
+    VK_FORMAT_R64G64_SFLOAT = 115,
+    VK_FORMAT_R64G64B64_UINT = 116,
+    VK_FORMAT_R64G64B64_SINT = 117,
+    VK_FORMAT_R64G64B64_SFLOAT = 118,
+    VK_FORMAT_R64G64B64A64_UINT = 119,
+    VK_FORMAT_R64G64B64A64_SINT = 120,
+    VK_FORMAT_R64G64B64A64_SFLOAT = 121,
+    VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122,
+    VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123,
+    VK_FORMAT_D16_UNORM = 124,
+    VK_FORMAT_X8_D24_UNORM_PACK32 = 125,
+    VK_FORMAT_D32_SFLOAT = 126,
+    VK_FORMAT_S8_UINT = 127,
+    VK_FORMAT_D16_UNORM_S8_UINT = 128,
+    VK_FORMAT_D24_UNORM_S8_UINT = 129,
+    VK_FORMAT_D32_SFLOAT_S8_UINT = 130,
+    VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131,
+    VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132,
+    VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133,
+    VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134,
+    VK_FORMAT_BC2_UNORM_BLOCK = 135,
+    VK_FORMAT_BC2_SRGB_BLOCK = 136,
+    VK_FORMAT_BC3_UNORM_BLOCK = 137,
+    VK_FORMAT_BC3_SRGB_BLOCK = 138,
+    VK_FORMAT_BC4_UNORM_BLOCK = 139,
+    VK_FORMAT_BC4_SNORM_BLOCK = 140,
+    VK_FORMAT_BC5_UNORM_BLOCK = 141,
+    VK_FORMAT_BC5_SNORM_BLOCK = 142,
+    VK_FORMAT_BC6H_UFLOAT_BLOCK = 143,
+    VK_FORMAT_BC6H_SFLOAT_BLOCK = 144,
+    VK_FORMAT_BC7_UNORM_BLOCK = 145,
+    VK_FORMAT_BC7_SRGB_BLOCK = 146,
+    VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147,
+    VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148,
+    VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149,
+    VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150,
+    VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151,
+    VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152,
+    VK_FORMAT_EAC_R11_UNORM_BLOCK = 153,
+    VK_FORMAT_EAC_R11_SNORM_BLOCK = 154,
+    VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155,
+    VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156,
+    VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157,
+    VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158,
+    VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159,
+    VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160,
+    VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161,
+    VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162,
+    VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163,
+    VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164,
+    VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165,
+    VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166,
+    VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167,
+    VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168,
+    VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169,
+    VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170,
+    VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171,
+    VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172,
+    VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173,
+    VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174,
+    VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175,
+    VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176,
+    VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177,
+    VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178,
+    VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179,
+    VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180,
+    VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181,
+    VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
+    VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
+    VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032,
+    // Provided by VK_VERSION_1_1
+    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_G8_B8R8_2PLANE_444_UNORM = 1000330000,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16 = 1000330001,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16 = 1000330002,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_G16_B16R16_2PLANE_444_UNORM = 1000330003,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_A4R4G4B4_UNORM_PACK16 = 1000340000,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_A4B4G4R4_UNORM_PACK16 = 1000340001,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK = 1000066002,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK = 1000066003,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK = 1000066004,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK = 1000066007,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK = 1000066008,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK = 1000066009,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK = 1000066010,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK = 1000066011,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK = 1000066012,
+    // Provided by VK_VERSION_1_3
+    VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK = 1000066013,
+    // Provided by VK_VERSION_1_4
+    VK_FORMAT_A1B5G5R5_UNORM_PACK16 = 1000470000,
+    // Provided by VK_VERSION_1_4
+    VK_FORMAT_A8_UNORM = 1000470001,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+    // Provided by VK_IMG_format_pvrtc
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+    // Provided by VK_ARM_tensors
+    VK_FORMAT_R8_BOOL_ARM = 1000460000,
+    // Provided by VK_NV_optical_flow
+    VK_FORMAT_R16G16_SFIXED5_NV = 1000464000,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R10X6_UINT_PACK16_ARM = 1000609000,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R10X6G10X6_UINT_2PACK16_ARM = 1000609001,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UINT_4PACK16_ARM = 1000609002,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R12X4_UINT_PACK16_ARM = 1000609003,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R12X4G12X4_UINT_2PACK16_ARM = 1000609004,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UINT_4PACK16_ARM = 1000609005,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2_UINT_PACK16_ARM = 1000609006,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2G14X2_UINT_2PACK16_ARM = 1000609007,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2G14X2B14X2A14X2_UINT_4PACK16_ARM = 1000609008,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2_UNORM_PACK16_ARM = 1000609009,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2G14X2_UNORM_2PACK16_ARM = 1000609010,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_R14X2G14X2B14X2A14X2_UNORM_4PACK16_ARM = 1000609011,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_G14X2_B14X2R14X2_2PLANE_420_UNORM_3PACK16_ARM = 1000609012,
+    // Provided by VK_ARM_format_pack
+    VK_FORMAT_G14X2_B14X2R14X2_2PLANE_422_UNORM_3PACK16_ARM = 1000609013,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK,
+    // Provided by VK_EXT_texture_compression_astc_hdr
+    VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8B8G8R8_422_UNORM_KHR = VK_FORMAT_G8B8G8R8_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_B8G8R8G8_422_UNORM_KHR = VK_FORMAT_B8G8R8G8_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R10X6_UNORM_PACK16_KHR = VK_FORMAT_R10X6_UNORM_PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = VK_FORMAT_R10X6G10X6_UNORM_2PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R12X4_UNORM_PACK16_KHR = VK_FORMAT_R12X4_UNORM_PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = VK_FORMAT_R12X4G12X4_UNORM_2PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16B16G16R16_422_UNORM_KHR = VK_FORMAT_G16B16G16R16_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_B16G16R16G16_422_UNORM_KHR = VK_FORMAT_B16G16R16G16_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
+    // Provided by VK_KHR_sampler_ycbcr_conversion
+    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
+    // Provided by VK_EXT_ycbcr_2plane_444_formats
+    VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT = VK_FORMAT_G8_B8R8_2PLANE_444_UNORM,
+    // Provided by VK_EXT_ycbcr_2plane_444_formats
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16,
+    // Provided by VK_EXT_ycbcr_2plane_444_formats
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16,
+    // Provided by VK_EXT_ycbcr_2plane_444_formats
+    VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT = VK_FORMAT_G16_B16R16_2PLANE_444_UNORM,
+    // Provided by VK_EXT_4444_formats
+    VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = VK_FORMAT_A4R4G4B4_UNORM_PACK16,
+    // Provided by VK_EXT_4444_formats
+    VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = VK_FORMAT_A4B4G4R4_UNORM_PACK16,
+    // Provided by VK_NV_optical_flow
+    // VK_FORMAT_R16G16_S10_5_NV is a deprecated alias
+    VK_FORMAT_R16G16_S10_5_NV = VK_FORMAT_R16G16_SFIXED5_NV,
+    // Provided by VK_KHR_maintenance5
+    VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = VK_FORMAT_A1B5G5R5_UNORM_PACK16,
+    // Provided by VK_KHR_maintenance5
+    VK_FORMAT_A8_UNORM_KHR = VK_FORMAT_A8_UNORM,
+} VkFormat;
+*/
diff --git a/raylib/src/external/rlsw.h b/raylib/src/external/rlsw.h
new file mode 100644
--- /dev/null
+++ b/raylib/src/external/rlsw.h
@@ -0,0 +1,4801 @@
+/**********************************************************************************************
+*
+*   rlsw v1.0 - An OpenGL 1.1-style software renderer implementation
+*
+*   DESCRIPTION:
+*       rlsw is a custom OpenGL 1.1-style implementation on software, intended to provide all
+*       functionality available on rlgl.h library used by raylib, becoming a direct software
+*       rendering replacement for OpenGL 1.1 backend and allowing to run raylib on GPU-less
+*       devices when required
+*
+*   FEATURES:
+*       - Rendering to custom internal framebuffer with multiple color modes supported:
+*           - Color buffer: RGB - 8-bit (3:3:2) | RGB - 16-bit (5:6:5) | RGB - 24-bit (8:8:8)
+*           - Depth buffer: D - 8-bit (unorm) | D - 16-bit (unorm) | D - 24-bit (unorm)
+*       - Rendering modes supported: POINT, LINES, TRIANGLE, QUADS
+*           - Additional features: Polygon modes, Point width, Line width
+*       - Clipping support for all rendering modes
+*       - Texture features supported:
+*           - All uncompressed texture formats supported by raylib
+*           - Texture Minification/Magnification checks
+*           - Point and Bilinear filtering
+*           - Texture Wrap Modes with separate checks for S/T coordinates
+*       - Vertex Arrays support with direct primitive drawing mode
+*       - Matrix Stack support (Matrix Push/Pop)
+*       - Other GL misc features:
+*           - GL-style getter functions
+*           - Framebuffer resizing
+*           - Perspective correction
+*           - Scissor clipping
+*           - Depth testing
+*           - Blend modes
+*           - Face culling
+*
+*   ADDITIONAL NOTES:
+*       Check PR for more info: https://github.com/raysan5/raylib/pull/4832
+*
+*   CONFIGURATION:
+*       #define RLSW_IMPLEMENTATION
+*           Generates the implementation of the library into the included file
+*           If not defined, the library is in header only mode and can be included in other headers
+*           or source files without problems. But only ONE file should hold the implementation
+*
+*       #define RLSW_USE_SIMD_INTRINSICS
+*           Detect and use SIMD intrinsics on the host compilation platform
+*           SIMD could improve rendering considerable vectorizing some raster operations
+*           but the target platforms running the compiled program with SIMD enabled
+*           must support the SIMD the program has been built for, making them only
+*           recommended under specific situations and only if the developers know
+*           what are they doing; this flag is not defined by default
+*
+*       rlsw capabilities could be customized just defining some internal
+*       values before library inclusion (default values listed):
+*
+*           #define SW_GL_FRAMEBUFFER_COPY_BGRA     true
+*           #define SW_GL_BINDING_COPY_TEXTURE      true
+*           #define SW_COLOR_BUFFER_BITS            24
+*           #define SW_DEPTH_BUFFER_BITS            16
+*           #define SW_MAX_PROJECTION_STACK_SIZE    2
+*           #define SW_MAX_MODELVIEW_STACK_SIZE     8
+*           #define SW_MAX_TEXTURE_STACK_SIZE       2
+*           #define SW_MAX_TEXTURES                 128
+*
+*
+*   LICENSE: MIT
+*
+*   Copyright (c) 2025-2026 Le Juez Victor (@Bigfoot71), reviewed by Ramon Santamaria (@raysan5)
+*
+*   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 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.
+*
+**********************************************************************************************/
+
+#ifndef RLSW_H
+#define RLSW_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+// Function specifiers definition
+#ifndef SWAPI
+    #define SWAPI       // Functions defined as 'extern' by default (implicit specifiers)
+#endif
+
+#ifndef SW_MALLOC
+    #define SW_MALLOC(sz) malloc(sz)
+#endif
+#ifndef SW_REALLOC
+    #define SW_REALLOC(ptr, newSz) realloc(ptr, newSz)
+#endif
+#ifndef SW_FREE
+    #define SW_FREE(ptr) free(ptr)
+#endif
+
+#ifndef SW_RESTRICT
+    #ifdef _MSC_VER
+        #define SW_RESTRICT __restrict
+    #else
+        #define SW_RESTRICT restrict
+    #endif
+#endif
+
+#ifndef SW_GL_FRAMEBUFFER_COPY_BGRA
+    #define SW_GL_FRAMEBUFFER_COPY_BGRA     true
+#endif
+
+#ifndef SW_COLOR_BUFFER_BITS
+    #define SW_COLOR_BUFFER_BITS            32  //< 32 (rgba), 16 (rgb packed) or 8 (rgb packed)
+#endif
+
+#ifndef SW_DEPTH_BUFFER_BITS
+    #define SW_DEPTH_BUFFER_BITS            16  //< 32, 24 or 16
+#endif
+
+#ifndef SW_MAX_PROJECTION_STACK_SIZE
+    #define SW_MAX_PROJECTION_STACK_SIZE    2
+#endif
+
+#ifndef SW_MAX_MODELVIEW_STACK_SIZE
+    #define SW_MAX_MODELVIEW_STACK_SIZE     8
+#endif
+
+#ifndef SW_MAX_TEXTURE_STACK_SIZE
+    #define SW_MAX_TEXTURE_STACK_SIZE       2
+#endif
+
+#ifndef SW_MAX_TEXTURES
+    #define SW_MAX_TEXTURES                 128
+#endif
+
+// Under normal circumstances, clipping a polygon can add at most one vertex per clipping plane
+// Considering the largest polygon involved is a quadrilateral (4 vertices),
+// and that clipping occurs against both the frustum (6 planes) and the scissors (4 planes),
+// the maximum number of vertices after clipping is:
+// 4 (original vertices) + 6 (frustum planes) + 4 (scissors planes) = 14
+#ifndef SW_MAX_CLIPPED_POLYGON_VERTICES
+    #define SW_MAX_CLIPPED_POLYGON_VERTICES 14
+#endif
+
+#ifndef SW_CLIP_EPSILON
+    #define SW_CLIP_EPSILON                 1e-4f
+#endif
+
+//----------------------------------------------------------------------------------
+// OpenGL Compatibility Types
+//----------------------------------------------------------------------------------
+typedef unsigned int        GLenum;
+typedef unsigned char       GLboolean;
+typedef unsigned int        GLbitfield;
+typedef void                GLvoid;
+typedef signed char         GLbyte;
+typedef short               GLshort;
+typedef int                 GLint;
+typedef unsigned char       GLubyte;
+typedef unsigned short      GLushort;
+typedef unsigned int        GLuint;
+typedef int                 GLsizei;
+typedef float               GLfloat;
+typedef float               GLclampf;
+typedef double              GLdouble;
+typedef double              GLclampd;
+
+//----------------------------------------------------------------------------------
+// OpenGL Definitions
+// NOTE: Not used/supported definitions are commented
+//----------------------------------------------------------------------------------
+#define GL_FALSE                            0
+#define GL_TRUE                             1
+
+#define GL_SCISSOR_TEST                     0x0C11
+#define GL_TEXTURE_2D                       0x0DE1
+#define GL_DEPTH_TEST                       0x0B71
+#define GL_CULL_FACE                        0x0B44
+#define GL_BLEND                            0x0BE2
+
+#define GL_VENDOR                           0x1F00
+#define GL_RENDERER                         0x1F01
+#define GL_VERSION                          0x1F02
+#define GL_EXTENSIONS                       0x1F03
+
+//#define GL_ATTRIB_STACK_DEPTH             0x0BB0
+//#define GL_CLIENT_ATTRIB_STACK_DEPTH      0x0BB1
+#define GL_COLOR_CLEAR_VALUE                0x0C22
+#define GL_DEPTH_CLEAR_VALUE                0x0B73
+//#define GL_COLOR_WRITEMASK                0x0C23
+//#define GL_CURRENT_INDEX                  0x0B01
+#define GL_CURRENT_COLOR                    0x0B00
+//#define GL_CURRENT_NORMAL                 0x0B02
+//#define GL_CURRENT_RASTER_COLOR           0x0B04
+//#define GL_CURRENT_RASTER_DISTANCE        0x0B09
+//#define GL_CURRENT_RASTER_INDEX           0x0B05
+//#define GL_CURRENT_RASTER_POSITION        0x0B07
+//#define GL_CURRENT_RASTER_TEXTURE_COORDS  0x0B06
+//#define GL_CURRENT_RASTER_POSITION_VALID  0x0B08
+#define GL_CURRENT_TEXTURE_COORDS           0x0B03
+#define GL_POINT_SIZE                       0x0B11
+#define GL_LINE_WIDTH                       0x0B21
+//#define GL_INDEX_CLEAR_VALUE              0x0C20
+//#define GL_INDEX_MODE                     0x0C30
+//#define GL_INDEX_WRITEMASK                0x0C21
+#define GL_MODELVIEW_MATRIX                 0x0BA6
+#define GL_MODELVIEW_STACK_DEPTH            0x0BA3
+//#define GL_NAME_STACK_DEPTH               0x0D70
+#define GL_PROJECTION_MATRIX                0x0BA7
+#define GL_PROJECTION_STACK_DEPTH           0x0BA4
+//#define GL_RENDER_MODE                    0x0C40
+//#define GL_RGBA_MODE                      0x0C31
+#define GL_TEXTURE_MATRIX                   0x0BA8
+#define GL_TEXTURE_STACK_DEPTH              0x0BA5
+#define GL_VIEWPORT                         0x0BA2
+
+#define GL_COLOR_BUFFER_BIT                 0x00004000
+#define GL_DEPTH_BUFFER_BIT                 0x00000100
+
+#define GL_MODELVIEW                        0x1700
+#define GL_PROJECTION                       0x1701
+#define GL_TEXTURE                          0x1702
+
+#define GL_VERTEX_ARRAY                     0x8074
+#define GL_NORMAL_ARRAY                     0x8075      // WARNING: Not implemented (defined for RLGL)
+#define GL_COLOR_ARRAY                      0x8076
+//#define GL_INDEX_ARRAY                    0x8077
+#define GL_TEXTURE_COORD_ARRAY              0x8078
+
+#define GL_POINTS                           0x0000
+#define GL_LINES                            0x0001
+//#define GL_LINE_LOOP                      0x0002
+//#define GL_LINE_STRIP                     0x0003
+#define GL_TRIANGLES                        0x0004
+//#define GL_TRIANGLE_STRIP                 0x0005
+//#define GL_TRIANGLE_FAN                   0x0006
+#define GL_QUADS                            0x0007
+//#define GL_QUAD_STRIP                     0x0008
+//#define GL_POLYGON                        0x0009
+
+#define GL_POINT                            0x1B00
+#define GL_LINE                             0x1B01
+#define GL_FILL                             0x1B02
+
+#define GL_FRONT                            0x0404
+#define GL_BACK                             0x0405
+
+#define GL_ZERO                             0
+#define GL_ONE                              1
+#define GL_SRC_COLOR                        0x0300
+#define GL_ONE_MINUS_SRC_COLOR              0x0301
+#define GL_SRC_ALPHA                        0x0302
+#define GL_ONE_MINUS_SRC_ALPHA              0x0303
+#define GL_DST_ALPHA                        0x0304
+#define GL_ONE_MINUS_DST_ALPHA              0x0305
+#define GL_DST_COLOR                        0x0306
+#define GL_ONE_MINUS_DST_COLOR              0x0307
+#define GL_SRC_ALPHA_SATURATE               0x0308
+
+#define GL_NEAREST                          0x2600
+#define GL_LINEAR                           0x2601
+
+#define GL_REPEAT                           0x2901
+#define GL_CLAMP                            0x2900
+
+#define GL_TEXTURE_MAG_FILTER               0x2800
+#define GL_TEXTURE_MIN_FILTER               0x2801
+
+#define GL_TEXTURE_WRAP_S                   0x2802
+#define GL_TEXTURE_WRAP_T                   0x2803
+
+#define GL_NO_ERROR                         0
+#define GL_INVALID_ENUM                     0x0500
+#define GL_INVALID_VALUE                    0x0501
+#define GL_INVALID_OPERATION                0x0502
+#define GL_STACK_OVERFLOW                   0x0503
+#define GL_STACK_UNDERFLOW                  0x0504
+#define GL_OUT_OF_MEMORY                    0x0505
+
+#define GL_ALPHA                            0x1906
+#define GL_LUMINANCE                        0x1909
+#define GL_LUMINANCE_ALPHA                  0x190A
+#define GL_RGB                              0x1907
+#define GL_RGBA                             0x1908
+
+#define GL_BYTE                             0x1400
+#define GL_UNSIGNED_BYTE                    0x1401
+#define GL_SHORT                            0x1402
+#define GL_UNSIGNED_SHORT                   0x1403
+#define GL_INT                              0x1404
+#define GL_UNSIGNED_INT                     0x1405
+#define GL_FLOAT                            0x1406
+
+// OpenGL Definitions NOT USED
+#define GL_PERSPECTIVE_CORRECTION_HINT      0x0C50
+#define GL_PACK_ALIGNMENT                   0x0D05
+#define GL_UNPACK_ALIGNMENT                 0x0CF5
+#define GL_LINE_SMOOTH                      0x0B20
+#define GL_SMOOTH                           0x1D01
+#define GL_NICEST                           0x1102
+#define GL_CCW                              0x0901
+#define GL_CW                               0x0900
+#define GL_NEVER                            0x0200
+#define GL_LESS                             0x0201
+#define GL_EQUAL                            0x0202
+#define GL_LEQUAL                           0x0203
+#define GL_GREATER                          0x0204
+#define GL_NOTEQUAL                         0x0205
+#define GL_GEQUAL                           0x0206
+#define GL_ALWAYS                           0x0207
+
+//----------------------------------------------------------------------------------
+// OpenGL Bindings to rlsw
+//----------------------------------------------------------------------------------
+#define glReadPixels(x, y, w, h, f, t, p)           swCopyFramebuffer((x), (y), (w), (h), (f), (t), (p))
+#define glEnable(state)                             swEnable((state))
+#define glDisable(state)                            swDisable((state))
+#define glGetFloatv(pname, params)                  swGetFloatv((pname), (params))
+#define glGetString(pname)                          swGetString((pname))
+#define glGetError()                                swGetError()
+#define glViewport(x, y, w, h)                      swViewport((x), (y), (w), (h))
+#define glScissor(x, y, w, h)                       swScissor((x), (y), (w), (h))
+#define glClearColor(r, g, b, a)                    swClearColor((r), (g), (b), (a))
+#define glClearDepth(d)                             swClearDepth((d))
+#define glClear(bitmask)                            swClear((bitmask))
+#define glBlendFunc(sfactor, dfactor)               swBlendFunc((sfactor), (dfactor))
+#define glPolygonMode(face, mode)                   swPolygonMode((mode))
+#define glCullFace(face)                            swCullFace((face))
+#define glPointSize(size)                           swPointSize((size))
+#define glLineWidth(width)                          swLineWidth((width))
+#define glMatrixMode(mode)                          swMatrixMode((mode))
+#define glPushMatrix()                              swPushMatrix()
+#define glPopMatrix()                               swPopMatrix()
+#define glLoadIdentity()                            swLoadIdentity()
+#define glTranslatef(x, y, z)                       swTranslatef((x), (y), (z))
+#define glRotatef(a, x, y, z)                       swRotatef((a), (x), (y), (z))
+#define glScalef(x, y, z)                           swScalef((x), (y), (z))
+#define glMultMatrixf(v)                            swMultMatrixf((v))
+#define glFrustum(l, r, b, t, n, f)                 swFrustum((l), (r), (b), (t), (n), (f))
+#define glOrtho(l, r, b, t, n, f)                   swOrtho((l), (r), (b), (t), (n), (f))
+#define glBegin(mode)                               swBegin((mode))
+#define glEnd()                                     swEnd()
+#define glVertex2i(x, y)                            swVertex2i((x), (y))
+#define glVertex2f(x, y)                            swVertex2f((x), (y))
+#define glVertex2fv(v)                              swVertex2fv((v))
+#define glVertex3i(x, y, z)                         swVertex3i((x), (y), (z))
+#define glVertex3f(x, y, z)                         swVertex3f((x), (y), (z))
+#define glvertex3fv(v)                              swVertex3fv((v))
+#define glVertex4i(x, y, z, w)                      swVertex4i((x), (y), (z), (w))
+#define glVertex4f(x, y, z, w)                      swVertex4f((x), (y), (z), (w))
+#define glVertex4fv(v)                              swVertex4fv((v))
+#define glColor3ub(r, g, b)                         swColor3ub((r), (g), (b))
+#define glColor3ubv(v)                              swColor3ubv((v))
+#define glColor3f(r, g, b)                          swColor3f((r), (g), (b))
+#define glColor3fv(v)                               swColor3fv((v))
+#define glColor4ub(r, g, b, a)                      swColor4ub((r), (g), (b), (a))
+#define glColor4ubv(v)                              swColor4ubv((v))
+#define glColor4f(r, g, b, a)                       swColor4f((r), (g), (b), (a))
+#define glColor4fv(v)                               swColor4fv((v))
+#define glTexCoord2f(u, v)                          swTexCoord2f((u), (v))
+#define glTexCoord2fv(v)                            swTexCoord2fv((v))
+
+#define glEnableClientState(t)                      ((void)(t))
+#define glDisableClientState(t)                     swBindArray((t), 0)
+#define glVertexPointer(sz, t, s, p)                swBindArray(SW_VERTEX_ARRAY, (p))
+#define glTexCoordPointer(sz, t, s, p)              swBindArray(SW_TEXTURE_COORD_ARRAY, (p))
+#define glColorPointer(sz, t, s, p)                 swBindArray(SW_COLOR_ARRAY, (p))
+#define glDrawArrays(m, o, c)                       swDrawArrays((m), (o), (c))
+#define glDrawElements(m,c,t,i)                     swDrawElements((m),(c),(t),(i))
+#define glGenTextures(c, v)                         swGenTextures((c), (v))
+#define glDeleteTextures(c, v)                      swDeleteTextures((c), (v))
+#define glTexImage2D(tr, l, if, w, h, b, f, t, p)   swTexImage2D((w), (h), (f), (t), (p))
+#define glTexParameteri(tr, pname, param)           swTexParameteri((pname), (param))
+#define glBindTexture(tr, id)                       swBindTexture((id))
+
+// OpenGL functions NOT IMPLEMENTED by rlsw
+#define glDepthMask(X)                          ((void)(X))
+#define glColorMask(X,Y,Z,W)                    ((void)(X),(void)(Y),(void)(Z),(void)(W))
+#define glPixelStorei(X,Y)                      ((void)(X),(void)(Y))
+#define glHint(X,Y)                             ((void)(X),(void)(Y))
+#define glShadeModel(X)                         ((void)(X))
+#define glFrontFace(X)                          ((void)(X))
+#define glDepthFunc(X)                          ((void)(X))
+#define glTexSubImage2D(X,Y,Z,W,A,B,C,D,E)      ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A),(void)(B),(void)(C),(void)(D),(void)(E))
+#define glGetTexImage(X,Y,Z,W,A)                ((void)(X),(void)(Y),(void)(Z),(void)(W),(void)(A))
+#define glNormal3f(X,Y,Z)                       ((void)(X),(void)(Y),(void)(Z))
+#define glNormal3fv(X)                          ((void)(X))
+#define glNormalPointer(X,Y,Z)                  ((void)(X),(void)(Y),(void)(Z))
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef enum {
+    SW_SCISSOR_TEST = GL_SCISSOR_TEST,
+    SW_TEXTURE_2D = GL_TEXTURE_2D,
+    SW_DEPTH_TEST = GL_DEPTH_TEST,
+    SW_CULL_FACE = GL_CULL_FACE,
+    SW_BLEND = GL_BLEND
+} SWstate;
+
+typedef enum {
+    SW_VENDOR = GL_VENDOR,
+    SW_RENDERER = GL_RENDERER,
+    SW_VERSION = GL_VERSION,
+    SW_EXTENSIONS = GL_EXTENSIONS,
+    SW_COLOR_CLEAR_VALUE = GL_COLOR_CLEAR_VALUE,
+    SW_DEPTH_CLEAR_VALUE = GL_DEPTH_CLEAR_VALUE,
+    SW_CURRENT_COLOR = GL_CURRENT_COLOR,
+    SW_CURRENT_TEXTURE_COORDS = GL_CURRENT_TEXTURE_COORDS,
+    SW_POINT_SIZE = GL_POINT_SIZE,
+    SW_LINE_WIDTH = GL_LINE_WIDTH,
+    SW_MODELVIEW_MATRIX = GL_MODELVIEW_MATRIX,
+    SW_MODELVIEW_STACK_DEPTH = GL_MODELVIEW_STACK_DEPTH,
+    SW_PROJECTION_MATRIX = GL_PROJECTION_MATRIX,
+    SW_PROJECTION_STACK_DEPTH = GL_PROJECTION_STACK_DEPTH,
+    SW_TEXTURE_MATRIX = GL_TEXTURE_MATRIX,
+    SW_TEXTURE_STACK_DEPTH = GL_TEXTURE_STACK_DEPTH,
+    SW_VIEWPORT = GL_VIEWPORT
+} SWget;
+
+typedef enum {
+    SW_COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT,
+    SW_DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT
+} SWbuffer;
+
+typedef enum {
+    SW_PROJECTION = GL_PROJECTION,
+    SW_MODELVIEW = GL_MODELVIEW,
+    SW_TEXTURE = GL_TEXTURE
+} SWmatrix;
+
+typedef enum {
+    SW_VERTEX_ARRAY = GL_VERTEX_ARRAY,
+    SW_TEXTURE_COORD_ARRAY = GL_TEXTURE_COORD_ARRAY,
+    SW_COLOR_ARRAY = GL_COLOR_ARRAY
+} SWarray;
+
+typedef enum {
+    SW_POINTS = GL_POINTS,
+    SW_LINES = GL_LINES,
+    SW_TRIANGLES = GL_TRIANGLES,
+    SW_QUADS = GL_QUADS
+} SWdraw;
+
+typedef enum {
+    SW_POINT = GL_POINT,
+    SW_LINE = GL_LINE,
+    SW_FILL = GL_FILL
+} SWpoly;
+
+typedef enum {
+    SW_FRONT = GL_FRONT,
+    SW_BACK = GL_BACK,
+} SWface;
+
+typedef enum {
+    SW_ZERO = GL_ZERO,
+    SW_ONE = GL_ONE,
+    SW_SRC_COLOR = GL_SRC_COLOR,
+    SW_ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR,
+    SW_SRC_ALPHA = GL_SRC_ALPHA,
+    SW_ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA,
+    SW_DST_ALPHA = GL_DST_ALPHA,
+    SW_ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA,
+    SW_DST_COLOR = GL_DST_COLOR,
+    SW_ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR,
+    SW_SRC_ALPHA_SATURATE = GL_SRC_ALPHA_SATURATE
+} SWfactor;
+
+typedef enum {
+    SW_LUMINANCE = GL_LUMINANCE,
+    SW_LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA,
+    SW_RGB = GL_RGB,
+    SW_RGBA = GL_RGBA,
+} SWformat;
+
+typedef enum {
+    SW_UNSIGNED_BYTE = GL_UNSIGNED_BYTE,
+    SW_BYTE = GL_BYTE,
+    SW_UNSIGNED_SHORT = GL_UNSIGNED_SHORT,
+    SW_SHORT = GL_SHORT,
+    SW_UNSIGNED_INT = GL_UNSIGNED_INT,
+    SW_INT = GL_INT,
+    SW_FLOAT = GL_FLOAT
+} SWtype;
+
+typedef enum {
+    SW_NEAREST = GL_NEAREST,
+    SW_LINEAR = GL_LINEAR
+} SWfilter;
+
+typedef enum {
+    SW_REPEAT = GL_REPEAT,
+    SW_CLAMP = GL_CLAMP,
+} SWwrap;
+
+typedef enum {
+    SW_TEXTURE_MIN_FILTER = GL_TEXTURE_MIN_FILTER,
+    SW_TEXTURE_MAG_FILTER = GL_TEXTURE_MAG_FILTER,
+    SW_TEXTURE_WRAP_S = GL_TEXTURE_WRAP_S,
+    SW_TEXTURE_WRAP_T = GL_TEXTURE_WRAP_T
+} SWtexparam;
+
+typedef enum {
+    SW_NO_ERROR = GL_NO_ERROR,
+    SW_INVALID_ENUM = GL_INVALID_ENUM,
+    SW_INVALID_VALUE = GL_INVALID_VALUE,
+    SW_STACK_OVERFLOW = GL_STACK_OVERFLOW,
+    SW_STACK_UNDERFLOW = GL_STACK_UNDERFLOW,
+    SW_INVALID_OPERATION = GL_INVALID_OPERATION,
+} SWerrcode;
+
+//------------------------------------------------------------------------------------
+// Functions Declaration - Public API
+//------------------------------------------------------------------------------------
+SWAPI bool swInit(int w, int h);
+SWAPI void swClose(void);
+
+SWAPI bool swResizeFramebuffer(int w, int h);
+SWAPI void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels);
+SWAPI void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels);
+
+SWAPI void swEnable(SWstate state);
+SWAPI void swDisable(SWstate state);
+
+SWAPI void swGetFloatv(SWget name, float *v);
+SWAPI const char *swGetString(SWget name);
+SWAPI SWerrcode swGetError(void);
+
+SWAPI void swViewport(int x, int y, int width, int height);
+SWAPI void swScissor(int x, int y, int width, int height);
+
+SWAPI void swClearColor(float r, float g, float b, float a);
+SWAPI void swClearDepth(float depth);
+SWAPI void swClear(uint32_t bitmask);
+
+SWAPI void swBlendFunc(SWfactor sfactor, SWfactor dfactor);
+SWAPI void swPolygonMode(SWpoly mode);
+SWAPI void swCullFace(SWface face);
+
+SWAPI void swPointSize(float size);
+SWAPI void swLineWidth(float width);
+
+SWAPI void swMatrixMode(SWmatrix mode);
+SWAPI void swPushMatrix(void);
+SWAPI void swPopMatrix(void);
+SWAPI void swLoadIdentity(void);
+SWAPI void swTranslatef(float x, float y, float z);
+SWAPI void swRotatef(float angle, float x, float y, float z);
+SWAPI void swScalef(float x, float y, float z);
+SWAPI void swMultMatrixf(const float *mat);
+SWAPI void swFrustum(double left, double right, double bottom, double top, double znear, double zfar);
+SWAPI void swOrtho(double left, double right, double bottom, double top, double znear, double zfar);
+
+SWAPI void swBegin(SWdraw mode);
+SWAPI void swEnd(void);
+
+SWAPI void swVertex2i(int x, int y);
+SWAPI void swVertex2f(float x, float y);
+SWAPI void swVertex2fv(const float *v);
+SWAPI void swVertex3i(int x, int y, int z);
+SWAPI void swVertex3f(float x, float y, float z);
+SWAPI void swVertex3fv(const float *v);
+SWAPI void swVertex4i(int x, int y, int z, int w);
+SWAPI void swVertex4f(float x, float y, float z, float w);
+SWAPI void swVertex4fv(const float *v);
+
+SWAPI void swColor3ub(uint8_t r, uint8_t g, uint8_t b);
+SWAPI void swColor3ubv(const uint8_t *v);
+SWAPI void swColor3f(float r, float g, float b);
+SWAPI void swColor3fv(const float *v);
+SWAPI void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+SWAPI void swColor4ubv(const uint8_t *v);
+SWAPI void swColor4f(float r, float g, float b, float a);
+SWAPI void swColor4fv(const float *v);
+
+SWAPI void swTexCoord2f(float u, float v);
+SWAPI void swTexCoord2fv(const float *v);
+
+SWAPI void swBindArray(SWarray type, void *buffer);
+SWAPI void swDrawArrays(SWdraw mode, int offset, int count);
+SWAPI void swDrawElements(SWdraw mode, int count, int type, const void *indices);
+
+SWAPI void swGenTextures(int count, uint32_t *textures);
+SWAPI void swDeleteTextures(int count, uint32_t *textures);
+
+SWAPI void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data);
+SWAPI void swTexParameteri(int param, int value);
+SWAPI void swBindTexture(uint32_t id);
+
+#endif // RLSW_H
+
+/***********************************************************************************
+*
+*   RLSW IMPLEMENTATION
+*
+************************************************************************************/
+#define RLSW_IMPLEMENTATION
+#if defined(RLSW_IMPLEMENTATION)
+
+#include <stdlib.h>         // Required for: malloc(), free()
+#include <stddef.h>         // Required for: NULL, size_t, uint8_t, uint16_t, uint32_t...
+#include <math.h>           // Required for: sinf(), cosf(), floorf(), fabsf(), sqrtf(), roundf()
+
+// Simple log system to avoid printf() calls if required
+// NOTE: Avoiding those calls, also avoids const strings memory usage
+#define SW_SUPPORT_LOG_INFO
+#if defined(SW_SUPPORT_LOG_INFO) //&& defined(_DEBUG)      // WARNING: LOG() output required for this tool
+    #include <stdio.h>
+    #define SW_LOG(...) printf(__VA_ARGS__)
+#else
+    #define SW_LOG(...)
+#endif
+
+#if defined(_MSC_VER)
+    #define SW_ALIGN(x) __declspec(align(x))
+#elif defined(__GNUC__) || defined(__clang__)
+    #define SW_ALIGN(x) __attribute__((aligned(x)))
+#else
+    #define SW_ALIGN(x) // Do nothing if not available
+#endif
+
+#if defined(_M_X64) || defined(__x86_64__)
+    #define SW_ARCH_X86_64
+#elif defined(_M_IX86) || defined(__i386__)
+    #define SW_ARCH_X86
+#elif defined(_M_ARM) || defined(__arm__)
+    #define SW_ARCH_ARM32
+#elif defined(_M_ARM64) || defined(__aarch64__)
+    #define SW_ARCH_ARM64
+#elif defined(__riscv)
+    #define SW_ARCH_RISCV
+#endif
+
+#if defined(RLSW_USE_SIMD_INTRINSICS)
+    // Check for SIMD vector instructions
+    // NOTE: Compiler is responsible to enable required flags for host device,
+    // supported features are detected at compiler init but varies depending on compiler
+    // TODO: This logic must be reviewed to avoid the inclusion of multiple headers
+    // and enable the higher level of SIMD available
+    #if defined(__FMA__) && defined(__AVX2__)
+        #define SW_HAS_FMA_AVX2
+        #include <immintrin.h>
+    #elif defined(__FMA__) && defined(__AVX__)
+        #define SW_HAS_FMA_AVX
+        #include <immintrin.h>
+    #elif defined(__AVX2__)
+        #define SW_HAS_AVX2
+        #include <immintrin.h>
+    #elif defined(__AVX__)
+        #define SW_HAS_AVX
+        #include <immintrin.h>
+    #endif
+    #if defined(__SSE4_2__)
+        #define SW_HAS_SSE42
+        #include <nmmintrin.h>
+    #elif defined(__SSE4_1__)
+        #define SW_HAS_SSE41
+        #include <smmintrin.h>
+    #elif defined(__SSSE3__)
+        #define SW_HAS_SSSE3
+        #include <tmmintrin.h>
+    #elif defined(__SSE3__)
+        #define SW_HAS_SSE3
+        #include <pmmintrin.h>
+    #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64
+        #define SW_HAS_SSE2
+        #include <emmintrin.h>
+    #elif defined(__SSE__)
+        #define SW_HAS_SSE
+        #include <xmmintrin.h>
+    #endif
+    #if defined(__ARM_NEON) || defined(__aarch64__)
+        #if defined(__ARM_FEATURE_FMA)
+            #define SW_HAS_NEON_FMA
+        #else
+            #define SW_HAS_NEON
+        #endif
+        #include <arm_neon.h>
+    #endif
+    #if defined(__riscv_vector)
+        // NOTE: Requires compilation flags: -march=rv64gcv -mabi=lp64d
+        #define SW_HAS_RVV
+        #include <riscv_vector.h>
+    #endif
+#endif  // RLSW_USE_SIMD_INTRINSICS
+
+#ifdef __cplusplus
+    #define SW_CURLY_INIT(name) name
+#else
+    #define SW_CURLY_INIT(name) (name)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define SW_PI       3.14159265358979323846f
+#define SW_INV_255  0.00392156862745098f            // 1.0f/255.0f
+#define SW_DEG2RAD  (SW_PI/180.0f)
+#define SW_RAD2DEG  (180.0f/SW_PI)
+
+#define SW_COLOR_PIXEL_SIZE     (SW_COLOR_BUFFER_BITS >> 3)
+#define SW_DEPTH_PIXEL_SIZE     (SW_DEPTH_BUFFER_BITS >> 3)
+#define SW_PIXEL_SIZE           (SW_COLOR_PIXEL_SIZE + SW_DEPTH_PIXEL_SIZE)
+
+#if (SW_PIXEL_SIZE <= 4)
+    #define SW_PIXEL_ALIGNMENT 4
+#else // if (SW_PIXEL_SIZE <= 8)
+    #define SW_PIXEL_ALIGNMENT 8
+#endif
+
+#if (SW_COLOR_BUFFER_BITS == 8)
+    #define SW_COLOR_TYPE       uint8_t
+    #define SW_COLOR_IS_PACKED  1
+    #define SW_COLOR_PACK_COMP  1
+    #define SW_PACK_COLOR(r,g,b) ((((uint8_t)((r)*7+0.5f))&0x07)<<5 | (((uint8_t)((g)*7+0.5f))&0x07)<<2 | ((uint8_t)((b)*3+0.5f))&0x03)
+    #define SW_UNPACK_R(p)      (((p)>>5)&0x07)
+    #define SW_UNPACK_G(p)      (((p)>>2)&0x07)
+    #define SW_UNPACK_B(p)      ((p)&0x03)
+    #define SW_SCALE_R(v)       ((v)*255+3)/7
+    #define SW_SCALE_G(v)       ((v)*255+3)/7
+    #define SW_SCALE_B(v)       ((v)*255+1)/3
+    #define SW_TO_FLOAT_R(v)    ((v)*(1.0f/7.0f))
+    #define SW_TO_FLOAT_G(v)    ((v)*(1.0f/7.0f))
+    #define SW_TO_FLOAT_B(v)    ((v)*(1.0f/3.0f))
+#elif (SW_COLOR_BUFFER_BITS == 16)
+    #define SW_COLOR_TYPE       uint16_t
+    #define SW_COLOR_IS_PACKED  1
+    #define SW_COLOR_PACK_COMP  1
+    #define SW_PACK_COLOR(r,g,b) ((((uint16_t)((r)*31+0.5f))&0x1F)<<11 | (((uint16_t)((g)*63+0.5f))&0x3F)<<5 | ((uint16_t)((b)*31+0.5f))&0x1F)
+    #define SW_UNPACK_R(p)      (((p)>>11)&0x1F)
+    #define SW_UNPACK_G(p)      (((p)>>5)&0x3F)
+    #define SW_UNPACK_B(p)      ((p)&0x1F)
+    #define SW_SCALE_R(v)       ((v)*255+15)/31
+    #define SW_SCALE_G(v)       ((v)*255+31)/63
+    #define SW_SCALE_B(v)       ((v)*255+15)/31
+    #define SW_TO_FLOAT_R(v)    ((v)*(1.0f/31.0f))
+    #define SW_TO_FLOAT_G(v)    ((v)*(1.0f/63.0f))
+    #define SW_TO_FLOAT_B(v)    ((v)*(1.0f/31.0f))
+#else // 32 bits
+    #define SW_COLOR_TYPE       uint8_t
+    #define SW_COLOR_IS_PACKED  0
+    #define SW_COLOR_PACK_COMP  4
+#endif
+
+#if (SW_DEPTH_BUFFER_BITS == 16)
+    #define SW_DEPTH_TYPE       uint16_t
+    #define SW_DEPTH_IS_PACKED  1
+    #define SW_DEPTH_PACK_COMP  1
+    #define SW_DEPTH_MAX        UINT16_MAX
+    #define SW_DEPTH_SCALE      (1.0f/UINT16_MAX)
+    #define SW_PACK_DEPTH(d)    ((SW_DEPTH_TYPE)((d)*SW_DEPTH_MAX))
+    #define SW_UNPACK_DEPTH(p)  (p)
+#elif (SW_DEPTH_BUFFER_BITS == 24)
+    #define SW_DEPTH_TYPE       uint8_t
+    #define SW_DEPTH_IS_PACKED  0
+    #define SW_DEPTH_PACK_COMP  3
+    #define SW_DEPTH_MAX        0xFFFFFFU
+    #define SW_DEPTH_SCALE      (1.0f/0xFFFFFFU)
+    #define SW_PACK_DEPTH_0(d)  ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>16)&0xFFU))
+    #define SW_PACK_DEPTH_1(d)  ((uint8_t)(((uint32_t)((d)*SW_DEPTH_MAX)>>8)&0xFFU))
+    #define SW_PACK_DEPTH_2(d)  ((uint8_t)((uint32_t)((d)*SW_DEPTH_MAX)&0xFFU))
+    #define SW_UNPACK_DEPTH(p)  ((((uint32_t)(p)[0]<<16)|((uint32_t)(p)[1]<<8)|(uint32_t)(p)[2]))
+#else // 32 bits
+    #define SW_DEPTH_TYPE       float
+    #define SW_DEPTH_IS_PACKED  1
+    #define SW_DEPTH_PACK_COMP  1
+    #define SW_DEPTH_MAX        1.0f
+    #define SW_DEPTH_SCALE      1.0f
+    #define SW_PACK_DEPTH(d)    ((SW_DEPTH_TYPE)(d))
+    #define SW_UNPACK_DEPTH(p)  (p)
+#endif
+
+#define SW_STATE_CHECK(flags)   (SW_STATE_CHECK_EX(RLSW.stateFlags, (flags)))
+#define SW_STATE_CHECK_EX(state, flags) (((state) & (flags)) == (flags))
+
+#define SW_STATE_SCISSOR_TEST   (1 << 0)
+#define SW_STATE_TEXTURE_2D     (1 << 1)
+#define SW_STATE_DEPTH_TEST     (1 << 2)
+#define SW_STATE_CULL_FACE      (1 << 3)
+#define SW_STATE_BLEND          (1 << 4)
+
+//----------------------------------------------------------------------------------
+// Module Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Pixel data format type
+// NOTE: Enum aligned with raylib PixelFormat
+typedef enum {
+    SW_PIXELFORMAT_UNKNOWN = 0,
+    SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,         // 8 bit per pixel (no alpha)
+    SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,        // 8*2 bpp (2 channels)
+    SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5,            // 16 bpp
+    SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8,            // 24 bpp
+    SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,          // 16 bpp (1 bit alpha)
+    SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4,          // 16 bpp (4 bit alpha)
+    SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,          // 32 bpp
+    SW_PIXELFORMAT_UNCOMPRESSED_R32,               // 32 bpp (1 channel - float)
+    SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32,         // 32*3 bpp (3 channels - float)
+    SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32,      // 32*4 bpp (4 channels - float)
+    SW_PIXELFORMAT_UNCOMPRESSED_R16,               // 16 bpp (1 channel - half float)
+    SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16,         // 16*3 bpp (3 channels - half float)
+    SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16,      // 16*4 bpp (4 channels - half float)
+} sw_pixelformat_t;
+
+typedef void (*sw_factor_f)(
+    float *SW_RESTRICT factor,
+    const float *SW_RESTRICT src,
+    const float *SW_RESTRICT dst
+);
+
+typedef float sw_matrix_t[4*4];
+typedef uint16_t sw_half_t;
+
+typedef struct {
+    float position[4];          // Position coordinates
+    float texcoord[2];          // Texture coordinates
+    float color[4];             // Color value (RGBA)
+
+    float homogeneous[4];       // Homogeneous coordinates
+    float screen[2];            // Screen coordinates
+} sw_vertex_t;
+
+typedef struct {
+    uint8_t *pixels;            // Texture pixels (RGBA32)
+
+    int width, height;          // Dimensions of the texture
+    int wMinus1, hMinus1;       // Dimensions minus one
+
+    SWfilter minFilter;         // Minification filter
+    SWfilter magFilter;         // Magnification filter
+
+    SWwrap sWrap;               // texcoord.x wrap mode
+    SWwrap tWrap;               // texcoord.y wrap mode
+
+    float tx;                   // Texel width
+    float ty;                   // Texel height
+} sw_texture_t;
+
+// Pixel data type
+typedef SW_ALIGN(SW_PIXEL_ALIGNMENT) struct {
+    SW_COLOR_TYPE color[SW_COLOR_PACK_COMP];
+    SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP];
+#if (SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT != 0)
+    uint8_t padding[SW_PIXEL_ALIGNMENT - SW_PIXEL_SIZE % SW_PIXEL_ALIGNMENT];
+#endif
+} sw_pixel_t;
+
+typedef struct {
+    sw_pixel_t *pixels;
+    int width;
+    int height;
+    int allocSz;
+} sw_framebuffer_t;
+
+typedef struct {
+    sw_framebuffer_t framebuffer;   // Main framebuffer
+    sw_pixel_t clearValue;          // Clear value of the framebuffer
+
+    float vpCenter[2];              // Viewport center
+    float vpHalf[2];                // Viewport half dimensions
+    int vpSize[2];                  // Viewport dimensions (minus one)
+    int vpMin[2];                   // Viewport minimum renderable point (top-left)
+    int vpMax[2];                   // Viewport maximum renderable point (bottom-right)
+
+    int scMin[2];                   // Scissor rectangle minimum renderable point (top-left)
+    int scMax[2];                   // Scissor rectangle maximum renderable point (bottom-right)
+    float scClipMin[2];             // Scissor rectangle minimum renderable point in clip space
+    float scClipMax[2];             // Scissor rectangle maximum renderable point in clip space
+
+    uint32_t currentTexture;        // Current active texture id
+
+    struct {
+        float *positions;
+        float *texcoords;
+        uint8_t *colors;
+    } array;
+
+    struct {
+        float texcoord[2];
+        float color[4];
+    } current;
+
+    sw_vertex_t vertexBuffer[SW_MAX_CLIPPED_POLYGON_VERTICES];  // Buffer used for storing primitive vertices, used for processing and rendering
+    int vertexCounter;                                          // Number of vertices in 'ctx.vertexBuffer'
+
+    SWdraw drawMode;                                            // Current primitive mode (e.g., lines, triangles)
+    SWpoly polyMode;                                            // Current polygon filling mode (e.g., lines, triangles)
+    int reqVertices;                                            // Number of vertices required for the primitive being drawn
+    float pointRadius;                                          // Rasterized point radius
+    float lineWidth;                                            // Rasterized line width
+
+    sw_matrix_t stackProjection[SW_MAX_PROJECTION_STACK_SIZE];  // Projection matrix stack for push/pop operations
+    sw_matrix_t stackModelview[SW_MAX_MODELVIEW_STACK_SIZE];    // Modelview matrix stack for push/pop operations
+    sw_matrix_t stackTexture[SW_MAX_TEXTURE_STACK_SIZE];        // Texture matrix stack for push/pop operations
+    uint32_t stackProjectionCounter;                            // Counter for matrix stack operations
+    uint32_t stackModelviewCounter;                             // Counter for matrix stack operations
+    uint32_t stackTextureCounter;                               // Counter for matrix stack operations
+    SWmatrix currentMatrixMode;                                 // Current matrix mode (e.g., sw_MODELVIEW, sw_PROJECTION)
+    sw_matrix_t *currentMatrix;                                 // Pointer to the currently used matrix according to the mode
+    sw_matrix_t matMVP;                                         // Model view projection matrix, calculated and used internally
+    bool isDirtyMVP;                                            // Indicates if the MVP matrix should be rebuilt
+
+    SWfactor srcFactor;
+    SWfactor dstFactor;
+
+    sw_factor_f srcFactorFunc;
+    sw_factor_f dstFactorFunc;
+
+    SWface cullFace;                                            // Faces to cull
+    SWerrcode errCode;                                          // Last error code
+
+    sw_texture_t *loadedTextures;
+    int loadedTextureCount;
+
+    uint32_t *freeTextureIds;
+    int freeTextureIdCount;
+
+    uint32_t stateFlags;
+} sw_context_t;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static sw_context_t RLSW = { 0 };
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static inline void sw_matrix_id(sw_matrix_t dst)
+{
+    dst[0]  = 1, dst[1]  = 0, dst[2]  = 0, dst[3]  = 0;
+    dst[4]  = 0, dst[5]  = 1, dst[6]  = 0, dst[7]  = 0;
+    dst[8]  = 0, dst[9]  = 0, dst[10] = 1, dst[11] = 0;
+    dst[12] = 0, dst[13] = 0, dst[14] = 0, dst[15] = 1;
+}
+
+static inline void sw_matrix_mul_rst(float *SW_RESTRICT dst, const float *SW_RESTRICT left, const float *SW_RESTRICT right)
+{
+    float l00 = left[0],  l01 = left[1],  l02 = left[2],  l03 = left[3];
+    float l10 = left[4],  l11 = left[5],  l12 = left[6],  l13 = left[7];
+    float l20 = left[8],  l21 = left[9],  l22 = left[10], l23 = left[11];
+    float l30 = left[12], l31 = left[13], l32 = left[14], l33 = left[15];
+
+    dst[0]  = l00*right[0] + l01*right[4] + l02*right[8]  + l03*right[12];
+    dst[4]  = l10*right[0] + l11*right[4] + l12*right[8]  + l13*right[12];
+    dst[8]  = l20*right[0] + l21*right[4] + l22*right[8]  + l23*right[12];
+    dst[12] = l30*right[0] + l31*right[4] + l32*right[8]  + l33*right[12];
+
+    dst[1]  = l00*right[1] + l01*right[5] + l02*right[9]  + l03*right[13];
+    dst[5]  = l10*right[1] + l11*right[5] + l12*right[9]  + l13*right[13];
+    dst[9]  = l20*right[1] + l21*right[5] + l22*right[9]  + l23*right[13];
+    dst[13] = l30*right[1] + l31*right[5] + l32*right[9]  + l33*right[13];
+
+    dst[2]  = l00*right[2] + l01*right[6] + l02*right[10] + l03*right[14];
+    dst[6]  = l10*right[2] + l11*right[6] + l12*right[10] + l13*right[14];
+    dst[10] = l20*right[2] + l21*right[6] + l22*right[10] + l23*right[14];
+    dst[14] = l30*right[2] + l31*right[6] + l32*right[10] + l33*right[14];
+
+    dst[3]  = l00*right[3] + l01*right[7] + l02*right[11] + l03*right[15];
+    dst[7]  = l10*right[3] + l11*right[7] + l12*right[11] + l13*right[15];
+    dst[11] = l20*right[3] + l21*right[7] + l22*right[11] + l23*right[15];
+    dst[15] = l30*right[3] + l31*right[7] + l32*right[11] + l33*right[15];
+}
+
+static inline void sw_matrix_mul(sw_matrix_t dst, const sw_matrix_t left, const sw_matrix_t right)
+{
+    float result[16];
+
+    sw_matrix_mul_rst(result, left, right);
+
+    for (int i = 0; i < 16; i++) dst[i] = result[i];
+}
+
+static inline float sw_saturate(float x)
+{
+    union { float f; uint32_t u; } fb;
+    fb.f = x;
+
+    // Check if x < 0.0f
+    // If sign bit is set (MSB), x is negative
+    if ((fb.u & 0x80000000) != 0) return 0.0f;
+
+    // Check if x > 1.0f
+    // Works for positive floats: IEEE 754 ordering matches integer ordering
+    if (fb.u > 0x3F800000) return 1.0f;
+
+    // x is in [0.0f, 1.0f]
+    return x;
+}
+
+static inline float sw_fract(float x)
+{
+    return (x - floorf(x));
+}
+
+static inline int sw_clampi(int v, int min, int max)
+{
+    if (v < min) return min;
+    if (v > max) return max;
+    return v;
+}
+
+static inline void sw_lerp_vertex_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float t)
+{
+    const float tInv = 1.0f - t;
+
+    // Position interpolation (4 components)
+    out->position[0] = a->position[0]*tInv + b->position[0]*t;
+    out->position[1] = a->position[1]*tInv + b->position[1]*t;
+    out->position[2] = a->position[2]*tInv + b->position[2]*t;
+    out->position[3] = a->position[3]*tInv + b->position[3]*t;
+
+    // Texture coordinate interpolation (2 components)
+    out->texcoord[0] = a->texcoord[0]*tInv + b->texcoord[0]*t;
+    out->texcoord[1] = a->texcoord[1]*tInv + b->texcoord[1]*t;
+
+    // Color interpolation (4 components)
+    out->color[0] = a->color[0]*tInv + b->color[0]*t;
+    out->color[1] = a->color[1]*tInv + b->color[1]*t;
+    out->color[2] = a->color[2]*tInv + b->color[2]*t;
+    out->color[3] = a->color[3]*tInv + b->color[3]*t;
+
+    // Homogeneous coordinate interpolation (4 components)
+    out->homogeneous[0] = a->homogeneous[0]*tInv + b->homogeneous[0]*t;
+    out->homogeneous[1] = a->homogeneous[1]*tInv + b->homogeneous[1]*t;
+    out->homogeneous[2] = a->homogeneous[2]*tInv + b->homogeneous[2]*t;
+    out->homogeneous[3] = a->homogeneous[3]*tInv + b->homogeneous[3]*t;
+}
+
+static inline void sw_get_vertex_grad_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT a, const sw_vertex_t *SW_RESTRICT b, float scale)
+{
+    // Calculate gradients for Position
+    out->position[0] = (b->position[0] - a->position[0])*scale;
+    out->position[1] = (b->position[1] - a->position[1])*scale;
+    out->position[2] = (b->position[2] - a->position[2])*scale;
+    out->position[3] = (b->position[3] - a->position[3])*scale;
+
+    // Calculate gradients for Texture coordinates
+    out->texcoord[0] = (b->texcoord[0] - a->texcoord[0])*scale;
+    out->texcoord[1] = (b->texcoord[1] - a->texcoord[1])*scale;
+
+    // Calculate gradients for Color
+    out->color[0] = (b->color[0] - a->color[0])*scale;
+    out->color[1] = (b->color[1] - a->color[1])*scale;
+    out->color[2] = (b->color[2] - a->color[2])*scale;
+    out->color[3] = (b->color[3] - a->color[3])*scale;
+
+    // Calculate gradients for Homogeneous coordinates
+    out->homogeneous[0] = (b->homogeneous[0] - a->homogeneous[0])*scale;
+    out->homogeneous[1] = (b->homogeneous[1] - a->homogeneous[1])*scale;
+    out->homogeneous[2] = (b->homogeneous[2] - a->homogeneous[2])*scale;
+    out->homogeneous[3] = (b->homogeneous[3] - a->homogeneous[3])*scale;
+}
+
+static inline void sw_add_vertex_grad_PTCH(sw_vertex_t *SW_RESTRICT out, const sw_vertex_t *SW_RESTRICT gradients)
+{
+    // Add gradients to Position
+    out->position[0] += gradients->position[0];
+    out->position[1] += gradients->position[1];
+    out->position[2] += gradients->position[2];
+    out->position[3] += gradients->position[3];
+
+    // Add gradients to Texture coordinates
+    out->texcoord[0] += gradients->texcoord[0];
+    out->texcoord[1] += gradients->texcoord[1];
+
+    // Add gradients to Color
+    out->color[0] += gradients->color[0];
+    out->color[1] += gradients->color[1];
+    out->color[2] += gradients->color[2];
+    out->color[3] += gradients->color[3];
+
+    // Add gradients to Homogeneous coordinates
+    out->homogeneous[0] += gradients->homogeneous[0];
+    out->homogeneous[1] += gradients->homogeneous[1];
+    out->homogeneous[2] += gradients->homogeneous[2];
+    out->homogeneous[3] += gradients->homogeneous[3];
+}
+
+static inline void sw_add_vertex_grad_scaled_PTCH(
+    sw_vertex_t *SW_RESTRICT out,
+    const sw_vertex_t *SW_RESTRICT gradients,
+    float scale)
+{
+    // Add gradients to Position
+    out->position[0] += gradients->position[0]*scale;
+    out->position[1] += gradients->position[1]*scale;
+    out->position[2] += gradients->position[2]*scale;
+    out->position[3] += gradients->position[3]*scale;
+
+    // Add gradients to Texture coordinates
+    out->texcoord[0] += gradients->texcoord[0]*scale;
+    out->texcoord[1] += gradients->texcoord[1]*scale;
+
+    // Add gradients to Color
+    out->color[0] += gradients->color[0]*scale;
+    out->color[1] += gradients->color[1]*scale;
+    out->color[2] += gradients->color[2]*scale;
+    out->color[3] += gradients->color[3]*scale;
+
+    // Add gradients to Homogeneous coordinates
+    out->homogeneous[0] += gradients->homogeneous[0]*scale;
+    out->homogeneous[1] += gradients->homogeneous[1]*scale;
+    out->homogeneous[2] += gradients->homogeneous[2]*scale;
+    out->homogeneous[3] += gradients->homogeneous[3]*scale;
+}
+
+static inline void sw_float_to_unorm8_simd(uint8_t dst[4], const float src[4])
+{
+#if defined(SW_HAS_NEON)
+    float32x4_t values = vld1q_f32(src);
+    float32x4_t scaled = vmulq_n_f32(values, 255.0f);
+    int32x4_t clamped_s32 = vcvtq_s32_f32(scaled);  // f32 -> s32 (truncated)
+    int16x4_t narrow16_s = vqmovn_s32(clamped_s32);
+    int16x8_t combined16_s = vcombine_s16(narrow16_s, narrow16_s);
+    uint8x8_t narrow8_u = vqmovun_s16(combined16_s);
+    vst1_lane_u32((uint32_t*)dst, vreinterpret_u32_u8(narrow8_u), 0);
+#elif defined(SW_HAS_SSE41)
+    __m128 values = _mm_loadu_ps(src);
+    __m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f));
+    __m128i clamped = _mm_cvtps_epi32(scaled);      // f32 -> s32 (truncated)
+    clamped = _mm_packus_epi32(clamped, clamped);   // s32 -> u16 (saturated < 0 to 0)
+    clamped = _mm_packus_epi16(clamped, clamped);   // u16 -> u8 (saturated > 255 to 255)
+    *(uint32_t*)dst = _mm_cvtsi128_si32(clamped);
+#elif defined(SW_HAS_SSE2)
+    __m128 values = _mm_loadu_ps(src);
+    __m128 scaled = _mm_mul_ps(values, _mm_set1_ps(255.0f));
+    __m128i clamped = _mm_cvtps_epi32(scaled);      // f32 -> s32 (truncated)
+    clamped = _mm_packs_epi32(clamped, clamped);    // s32 -> s16 (saturated)
+    clamped = _mm_packus_epi16(clamped, clamped);   // s16 -> u8 (saturated < 0 to 0)
+    *(uint32_t*)dst = _mm_cvtsi128_si32(clamped);
+#elif defined(SW_HAS_RVV)
+    // TODO: Sample code generated by AI, needs testing and review
+    // NOTE: RVV 1.0 specs define the use of __riscv_ prefix for instrinsic functions
+    size_t vl = __riscv_vsetvl_e32m1(4); // Load up to 4 floats into a vector register
+    vfloat32m1_t vsrc = __riscv_vle32_v_f32m1(src, vl); // Load float32 values
+
+    // Clamp to [0.0f, 1.0f]
+    vfloat32m1_t vzero = __riscv_vfmv_v_f_f32m1(0.0f, vl);
+    vfloat32m1_t vone  = __riscv_vfmv_v_f_f32m1(1.0f, vl);
+    vsrc = __riscv_vfmin_vv_f32m1(vsrc, vone, vl);
+    vsrc = __riscv_vfmax_vv_f32m1(vsrc, vzero, vl);
+
+    // Multiply by 255.0f and add 0.5f for rounding
+    vfloat32m1_t vscaled = __riscv_vfmul_vf_f32m1(vsrc, 255.0f, vl);
+    vscaled = __riscv_vfadd_vf_f32m1(vscaled, 0.5f, vl);
+
+    // Convert to unsigned integer (truncate toward zero)
+    vuint32m1_t vu32 = __riscv_vfcvt_xu_f_v_u32m1(vscaled, vl);
+
+    // Narrow from u32 -> u8
+    vuint8m1_t vu8 = __riscv_vnclipu_wx_u8m1(vu32, 0, vl); // Round toward zero
+    __riscv_vse8_v_u8m1(dst, vu8, vl); // Store result
+#else
+    for (int i = 0; i < 4; i++)
+    {
+        float val = src[i]*255.0f;
+        val = (val > 255.0f)? 255.0f : val;
+        val = (val < 0.0f)? 0.0f : val;
+        dst[i] = (uint8_t)val;
+    }
+#endif
+}
+
+static inline void sw_float_from_unorm8_simd(float dst[4], const uint8_t src[4])
+{
+#if defined(SW_HAS_NEON)
+    uint8x8_t bytes8 = vld1_u8(src); //< Read 8 bytes, faster, but let's hope we're not at the end of the page (unlikely)...
+    uint16x8_t bytes16 = vmovl_u8(bytes8);
+    uint32x4_t ints = vmovl_u16(vget_low_u16(bytes16));
+    float32x4_t floats = vcvtq_f32_u32(ints);
+    floats = vmulq_n_f32(floats, SW_INV_255);
+    vst1q_f32(dst, floats);
+#elif defined(SW_HAS_SSE41)
+    __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src);
+    __m128i ints = _mm_cvtepu8_epi32(bytes);
+    __m128 floats = _mm_cvtepi32_ps(ints);
+    floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255));
+    _mm_storeu_ps(dst, floats);
+#elif defined(SW_HAS_SSE2)
+    __m128i bytes = _mm_cvtsi32_si128(*(const uint32_t *)src);
+    bytes = _mm_unpacklo_epi8(bytes, _mm_setzero_si128());
+    __m128i ints = _mm_unpacklo_epi16(bytes, _mm_setzero_si128());
+    __m128 floats = _mm_cvtepi32_ps(ints);
+    floats = _mm_mul_ps(floats, _mm_set1_ps(SW_INV_255));
+    _mm_storeu_ps(dst, floats);
+#elif defined(SW_HAS_RVV)
+    // TODO: Sample code generated by AI, needs testing and review
+    size_t vl = __riscv_vsetvl_e8m1(4); // Set vector length for 8-bit input elements
+    vuint8m1_t vsrc_u8 = __riscv_vle8_v_u8m1(src, vl); // Load 4 unsigned 8-bit integers
+    vuint32m1_t vsrc_u32 = __riscv_vwcvt_xu_u_v_u32m1(vsrc_u8, vl); // Widen to 32-bit unsigned integers
+    vfloat32m1_t vsrc_f32 = __riscv_vfcvt_f_xu_v_f32m1(vsrc_u32, vl); // Convert to float32
+    vfloat32m1_t vnorm = __riscv_vfmul_vf_f32m1(vsrc_f32, SW_INV_255, vl); // Multiply by 1/255.0 to normalize
+    __riscv_vse32_v_f32m1(dst, vnorm, vl); // Store result
+#else
+    dst[0] = (float)src[0]*SW_INV_255;
+    dst[1] = (float)src[1]*SW_INV_255;
+    dst[2] = (float)src[2]*SW_INV_255;
+    dst[3] = (float)src[3]*SW_INV_255;
+#endif
+}
+
+// Half conversion functions
+static inline uint32_t sw_half_to_float_ui(uint16_t h)
+{
+    uint32_t s = (uint32_t)(h & 0x8000) << 16;
+    int32_t em = h & 0x7fff;
+
+    // bias exponent and pad mantissa with 0; 112 is relative exponent bias (127-15)
+    int32_t r = (em + (112 << 10)) << 13;
+
+    // denormal: flush to zero
+    r = (em < (1 << 10))? 0 : r;
+
+    // infinity/NaN; note that we preserve NaN payload as a byproduct of unifying inf/nan cases
+    // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255
+    r += (em >= (31 << 10))? (112 << 23) : 0;
+
+    return s | r;
+}
+
+static inline float sw_half_to_float(sw_half_t y)
+{
+    union { float f; uint32_t i; } v = { .i = sw_half_to_float_ui(y) };
+
+    return v.f;
+}
+
+static inline uint16_t sw_half_from_float_ui(uint32_t ui)
+{
+    int32_t s = (ui >> 16) & 0x8000;
+    int32_t em = ui & 0x7fffffff;
+
+    // Bias exponent and round to nearest; 112 is relative exponent bias (127-15)
+    int32_t h = (em - (112 << 23) + (1 << 12)) >> 13;
+
+    // Underflow: flush to zero; 113 encodes exponent -14
+    h = (em < (113 << 23))? 0 : h;
+
+    // Overflow: infinity; 143 encodes exponent 16
+    h = (em >= (143 << 23))? 0x7c00 : h;
+
+    // NaN; note that we convert all types of NaN to qNaN
+    h = (em > (255 << 23))? 0x7e00 : h;
+
+    return (uint16_t)(s | h);
+}
+
+static inline sw_half_t sw_half_from_float(float i)
+{
+    union { float f; uint32_t i; } v;
+    v.f = i;
+    return sw_half_from_float_ui(v.i);
+}
+
+// Framebuffer management functions
+//-------------------------------------------------------------------------------------------
+static inline bool sw_framebuffer_load(int w, int h)
+{
+    int size = w*h;
+
+    RLSW.framebuffer.pixels = SW_MALLOC(sizeof(sw_pixel_t)*size);
+    if (RLSW.framebuffer.pixels == NULL) return false;
+
+    RLSW.framebuffer.width = w;
+    RLSW.framebuffer.height = h;
+    RLSW.framebuffer.allocSz = size;
+
+    return true;
+}
+
+static inline bool sw_framebuffer_resize(int w, int h)
+{
+    int newSize = w*h;
+
+    if (newSize <= RLSW.framebuffer.allocSz)
+    {
+        RLSW.framebuffer.width = w;
+        RLSW.framebuffer.height = h;
+        return true;
+    }
+
+    void *newPixels = SW_REALLOC(RLSW.framebuffer.pixels, sizeof(sw_pixel_t)*newSize);
+    if (newPixels == NULL) return false;
+
+    RLSW.framebuffer.pixels = newPixels;
+
+    RLSW.framebuffer.width = w;
+    RLSW.framebuffer.height = h;
+    RLSW.framebuffer.allocSz = newSize;
+
+    return true;
+}
+
+static inline void sw_framebuffer_read_color(float dst[4], const sw_pixel_t *src)
+{
+#if SW_COLOR_IS_PACKED
+    SW_COLOR_TYPE pixel = src->color[0];
+    dst[0] = SW_TO_FLOAT_R(SW_UNPACK_R(pixel));
+    dst[1] = SW_TO_FLOAT_G(SW_UNPACK_G(pixel));
+    dst[2] = SW_TO_FLOAT_B(SW_UNPACK_B(pixel));
+    dst[3] = 1.0f;
+#else
+    sw_float_from_unorm8_simd(dst, src->color);
+#endif
+}
+
+static inline void sw_framebuffer_read_color8(uint8_t dst[4], const sw_pixel_t *src)
+{
+#if SW_COLOR_IS_PACKED
+    SW_COLOR_TYPE pixel = src->color[0];
+    dst[0] = SW_SCALE_R(SW_UNPACK_R(pixel));
+    dst[1] = SW_SCALE_G(SW_UNPACK_G(pixel));
+    dst[2] = SW_SCALE_B(SW_UNPACK_B(pixel));
+    dst[3] = 255;
+#else
+    const SW_COLOR_TYPE *p = src->color;
+    dst[0] = p[0];
+    dst[1] = p[1];
+    dst[2] = p[2];
+    dst[3] = p[3];
+#endif
+}
+
+static inline float sw_framebuffer_read_depth(const sw_pixel_t *src)
+{
+#if SW_DEPTH_IS_PACKED
+    return src->depth[0]*SW_DEPTH_SCALE;
+#else
+    return SW_UNPACK_DEPTH(src->depth)*SW_DEPTH_SCALE;
+#endif
+}
+
+static inline void sw_framebuffer_write_color(sw_pixel_t *dst, const float src[4])
+{
+#if SW_COLOR_IS_PACKED
+    dst->color[0] = SW_PACK_COLOR(src[0], src[1], src[2]);
+#else
+    sw_float_to_unorm8_simd(dst->color, src);
+#endif
+}
+
+static inline void sw_framebuffer_write_depth(sw_pixel_t *dst, float depth)
+{
+    depth = sw_saturate(depth); // REVIEW: An overflow can occur in certain circumstances with clipping, and needs to be reviewed...
+
+#if SW_DEPTH_IS_PACKED
+    dst->depth[0] = SW_PACK_DEPTH(depth);
+#else
+    dst->depth[0] = SW_PACK_DEPTH_0(depth);
+    dst->depth[1] = SW_PACK_DEPTH_1(depth);
+    dst->depth[2] = SW_PACK_DEPTH_2(depth);
+#endif
+}
+
+static inline void sw_framebuffer_fill_color(sw_pixel_t *ptr, int size, const SW_COLOR_TYPE color[SW_COLOR_PACK_COMP])
+{
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
+        for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
+        {
+            sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+            for (int x = 0; x < w; x++, row++)
+            {
+                for (int i = 0; i < SW_COLOR_PACK_COMP; i++) row->color[i] = color[i];
+            }
+        }
+    }
+    else
+    {
+        for (int i = 0; i < size; i++, ptr++)
+        {
+            for (int j = 0; j < SW_COLOR_PACK_COMP; j++) ptr->color[j] = color[j];
+        }
+    }
+}
+
+static inline void sw_framebuffer_fill_depth(sw_pixel_t *ptr, int size, const SW_DEPTH_TYPE depth[SW_DEPTH_PACK_COMP])
+{
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
+        for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
+        {
+            sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+            for (int x = 0; x < w; x++, row++)
+            {
+                for (int i = 0; i < SW_DEPTH_PACK_COMP; i++) row->depth[i] = depth[i];
+            }
+        }
+    }
+    else
+    {
+        for (int i = 0; i < size; i++, ptr++)
+        {
+            for (int j = 0; j < SW_DEPTH_PACK_COMP; j++) ptr->depth[j] = depth[j];
+        }
+    }
+}
+
+static inline void sw_framebuffer_fill(sw_pixel_t *ptr, int size, sw_pixel_t value)
+{
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        int w = RLSW.scMax[0] - RLSW.scMin[0] + 1;
+        for (int y = RLSW.scMin[1]; y <= RLSW.scMax[1]; y++)
+        {
+            sw_pixel_t *row = ptr + y*RLSW.framebuffer.width + RLSW.scMin[0];
+            for (int x = 0; x < w; x++, row++) *row = value;
+        }
+    }
+    else
+    {
+        for (int i = 0; i < size; i++, ptr++) *ptr = value;
+    }
+}
+
+static inline void sw_framebuffer_copy_fast(void* dst)
+{
+    int size = RLSW.framebuffer.width*RLSW.framebuffer.height;
+    const sw_pixel_t *pixels = RLSW.framebuffer.pixels;
+
+#if SW_COLOR_BUFFER_BITS == 8
+    uint8_t *dst8 = (uint8_t*)dst;
+    for (int i = 0; i < size; i++) dst8[i] = pixels[i].color[0];
+#elif SW_COLOR_BUFFER_BITS == 16
+    uint16_t *dst16 = (uint16_t*)dst;
+    for (int i = 0; i < size; i++) dst16[i] = *(uint16_t*)pixels[i].color;
+#else // 32 bits
+    uint32_t *dst32 = (uint32_t*)dst;
+    #if SW_GL_FRAMEBUFFER_COPY_BGRA
+        for (int i = 0; i < size; i++)
+        {
+            const uint8_t *c = pixels[i].color;
+            dst32[i] = (uint32_t)c[2] | ((uint32_t)c[1] << 8) | ((uint32_t)c[0] << 16) | ((uint32_t)c[3] << 24);
+        }
+    #else // RGBA
+        for (int i = 0; i < size; i++) dst32[i] = *(uint32_t*)pixels[i].color;
+    #endif
+#endif
+}
+
+#define DEFINE_FRAMEBUFFER_COPY_BEGIN(name, DST_PTR_T)                          \
+static inline void sw_framebuffer_copy_to_##name(int x, int y, int w, int h, DST_PTR_T *dst) \
+{                                                                               \
+    const int stride = RLSW.framebuffer.width;                                  \
+    const sw_pixel_t *src = RLSW.framebuffer.pixels + (y*stride + x);           \
+                                                                                \
+    for (int iy = 0; iy < h; iy++) {                                            \
+        const sw_pixel_t *line = src;                                           \
+        for (int ix = 0; ix < w; ix++) {                                        \
+            uint8_t color[4];                                                   \
+            sw_framebuffer_read_color8(color, line);                            \
+
+#define DEFINE_FRAMEBUFFER_COPY_END()                                           \
+            ++line;                                                             \
+        }                                                                       \
+        src += stride;                                                          \
+    }                                                                           \
+}
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(GRAYSCALE, uint8_t)
+{
+    // NTSC grayscale conversion: Y = 0.299R + 0.587G + 0.114B
+    uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000);
+    *dst++ = gray;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(GRAYALPHA, uint8_t)
+{
+    // Convert RGB to grayscale using NTSC formula
+    uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000);
+
+    dst[0] = gray;
+    dst[1] = color[3]; // alpha
+
+    dst += 2;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(R5G6B5, uint16_t)
+{
+    // Convert 8-bit RGB to 5:6:5 format
+    uint8_t r5 = (color[0]*31 + 127)/255;
+    uint8_t g6 = (color[1]*63 + 127)/255;
+    uint8_t b5 = (color[2]*31 + 127)/255;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5;
+#else // RGBA
+    uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5;
+#endif
+
+    *dst++ = rgb565;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(R8G8B8, uint8_t)
+{
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    dst[0] = color[2];
+    dst[1] = color[1];
+    dst[2] = color[0];
+#else // RGBA
+    dst[0] = color[0];
+    dst[1] = color[1];
+    dst[2] = color[2];
+#endif
+
+    dst += 3;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(R5G5B5A1, uint16_t)
+{
+    uint8_t r5 = (color[0]*31 + 127)/255;
+    uint8_t g5 = (color[1]*31 + 127)/255;
+    uint8_t b5 = (color[2]*31 + 127)/255;
+    uint8_t a1 = (color[3] >= 128)? 1 : 0;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1;
+#else // RGBA
+    uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1;
+#endif
+
+    *dst++ = pixel;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(R4G4B4A4, uint16_t)
+{
+    uint8_t r4 = (color[0]*15 + 127)/255;
+    uint8_t g4 = (color[1]*15 + 127)/255;
+    uint8_t b4 = (color[2]*15 + 127)/255;
+    uint8_t a4 = (color[3]*15 + 127)/255;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4;
+#else // RGBA
+    uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4;
+#endif
+
+    *dst++ = pixel;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+DEFINE_FRAMEBUFFER_COPY_BEGIN(R8G8B8A8, uint8_t)
+{
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    dst[0] = color[2];
+    dst[1] = color[1];
+    dst[2] = color[0];
+#else // RGBA
+    dst[0] = color[0];
+    dst[1] = color[1];
+    dst[2] = color[2];
+#endif
+    dst[3] = color[3];
+
+    dst += 4;
+}
+DEFINE_FRAMEBUFFER_COPY_END()
+
+#define DEFINE_FRAMEBUFFER_BLIT_BEGIN(name, DST_PTR_T)                          \
+static inline void sw_framebuffer_blit_to_##name(                               \
+    int xDst, int yDst, int wDst, int hDst,                                     \
+    int xSrc, int ySrc, int wSrc, int hSrc,                                     \
+    DST_PTR_T *dst)                                                             \
+{                                                                               \
+    const sw_pixel_t *srcBase = RLSW.framebuffer.pixels;                        \
+    const int fbWidth = RLSW.framebuffer.width;                                 \
+                                                                                \
+    const uint32_t xScale = ((uint32_t)wSrc << 16)/(uint32_t)wDst;              \
+    const uint32_t yScale = ((uint32_t)hSrc << 16)/(uint32_t)hDst;              \
+                                                                                \
+    for (int dy = 0; dy < hDst; dy++) {                                         \
+        uint32_t yFix = ((uint32_t)ySrc << 16) + dy*yScale;                     \
+        int sy = yFix >> 16;                                                    \
+        const sw_pixel_t *srcLine = srcBase + sy*fbWidth + xSrc;                \
+                                                                                \
+        const sw_pixel_t *srcPtr = srcLine;                                     \
+        for (int dx = 0; dx < wDst; dx++) {                                     \
+            uint32_t xFix = dx*xScale;                                          \
+            int sx = xFix >> 16;                                                \
+            const sw_pixel_t *pixel = srcPtr + sx;                              \
+            uint8_t color[4];                                                   \
+            sw_framebuffer_read_color8(color, pixel);
+
+#define DEFINE_FRAMEBUFFER_BLIT_END()                                           \
+        }                                                                       \
+    }                                                                           \
+}
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(GRAYSCALE, uint8_t)
+{
+    uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000);
+    *dst++ = gray;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(GRAYALPHA, uint8_t)
+{
+    uint8_t gray = (uint8_t)((color[0]*299 + color[1]*587 + color[2]*114 + 500)/1000);
+
+    dst[0] = gray;
+    dst[1] = color[3]; // alpha
+
+    dst += 2;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(R5G6B5, uint16_t)
+{
+    uint8_t r5 = (color[0]*31 + 127)/255;
+    uint8_t g6 = (color[1]*63 + 127)/255;
+    uint8_t b5 = (color[2]*31 + 127)/255;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t rgb565 = (b5 << 11) | (g6 << 5) | r5;
+#else // RGBA
+    uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5;
+#endif
+
+    *dst++ = rgb565;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(R8G8B8, uint8_t)
+{
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    dst[0] = color[2];
+    dst[1] = color[1];
+    dst[2] = color[0];
+#else // RGBA
+    dst[0] = color[0];
+    dst[1] = color[1];
+    dst[2] = color[2];
+#endif
+
+    dst += 3;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(R5G5B5A1, uint16_t)
+{
+    uint8_t r5 = (color[0]*31 + 127)/255;
+    uint8_t g5 = (color[1]*31 + 127)/255;
+    uint8_t b5 = (color[2]*31 + 127)/255;
+    uint8_t a1 = (color[3] >= 128)? 1 : 0;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t pixel = (b5 << 11) | (g5 << 6) | (r5 << 1) | a1;
+#else // RGBA
+    uint16_t pixel = (r5 << 11) | (g5 << 6) | (b5 << 1) | a1;
+#endif
+
+    *dst++ = pixel;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(R4G4B4A4, uint16_t)
+{
+    uint8_t r4 = (color[0]*15 + 127)/255;
+    uint8_t g4 = (color[1]*15 + 127)/255;
+    uint8_t b4 = (color[2]*15 + 127)/255;
+    uint8_t a4 = (color[3]*15 + 127)/255;
+
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    uint16_t pixel = (b4 << 12) | (g4 << 8) | (r4 << 4) | a4;
+#else // RGBA
+    uint16_t pixel = (r4 << 12) | (g4 << 8) | (b4 << 4) | a4;
+#endif
+
+    *dst++ = pixel;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+
+DEFINE_FRAMEBUFFER_BLIT_BEGIN(R8G8B8A8, uint8_t)
+{
+#if SW_GL_FRAMEBUFFER_COPY_BGRA
+    dst[0] = color[2];
+    dst[1] = color[1];
+    dst[2] = color[0];
+#else // RGBA
+    dst[0] = color[0];
+    dst[1] = color[1];
+    dst[2] = color[2];
+#endif
+    dst[3] = color[3];
+
+    dst += 4;
+}
+DEFINE_FRAMEBUFFER_BLIT_END()
+//-------------------------------------------------------------------------------------------
+
+// Pixel format management functions
+//-------------------------------------------------------------------------------------------
+static inline int sw_get_pixel_format(SWformat format, SWtype type)
+{
+    int channels = 0;
+    int bitsPerChannel = 8; // Default: 8 bits per channel
+
+    // Determine the number of channels (format)
+    switch (format)
+    {
+        case SW_LUMINANCE:        channels = 1; break;
+        case SW_LUMINANCE_ALPHA:  channels = 2; break;
+        case SW_RGB:              channels = 3; break;
+        case SW_RGBA:             channels = 4; break;
+        default: return SW_PIXELFORMAT_UNKNOWN;
+    }
+
+    // Determine the depth of each channel (type)
+    switch (type)
+    {
+        case SW_UNSIGNED_BYTE:    bitsPerChannel = 8;  break;
+        case SW_BYTE:             bitsPerChannel = 8;  break;
+        case SW_UNSIGNED_SHORT:   bitsPerChannel = 16; break;
+        case SW_SHORT:            bitsPerChannel = 16; break;
+        case SW_UNSIGNED_INT:     bitsPerChannel = 32; break;
+        case SW_INT:              bitsPerChannel = 32; break;
+        case SW_FLOAT:            bitsPerChannel = 32; break;
+        default: return SW_PIXELFORMAT_UNKNOWN;
+    }
+
+    // Map the format and type to the correct internal format
+    if (bitsPerChannel == 8)
+    {
+        if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+        if (channels == 2) return SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
+        if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8;
+        if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
+    }
+    else if (bitsPerChannel == 16)
+    {
+        if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R16;
+        if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16;
+        if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16;
+    }
+    else if (bitsPerChannel == 32)
+    {
+        if (channels == 1) return SW_PIXELFORMAT_UNCOMPRESSED_R32;
+        if (channels == 3) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32;
+        if (channels == 4) return SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
+    }
+
+    return SW_PIXELFORMAT_UNKNOWN;
+}
+
+static inline void sw_get_pixel(uint8_t *color, const void *pixels, uint32_t offset, sw_pixelformat_t format)
+{
+    switch (format)
+    {
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
+        {
+            uint8_t gray = ((const uint8_t*)pixels)[offset];
+            color[0] = gray;
+            color[1] = gray;
+            color[2] = gray;
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
+        {
+            const uint8_t *src = &((const uint8_t*)pixels)[offset*2];
+            color[0] = src[0];
+            color[1] = src[0];
+            color[2] = src[0];
+            color[3] = src[1];
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5:
+        {
+            uint16_t pixel = ((const uint16_t*)pixels)[offset];
+            color[0] = ((pixel >> 11) & 0x1F)*255/31;   // R (5 bits)
+            color[1] = ((pixel >> 5) & 0x3F)*255/63;    // G (6 bits)
+            color[2] = (pixel & 0x1F)*255/31;           // B (5 bits)
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8:
+        {
+            const uint8_t *src = &((const uint8_t*)pixels)[offset*3];
+            color[0] = src[0];
+            color[1] = src[1];
+            color[2] = src[2];
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
+        {
+            uint16_t pixel = ((const uint16_t*)pixels)[offset];
+            color[0] = ((pixel >> 11) & 0x1F)*255/31;   // R (5 bits)
+            color[1] = ((pixel >> 6) & 0x1F)*255/31;    // G (5 bits)
+            color[2] = ((pixel >> 1) & 0x1F)*255/31;    // B (5 bits)
+            color[3] = (pixel & 0x01)*255;              // A (1 bit)
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
+        {
+            uint16_t pixel = ((const uint16_t*)pixels)[offset];
+            color[0] = ((pixel >> 12) & 0x0F)*255/15;   // R (4 bits)
+            color[1] = ((pixel >> 8) & 0x0F)*255/15;    // G (4 bits)
+            color[2] = ((pixel >> 4) & 0x0F)*255/15;    // B (4 bits)
+            color[3] = (pixel & 0x0F)*255/15;           // A (4 bits)
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
+        {
+            const uint8_t *src = &((const uint8_t*)pixels)[offset*4];
+            color[0] = src[0];
+            color[1] = src[1];
+            color[2] = src[2];
+            color[3] = src[3];
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32:
+        {
+            float val = ((const float*)pixels)[offset];
+            uint8_t gray = (uint8_t)(val*255.0f);
+            color[0] = gray;
+            color[1] = gray;
+            color[2] = gray;
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32:
+        {
+            const float *src = &((const float*)pixels)[offset*3];
+            color[0] = (uint8_t)(src[0]*255.0f);
+            color[1] = (uint8_t)(src[1]*255.0f);
+            color[2] = (uint8_t)(src[2]*255.0f);
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
+        {
+            const float *src = &((const float*)pixels)[offset*4];
+            color[0] = (uint8_t)(src[0]*255.0f);
+            color[1] = (uint8_t)(src[1]*255.0f);
+            color[2] = (uint8_t)(src[2]*255.0f);
+            color[3] = (uint8_t)(src[3]*255.0f);
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16:
+        {
+            uint16_t val = ((const uint16_t*)pixels)[offset];
+            uint8_t gray = sw_half_to_float(val)*SW_INV_255;
+            color[0] = gray;
+            color[1] = gray;
+            color[2] = gray;
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16:
+        {
+            const uint16_t *src = &((const uint16_t*)pixels)[offset*3];
+            color[0] = sw_half_to_float(src[0])*SW_INV_255;
+            color[1] = sw_half_to_float(src[1])*SW_INV_255;
+            color[2] = sw_half_to_float(src[2])*SW_INV_255;
+            color[3] = 255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
+        {
+            const uint16_t *src = &((const uint16_t*)pixels)[offset*4];
+            color[0] = sw_half_to_float(src[0])*SW_INV_255;
+            color[1] = sw_half_to_float(src[1])*SW_INV_255;
+            color[2] = sw_half_to_float(src[2])*SW_INV_255;
+            color[3] = sw_half_to_float(src[3])*SW_INV_255;
+            break;
+        }
+        case SW_PIXELFORMAT_UNKNOWN:
+        default:
+        {
+            color[0] = 0;
+            color[1] = 0;
+            color[2] = 0;
+            color[3] = 0;
+            break;
+        }
+    }
+}
+//-------------------------------------------------------------------------------------------
+
+// Texture sampling functionality
+//-------------------------------------------------------------------------------------------
+static inline void sw_texture_fetch(float* color, const sw_texture_t* tex, int x, int y)
+{
+    sw_float_from_unorm8_simd(color, &tex->pixels[4*(y*tex->width + x)]);
+}
+
+static inline void sw_texture_sample_nearest(float *color, const sw_texture_t *tex, float u, float v)
+{
+    u = (tex->sWrap == SW_REPEAT)? sw_fract(u) : sw_saturate(u);
+    v = (tex->tWrap == SW_REPEAT)? sw_fract(v) : sw_saturate(v);
+
+    int x = u*tex->width;
+    int y = v*tex->height;
+
+    sw_texture_fetch(color, tex, x, y);
+}
+
+static inline void sw_texture_sample_linear(float *color, const sw_texture_t *tex, float u, float v)
+{
+    // TODO: With a bit more cleverness we could clearly reduce the
+    // number of operations here, but for now it works fine
+
+    float xf = (u*tex->width) - 0.5f;
+    float yf = (v*tex->height) - 0.5f;
+
+    float fx = sw_fract(xf);
+    float fy = sw_fract(yf);
+
+    int x0 = (int)xf;
+    int y0 = (int)yf;
+
+    int x1 = x0 + 1;
+    int y1 = y0 + 1;
+
+    // NOTE: If the textures are POT we could avoid the division for SW_REPEAT
+
+    if (tex->sWrap == SW_CLAMP)
+    {
+        x0 = (x0 > tex->wMinus1)? tex->wMinus1 : x0;
+        x1 = (x1 > tex->wMinus1)? tex->wMinus1 : x1;
+    }
+    else
+    {
+        x0 = (x0%tex->width + tex->width)%tex->width;
+        x1 = (x1%tex->width + tex->width)%tex->width;
+    }
+
+    if (tex->tWrap == SW_CLAMP)
+    {
+        y0 = (y0 > tex->hMinus1)? tex->hMinus1 : y0;
+        y1 = (y1 > tex->hMinus1)? tex->hMinus1 : y1;
+    }
+    else
+    {
+        y0 = (y0%tex->height + tex->height)%tex->height;
+        y1 = (y1%tex->height + tex->height)%tex->height;
+    }
+
+    float c00[4], c10[4], c01[4], c11[4];
+    sw_texture_fetch(c00, tex, x0, y0);
+    sw_texture_fetch(c10, tex, x1, y0);
+    sw_texture_fetch(c01, tex, x0, y1);
+    sw_texture_fetch(c11, tex, x1, y1);
+
+    for (int i = 0; i < 4; i++)
+    {
+        float t = c00[i] + fx*(c10[i] - c00[i]);
+        float b = c01[i] + fx*(c11[i] - c01[i]);
+        color[i] = t + fy*(b - t);
+    }
+}
+
+static inline void sw_texture_sample(float *color, const sw_texture_t *tex, float u, float v, float dUdx, float dUdy, float dVdx, float dVdy)
+{
+    // Previous method: There is no need to compute the square root
+    // because using the squared value, the comparison remains `L2 > 1.0f*1.0f`
+    //float du = sqrtf(dUdx*dUdx + dUdy*dUdy);
+    //float dv = sqrtf(dVdx*dVdx + dVdy*dVdy);
+    //float L = (du > dv)? du : dv;
+
+    // Calculate the derivatives for each axis
+    float dU2 = dUdx*dUdx + dUdy*dUdy;
+    float dV2 = dVdx*dVdx + dVdy*dVdy;
+    float L2 = (dU2 > dV2)? dU2 : dV2;
+
+    SWfilter filter = (L2 > 1.0f)? tex->minFilter : tex->magFilter;
+
+    switch (filter)
+    {
+        case SW_NEAREST: sw_texture_sample_nearest(color, tex, u, v); break;
+        case SW_LINEAR: sw_texture_sample_linear(color, tex, u, v); break;
+        default: break;
+    }
+}
+//-------------------------------------------------------------------------------------------
+
+// Color blending functionality
+//-------------------------------------------------------------------------------------------
+static inline void sw_factor_zero(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = factor[1] = factor[2] = factor[3] = 0.0f;
+}
+
+static inline void sw_factor_one(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = factor[1] = factor[2] = factor[3] = 1.0f;
+}
+
+static inline void sw_factor_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = src[0]; factor[1] = src[1]; factor[2] = src[2]; factor[3] = src[3];
+}
+
+static inline void sw_factor_one_minus_src_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = 1.0f - src[0]; factor[1] = 1.0f - src[1];
+    factor[2] = 1.0f - src[2]; factor[3] = 1.0f - src[3];
+}
+
+static inline void sw_factor_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = factor[1] = factor[2] = factor[3] = src[3];
+}
+
+static inline void sw_factor_one_minus_src_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    float invAlpha = 1.0f - src[3];
+    factor[0] = factor[1] = factor[2] = factor[3] = invAlpha;
+}
+
+static inline void sw_factor_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = factor[1] = factor[2] = factor[3] = dst[3];
+}
+
+static inline void sw_factor_one_minus_dst_alpha(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    float invAlpha = 1.0f - dst[3];
+    factor[0] = factor[1] = factor[2] = factor[3] = invAlpha;
+}
+
+static inline void sw_factor_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = dst[0]; factor[1] = dst[1]; factor[2] = dst[2]; factor[3] = dst[3];
+}
+
+static inline void sw_factor_one_minus_dst_color(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = 1.0f - dst[0]; factor[1] = 1.0f - dst[1];
+    factor[2] = 1.0f - dst[2]; factor[3] = 1.0f - dst[3];
+}
+
+static inline void sw_factor_src_alpha_saturate(float *SW_RESTRICT factor, const float *SW_RESTRICT src, const float *SW_RESTRICT dst)
+{
+    factor[0] = factor[1] = factor[2] = 1.0f;
+    factor[3] = (src[3] < 1.0f)? src[3] : 1.0f;
+}
+
+static inline void sw_blend_colors(float *SW_RESTRICT dst/*[4]*/, const float *SW_RESTRICT src/*[4]*/)
+{
+    float srcFactor[4], dstFactor[4];
+
+    RLSW.srcFactorFunc(srcFactor, src, dst);
+    RLSW.dstFactorFunc(dstFactor, src, dst);
+
+    dst[0] = srcFactor[0]*src[0] + dstFactor[0]*dst[0];
+    dst[1] = srcFactor[1]*src[1] + dstFactor[1]*dst[1];
+    dst[2] = srcFactor[2]*src[2] + dstFactor[2]*dst[2];
+    dst[3] = srcFactor[3]*src[3] + dstFactor[3]*dst[3];
+}
+//-------------------------------------------------------------------------------------------
+
+// Projection helper functions
+//-------------------------------------------------------------------------------------------
+static inline void sw_project_ndc_to_screen(float screen[2], const float ndc[4])
+{
+    screen[0] = RLSW.vpCenter[0] + ndc[0]*RLSW.vpHalf[0] + 0.5f;
+    screen[1] = RLSW.vpCenter[1] - ndc[1]*RLSW.vpHalf[1] + 0.5f;
+}
+//-------------------------------------------------------------------------------------------
+
+// Polygon clipping management
+//-------------------------------------------------------------------------------------------
+#define DEFINE_CLIP_FUNC(name, FUNC_IS_INSIDE, FUNC_COMPUTE_T)                          \
+static inline int sw_clip_##name(                                                       \
+    sw_vertex_t output[SW_MAX_CLIPPED_POLYGON_VERTICES],                                \
+    const sw_vertex_t input[SW_MAX_CLIPPED_POLYGON_VERTICES],                           \
+    int n)                                                                              \
+{                                                                                       \
+    const sw_vertex_t *prev = &input[n - 1];                                            \
+    int prevInside = FUNC_IS_INSIDE(prev->homogeneous);                                 \
+    int outputCount = 0;                                                                \
+                                                                                        \
+    for (int i = 0; i < n; i++) {                                                       \
+        const sw_vertex_t *curr = &input[i];                                            \
+        int currInside = FUNC_IS_INSIDE(curr->homogeneous);                             \
+                                                                                        \
+        /* If transition between interior/exterior, calculate intersection point */     \
+        if (prevInside != currInside) {                                                 \
+            float t = FUNC_COMPUTE_T(prev->homogeneous, curr->homogeneous);             \
+            sw_lerp_vertex_PTCH(&output[outputCount++], prev, curr, t);                 \
+        }                                                                               \
+                                                                                        \
+        /* If current vertex inside, add it */                                          \
+        if (currInside) {                                                               \
+            output[outputCount++] = *curr;                                              \
+        }                                                                               \
+                                                                                        \
+        prev = curr;                                                                    \
+        prevInside = currInside;                                                        \
+    }                                                                                   \
+                                                                                        \
+    return outputCount;                                                                 \
+}
+//-------------------------------------------------------------------------------------------
+
+// Frustum cliping functions
+//-------------------------------------------------------------------------------------------
+#define IS_INSIDE_PLANE_W(h) ((h)[3] >= SW_CLIP_EPSILON)
+#define IS_INSIDE_PLANE_X_POS(h) ( (h)[0] <  (h)[3])        // Exclusive for +X
+#define IS_INSIDE_PLANE_X_NEG(h) (-(h)[0] <  (h)[3])        // Exclusive for -X
+#define IS_INSIDE_PLANE_Y_POS(h) ( (h)[1] <  (h)[3])        // Exclusive for +Y
+#define IS_INSIDE_PLANE_Y_NEG(h) (-(h)[1] <  (h)[3])        // Exclusive for -Y
+#define IS_INSIDE_PLANE_Z_POS(h) ( (h)[2] <= (h)[3])        // Inclusive for +Z
+#define IS_INSIDE_PLANE_Z_NEG(h) (-(h)[2] <= (h)[3])        // Inclusive for -Z
+
+#define COMPUTE_T_PLANE_W(hPrev, hCurr) ((SW_CLIP_EPSILON - (hPrev)[3])/((hCurr)[3] - (hPrev)[3]))
+#define COMPUTE_T_PLANE_X_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[0])/(((hPrev)[3] - (hPrev)[0]) - ((hCurr)[3] - (hCurr)[0])))
+#define COMPUTE_T_PLANE_X_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[0])/(((hPrev)[3] + (hPrev)[0]) - ((hCurr)[3] + (hCurr)[0])))
+#define COMPUTE_T_PLANE_Y_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[1])/(((hPrev)[3] - (hPrev)[1]) - ((hCurr)[3] - (hCurr)[1])))
+#define COMPUTE_T_PLANE_Y_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[1])/(((hPrev)[3] + (hPrev)[1]) - ((hCurr)[3] + (hCurr)[1])))
+#define COMPUTE_T_PLANE_Z_POS(hPrev, hCurr) (((hPrev)[3] - (hPrev)[2])/(((hPrev)[3] - (hPrev)[2]) - ((hCurr)[3] - (hCurr)[2])))
+#define COMPUTE_T_PLANE_Z_NEG(hPrev, hCurr) (((hPrev)[3] + (hPrev)[2])/(((hPrev)[3] + (hPrev)[2]) - ((hCurr)[3] + (hCurr)[2])))
+
+DEFINE_CLIP_FUNC(w, IS_INSIDE_PLANE_W, COMPUTE_T_PLANE_W)
+DEFINE_CLIP_FUNC(x_pos, IS_INSIDE_PLANE_X_POS, COMPUTE_T_PLANE_X_POS)
+DEFINE_CLIP_FUNC(x_neg, IS_INSIDE_PLANE_X_NEG, COMPUTE_T_PLANE_X_NEG)
+DEFINE_CLIP_FUNC(y_pos, IS_INSIDE_PLANE_Y_POS, COMPUTE_T_PLANE_Y_POS)
+DEFINE_CLIP_FUNC(y_neg, IS_INSIDE_PLANE_Y_NEG, COMPUTE_T_PLANE_Y_NEG)
+DEFINE_CLIP_FUNC(z_pos, IS_INSIDE_PLANE_Z_POS, COMPUTE_T_PLANE_Z_POS)
+DEFINE_CLIP_FUNC(z_neg, IS_INSIDE_PLANE_Z_NEG, COMPUTE_T_PLANE_Z_NEG)
+//-------------------------------------------------------------------------------------------
+
+// Scissor clip functions
+//-------------------------------------------------------------------------------------------
+#define COMPUTE_T_SCISSOR_X_MIN(hPrev, hCurr) (((RLSW.scClipMin[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMin[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMin[0])*(hPrev)[3])))
+#define COMPUTE_T_SCISSOR_X_MAX(hPrev, hCurr) (((RLSW.scClipMax[0])*(hPrev)[3] - (hPrev)[0])/(((hCurr)[0] - (RLSW.scClipMax[0])*(hCurr)[3]) - ((hPrev)[0] - (RLSW.scClipMax[0])*(hPrev)[3])))
+#define COMPUTE_T_SCISSOR_Y_MIN(hPrev, hCurr) (((RLSW.scClipMin[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMin[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMin[1])*(hPrev)[3])))
+#define COMPUTE_T_SCISSOR_Y_MAX(hPrev, hCurr) (((RLSW.scClipMax[1])*(hPrev)[3] - (hPrev)[1])/(((hCurr)[1] - (RLSW.scClipMax[1])*(hCurr)[3]) - ((hPrev)[1] - (RLSW.scClipMax[1])*(hPrev)[3])))
+
+#define IS_INSIDE_SCISSOR_X_MIN(h) ((h)[0] >= (RLSW.scClipMin[0])*(h)[3])
+#define IS_INSIDE_SCISSOR_X_MAX(h) ((h)[0] <= (RLSW.scClipMax[0])*(h)[3])
+#define IS_INSIDE_SCISSOR_Y_MIN(h) ((h)[1] >= (RLSW.scClipMin[1])*(h)[3])
+#define IS_INSIDE_SCISSOR_Y_MAX(h) ((h)[1] <= (RLSW.scClipMax[1])*(h)[3])
+
+DEFINE_CLIP_FUNC(scissor_x_min, IS_INSIDE_SCISSOR_X_MIN, COMPUTE_T_SCISSOR_X_MIN)
+DEFINE_CLIP_FUNC(scissor_x_max, IS_INSIDE_SCISSOR_X_MAX, COMPUTE_T_SCISSOR_X_MAX)
+DEFINE_CLIP_FUNC(scissor_y_min, IS_INSIDE_SCISSOR_Y_MIN, COMPUTE_T_SCISSOR_Y_MIN)
+DEFINE_CLIP_FUNC(scissor_y_max, IS_INSIDE_SCISSOR_Y_MAX, COMPUTE_T_SCISSOR_Y_MAX)
+//-------------------------------------------------------------------------------------------
+
+// Main polygon clip function
+static inline bool sw_polygon_clip(sw_vertex_t polygon[SW_MAX_CLIPPED_POLYGON_VERTICES], int *vertexCounter)
+{
+    static sw_vertex_t tmp[SW_MAX_CLIPPED_POLYGON_VERTICES];
+
+    int n = *vertexCounter;
+
+    #define CLIP_AGAINST_PLANE(FUNC_CLIP)                       \
+    {                                                           \
+        n = FUNC_CLIP(tmp, polygon, n);                         \
+        if (n < 3)                                              \
+        {                                                       \
+            *vertexCounter = 0;                                 \
+            return false;                                       \
+        }                                                       \
+        for (int i = 0; i < n; i++) polygon[i] = tmp[i];        \
+    }
+
+    CLIP_AGAINST_PLANE(sw_clip_w);
+    CLIP_AGAINST_PLANE(sw_clip_x_pos);
+    CLIP_AGAINST_PLANE(sw_clip_x_neg);
+    CLIP_AGAINST_PLANE(sw_clip_y_pos);
+    CLIP_AGAINST_PLANE(sw_clip_y_neg);
+    CLIP_AGAINST_PLANE(sw_clip_z_pos);
+    CLIP_AGAINST_PLANE(sw_clip_z_neg);
+
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        CLIP_AGAINST_PLANE(sw_clip_scissor_x_min);
+        CLIP_AGAINST_PLANE(sw_clip_scissor_x_max);
+        CLIP_AGAINST_PLANE(sw_clip_scissor_y_min);
+        CLIP_AGAINST_PLANE(sw_clip_scissor_y_max);
+    }
+
+    *vertexCounter = n;
+
+    return (n >= 3);
+}
+
+// Triangle rendering logic
+//-------------------------------------------------------------------------------------------
+static inline bool sw_triangle_face_culling(void)
+{
+    // NOTE: Face culling is done before clipping to avoid unnecessary computations
+    //       To handle triangles crossing the w=0 plane correctly,
+    //       we perform the winding order test in homogeneous coordinates directly,
+    //       before the perspective division (division by w)
+    //       This test determines the orientation of the triangle in the (x,y,w) plane,
+    //       which corresponds to the projected 2D winding order sign,
+    //       even with negative w values
+
+    // Preload homogeneous coordinates into local variables
+    const float *h0 = RLSW.vertexBuffer[0].homogeneous;
+    const float *h1 = RLSW.vertexBuffer[1].homogeneous;
+    const float *h2 = RLSW.vertexBuffer[2].homogeneous;
+
+    // Compute a value proportional to the signed area in the projected 2D plane,
+    // calculated directly using homogeneous coordinates BEFORE division by w
+    // This is the determinant of the matrix formed by the (x, y, w) components
+    // of the vertices, which correctly captures the winding order in homogeneous
+    // space and its relationship to the projected 2D winding order, even with
+    // negative w values
+    // The determinant formula used here is:
+    // h0.x*(h1.y*h2.w - h2.y*h1.w) +
+    // h1.x*(h2.y*h0.w - h0.y*h2.w) +
+    // h2.x*(h0.y*h1.w - h1.y*h0.w)
+
+    const float hSgnArea =
+        h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) +
+        h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) +
+        h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]);
+
+    // Discard the triangle if its winding order (determined by the sign
+    // of the homogeneous area/determinant) matches the culled direction
+    // A positive hSgnArea typically corresponds to a counter-clockwise
+    // winding in the projected space when all w > 0
+    // This test is robust for points with w > 0 or w < 0, correctly
+    // capturing the change in orientation when crossing the w=0 plane
+
+    // The culling logic remains the same based on the signed area/determinant
+    // A value of 0 for hSgnArea means the points are collinear in (x, y, w)
+    // space, which corresponds to a degenerate triangle projection
+    // Such triangles are typically not culled by this test (0 < 0 is false, 0 > 0 is false)
+    // and should be handled by the clipper if necessary
+    return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0) : (hSgnArea > 0); // Cull if winding is "clockwise" : "counter-clockwise"
+}
+
+static inline void sw_triangle_clip_and_project(void)
+{
+    sw_vertex_t *polygon = RLSW.vertexBuffer;
+    int *vertexCounter = &RLSW.vertexCounter;
+
+    if (sw_polygon_clip(polygon, vertexCounter))
+    {
+        // Transformation to screen space and normalization
+        for (int i = 0; i < *vertexCounter; i++)
+        {
+            sw_vertex_t *v = &polygon[i];
+
+            // Calculation of the reciprocal of W for normalization
+            // as well as perspective-correct attributes
+            const float wRcp = 1.0f/v->homogeneous[3];
+            v->homogeneous[3] = wRcp;
+
+            // Division of XYZ coordinates by weight
+            v->homogeneous[0] *= wRcp;
+            v->homogeneous[1] *= wRcp;
+            v->homogeneous[2] *= wRcp;
+
+            // Division of texture coordinates (perspective-correct)
+            v->texcoord[0] *= wRcp;
+            v->texcoord[1] *= wRcp;
+
+            // Division of colors (perspective-correct)
+            v->color[0] *= wRcp;
+            v->color[1] *= wRcp;
+            v->color[2] *= wRcp;
+            v->color[3] *= wRcp;
+
+            // Transformation to screen space
+            sw_project_ndc_to_screen(v->screen, v->homogeneous);
+        }
+    }
+}
+
+#define DEFINE_TRIANGLE_RASTER_SCANLINE(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \
+static inline void FUNC_NAME(const sw_texture_t *tex, const sw_vertex_t *start,     \
+                             const sw_vertex_t *end, float dUdy, float dVdy)        \
+{                                                                                   \
+    /* Gets the start and end coordinates */                                        \
+    int xStart = (int)start->screen[0];                                             \
+    int xEnd   = (int)end->screen[0];                                               \
+                                                                                    \
+    /* Avoid empty lines */                                                         \
+    if (xStart == xEnd) return;                                                     \
+                                                                                    \
+    /* Compute the subpixel distance to traverse before the first pixel */          \
+    float xSubstep = 1.0f - sw_fract(start->screen[0]);                             \
+                                                                                    \
+    /* Compute the inverse horizontal distance along the X axis */                  \
+    float dxRcp = 1.0f/(end->screen[0] - start->screen[0]);                         \
+                                                                                    \
+    /* Compute the interpolation steps along the X axis */                          \
+    float dZdx = (end->homogeneous[2] - start->homogeneous[2])*dxRcp;               \
+    float dWdx = (end->homogeneous[3] - start->homogeneous[3])*dxRcp;               \
+                                                                                    \
+    float dCdx[4] = {                                                               \
+        (end->color[0] - start->color[0])*dxRcp,                                    \
+        (end->color[1] - start->color[1])*dxRcp,                                    \
+        (end->color[2] - start->color[2])*dxRcp,                                    \
+        (end->color[3] - start->color[3])*dxRcp                                     \
+    };                                                                              \
+                                                                                    \
+    float dUdx = 0.0f;                                                              \
+    float dVdx = 0.0f;                                                              \
+    if (ENABLE_TEXTURE) {                                                           \
+        dUdx = (end->texcoord[0] - start->texcoord[0])*dxRcp;                       \
+        dVdx = (end->texcoord[1] - start->texcoord[1])*dxRcp;                       \
+    }                                                                               \
+                                                                                    \
+    /* Initializing the interpolation starting values  */                           \
+    float z = start->homogeneous[2] + dZdx*xSubstep;                                \
+    float w = start->homogeneous[3] + dWdx*xSubstep;                                \
+                                                                                    \
+    float color[4] = {                                                              \
+        start->color[0] + dCdx[0]*xSubstep,                                         \
+        start->color[1] + dCdx[1]*xSubstep,                                         \
+        start->color[2] + dCdx[2]*xSubstep,                                         \
+        start->color[3] + dCdx[3]*xSubstep                                          \
+    };                                                                              \
+                                                                                    \
+    float u = 0.0f;                                                                 \
+    float v = 0.0f;                                                                 \
+    if (ENABLE_TEXTURE) {                                                           \
+        u = start->texcoord[0] + dUdx*xSubstep;                                     \
+        v = start->texcoord[1] + dVdx*xSubstep;                                     \
+    }                                                                               \
+                                                                                    \
+    /* Pre-calculate the starting pointers for the framebuffer row */               \
+    int y = (int)start->screen[1];                                                  \
+    sw_pixel_t *ptr = RLSW.framebuffer.pixels + y*RLSW.framebuffer.width + xStart;  \
+                                                                                    \
+    /* Scanline rasterization */                                                    \
+    for (int x = xStart; x < xEnd; x++)                                             \
+    {                                                                               \
+        float wRcp = 1.0f/w;                                                        \
+        float srcColor[4] = {                                                       \
+            color[0]*wRcp,                                                          \
+            color[1]*wRcp,                                                          \
+            color[2]*wRcp,                                                          \
+            color[3]*wRcp                                                           \
+        };                                                                          \
+                                                                                    \
+        if (ENABLE_DEPTH_TEST)                                                      \
+        {                                                                           \
+            /* TODO: Implement different depth funcs? */                            \
+            float depth =  sw_framebuffer_read_depth(ptr);                          \
+            if (z > depth) goto discard;                                            \
+        }                                                                           \
+                                                                                    \
+        /* TODO: Implement depth mask */                                            \
+        sw_framebuffer_write_depth(ptr, z);                                         \
+                                                                                    \
+        if (ENABLE_TEXTURE)                                                         \
+        {                                                                           \
+            float texColor[4];                                                      \
+            float s = u*wRcp;                                                       \
+            float t = v*wRcp;                                                       \
+            sw_texture_sample(texColor, tex, s, t, dUdx, dUdy, dVdx, dVdy);         \
+            srcColor[0] *= texColor[0];                                             \
+            srcColor[1] *= texColor[1];                                             \
+            srcColor[2] *= texColor[2];                                             \
+            srcColor[3] *= texColor[3];                                             \
+        }                                                                           \
+                                                                                    \
+        if (ENABLE_COLOR_BLEND)                                                     \
+        {                                                                           \
+            float dstColor[4];                                                      \
+            sw_framebuffer_read_color(dstColor, ptr);                               \
+            sw_blend_colors(dstColor, srcColor);                                    \
+            sw_framebuffer_write_color(ptr, dstColor);                              \
+        }                                                                           \
+        else                                                                        \
+        {                                                                           \
+            sw_framebuffer_write_color(ptr, srcColor);                              \
+        }                                                                           \
+                                                                                    \
+        /* Increment the interpolation parameter, UVs, and pointers */              \
+    discard:                                                                        \
+        z += dZdx;                                                                  \
+        w += dWdx;                                                                  \
+        color[0] += dCdx[0];                                                        \
+        color[1] += dCdx[1];                                                        \
+        color[2] += dCdx[2];                                                        \
+        color[3] += dCdx[3];                                                        \
+        if (ENABLE_TEXTURE)                                                         \
+        {                                                                           \
+            u += dUdx;                                                              \
+            v += dVdx;                                                              \
+        }                                                                           \
+        ++ptr;                                                                      \
+    }                                                                               \
+}
+
+#define DEFINE_TRIANGLE_RASTER(FUNC_NAME, FUNC_SCANLINE, ENABLE_TEXTURE)            \
+static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1,          \
+                             const sw_vertex_t *v2, const sw_texture_t *tex)        \
+{                                                                                   \
+    /* Swap vertices by increasing y */                                             \
+    if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \
+    if (v1->screen[1] > v2->screen[1]) { const sw_vertex_t *tmp = v1; v1 = v2; v2 = tmp; } \
+    if (v0->screen[1] > v1->screen[1]) { const sw_vertex_t *tmp = v0; v0 = v1; v1 = tmp; } \
+                                                                                    \
+    /* Extracting coordinates from the sorted vertices */                           \
+    float x0 = v0->screen[0], y0 = v0->screen[1];                                   \
+    float x1 = v1->screen[0], y1 = v1->screen[1];                                   \
+    float x2 = v2->screen[0], y2 = v2->screen[1];                                   \
+                                                                                    \
+    /* Compute height differences */                                                \
+    float h02 = y2 - y0;                                                            \
+    float h01 = y1 - y0;                                                            \
+    float h12 = y2 - y1;                                                            \
+                                                                                    \
+    if (h02 < 1e-6f) return;                                                        \
+                                                                                    \
+    /* Precompute the inverse values without additional checks */                   \
+    float h02Rcp = 1.0f/h02;                                                        \
+    float h01Rcp = (h01 > 1e-6f)? 1.0f/h01 : 0.0f;                                  \
+    float h12Rcp = (h12 > 1e-6f)? 1.0f/h12 : 0.0f;                                  \
+                                                                                    \
+    /* Pre-calculation of slopes */                                                 \
+    float dXdy02 = (x2 - x0)*h02Rcp;                                                \
+    float dXdy01 = (x1 - x0)*h01Rcp;                                                \
+    float dXdy12 = (x2 - x1)*h12Rcp;                                                \
+                                                                                    \
+    /* Y subpixel correction */                                                     \
+    float y0Substep = 1.0f - sw_fract(y0);                                          \
+    float y1Substep = 1.0f - sw_fract(y1);                                          \
+                                                                                    \
+    /* Y bounds (vertical clipping) */                                              \
+    int yTop = (int)y0;                                                             \
+    int yMid = (int)y1;                                                             \
+    int yBot = (int)y2;                                                             \
+                                                                                    \
+    /* Compute gradients for each side of the triangle */                           \
+    sw_vertex_t dVXdy02, dVXdy01, dVXdy12;                                          \
+    sw_get_vertex_grad_PTCH(&dVXdy02, v0, v2, h02Rcp);                              \
+    sw_get_vertex_grad_PTCH(&dVXdy01, v0, v1, h01Rcp);                              \
+    sw_get_vertex_grad_PTCH(&dVXdy12, v1, v2, h12Rcp);                              \
+                                                                                    \
+    /* Get a copy of vertices for interpolation and apply substep correction */     \
+    sw_vertex_t vLeft = *v0, vRight = *v0;                                          \
+    sw_add_vertex_grad_scaled_PTCH(&vLeft, &dVXdy02, y0Substep);                    \
+    sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy01, y0Substep);                   \
+                                                                                    \
+    vLeft.screen[0] += dXdy02*y0Substep;                                            \
+    vRight.screen[0] += dXdy01*y0Substep;                                           \
+                                                                                    \
+    /* Scanline for the upper part of the triangle */                               \
+    for (int y = yTop; y < yMid; y++)                                               \
+    {                                                                               \
+        vLeft.screen[1] = vRight.screen[1] = y;                                     \
+                                                                                    \
+        if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \
+        else FUNC_SCANLINE(tex, &vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \
+                                                                                    \
+        sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02);                                  \
+        vLeft.screen[0] += dXdy02;                                                  \
+                                                                                    \
+        sw_add_vertex_grad_PTCH(&vRight, &dVXdy01);                                 \
+        vRight.screen[0] += dXdy01;                                                 \
+    }                                                                               \
+                                                                                    \
+    /* Get a copy of next right for interpolation and apply substep correction */   \
+    vRight = *v1;                                                                   \
+    sw_add_vertex_grad_scaled_PTCH(&vRight, &dVXdy12, y1Substep);                   \
+    vRight.screen[0] += dXdy12*y1Substep;                                           \
+                                                                                    \
+    /* Scanline for the lower part of the triangle */                               \
+    for (int y = yMid; y < yBot; y++)                                               \
+    {                                                                               \
+        vLeft.screen[1] = vRight.screen[1] = y;                                     \
+                                                                                    \
+        if (vLeft.screen[0] < vRight.screen[0]) FUNC_SCANLINE(tex, &vLeft, &vRight, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \
+        else FUNC_SCANLINE(tex, &vRight, &vLeft, dVXdy02.texcoord[0], dVXdy02.texcoord[1]); \
+                                                                                    \
+        sw_add_vertex_grad_PTCH(&vLeft, &dVXdy02);                                  \
+        vLeft.screen[0] += dXdy02;                                                  \
+                                                                                    \
+        sw_add_vertex_grad_PTCH(&vRight, &dVXdy12);                                 \
+        vRight.screen[0] += dXdy12;                                                 \
+    }                                                                               \
+}
+
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline, 0, 0, 0)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX, 1, 0, 0)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH, 0, 1, 0)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_BLEND, 0, 0, 1)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH, 1, 1, 0)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_BLEND, 1, 0, 1)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_DEPTH_BLEND, 0, 1, 1)
+DEFINE_TRIANGLE_RASTER_SCANLINE(sw_triangle_raster_scanline_TEX_DEPTH_BLEND, 1, 1, 1)
+
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster, sw_triangle_raster_scanline, false)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX, sw_triangle_raster_scanline_TEX, true)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH, sw_triangle_raster_scanline_DEPTH, false)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_BLEND, sw_triangle_raster_scanline_BLEND, false)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH, sw_triangle_raster_scanline_TEX_DEPTH, true)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND, sw_triangle_raster_scanline_TEX_BLEND, true)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND, sw_triangle_raster_scanline_DEPTH_BLEND, false)
+DEFINE_TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND, sw_triangle_raster_scanline_TEX_DEPTH_BLEND, true)
+
+static inline void sw_triangle_render(void)
+{
+    if (RLSW.stateFlags & SW_STATE_CULL_FACE)
+    {
+        if (!sw_triangle_face_culling()) return;
+    }
+
+    sw_triangle_clip_and_project();
+
+    if (RLSW.vertexCounter < 3) return;
+
+    #define TRIANGLE_RASTER(RASTER_FUNC)                        \
+    {                                                           \
+        for (int i = 0; i < RLSW.vertexCounter - 2; i++)        \
+        {                                                       \
+            RASTER_FUNC(                                        \
+                &RLSW.vertexBuffer[0],                          \
+                &RLSW.vertexBuffer[i + 1],                      \
+                &RLSW.vertexBuffer[i + 2],                      \
+                &RLSW.loadedTextures[RLSW.currentTexture]       \
+            );                                                  \
+        }                                                       \
+    }
+
+    uint32_t state = RLSW.stateFlags;
+    if (RLSW.currentTexture == 0) state &= ~SW_STATE_TEXTURE_2D;
+    if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND;
+
+    if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX)
+    else TRIANGLE_RASTER(sw_triangle_raster)
+
+    #undef TRIANGLE_RASTER
+}
+//-------------------------------------------------------------------------------------------
+
+// Quad rendering logic
+//-------------------------------------------------------------------------------------------
+static inline bool sw_quad_face_culling(void)
+{
+    // NOTE: Face culling is done before clipping to avoid unnecessary computations
+    //       To handle quads crossing the w=0 plane correctly,
+    //       we perform the winding order test in homogeneous coordinates directly,
+    //       before the perspective division (division by w)
+    //       For a convex quad with vertices P0, P1, P2, P3 in sequential order,
+    //       the winding order of the quad is the same as the winding order
+    //       of the triangle P0 P1 P2. We use the homogeneous triangle
+    //       winding test on this first triangle
+
+    // Preload homogeneous coordinates into local variables
+    const float *h0 = RLSW.vertexBuffer[0].homogeneous;
+    const float *h1 = RLSW.vertexBuffer[1].homogeneous;
+    const float *h2 = RLSW.vertexBuffer[2].homogeneous;
+
+    // NOTE: h3 is not needed for this test
+    // const float *h3 = RLSW.vertexBuffer[3].homogeneous;
+
+    // Compute a value proportional to the signed area of the triangle P0 P1 P2
+    // in the projected 2D plane, calculated directly using homogeneous coordinates
+    // BEFORE division by w
+    // This is the determinant of the matrix formed by the (x, y, w) components
+    // of the vertices P0, P1, and P2. Its sign correctly indicates the winding order
+    // in homogeneous space and its relationship to the projected 2D winding order,
+    // even with negative w values
+    // The determinant formula used here is:
+    // h0.x*(h1.y*h2.w - h2.y*h1.w) +
+    // h1.x*(h2.y*h0.w - h0.y*h2.w) +
+    // h2.x*(h0.y*h1.w - h1.y*h0.w)
+
+    const float hSgnArea =
+        h0[0]*(h1[1]*h2[3] - h2[1]*h1[3]) +
+        h1[0]*(h2[1]*h0[3] - h0[1]*h2[3]) +
+        h2[0]*(h0[1]*h1[3] - h1[1]*h0[3]);
+
+    // Perform face culling based on the winding order determined by the sign
+    // of the homogeneous area/determinant of triangle P0 P1 P2
+    // This test is robust for points with w > 0 or w < 0 within the triangle,
+    // correctly capturing the change in orientation when crossing the w=0 plane
+
+    // A positive hSgnArea typically corresponds to a counter-clockwise
+    // winding in the projected space when all w > 0
+    // A value of 0 for hSgnArea means P0, P1, P2 are collinear in (x, y, w)
+    // space, which corresponds to a degenerate triangle projection
+    // Such quads might also be degenerate or non-planar. They are typically
+    // not culled by this test (0 < 0 is false, 0 > 0 is false)
+    // and should be handled by the clipper if necessary
+
+    return (RLSW.cullFace == SW_FRONT)? (hSgnArea < 0.0f) : (hSgnArea > 0.0f); // Cull if winding is "clockwise" : "counter-clockwise"
+}
+
+static inline void sw_quad_clip_and_project(void)
+{
+    sw_vertex_t *polygon = RLSW.vertexBuffer;
+    int *vertexCounter = &RLSW.vertexCounter;
+
+    if (sw_polygon_clip(polygon, vertexCounter))
+    {
+        // Transformation to screen space and normalization
+        for (int i = 0; i < *vertexCounter; i++)
+        {
+            sw_vertex_t *v = &polygon[i];
+
+            // Calculation of the reciprocal of W for normalization
+            // as well as perspective-correct attributes
+            const float wRcp = 1.0f/v->homogeneous[3];
+            v->homogeneous[3] = wRcp;
+
+            // Division of XYZ coordinates by weight
+            v->homogeneous[0] *= wRcp;
+            v->homogeneous[1] *= wRcp;
+            v->homogeneous[2] *= wRcp;
+
+            // Division of texture coordinates (perspective-correct)
+            v->texcoord[0] *= wRcp;
+            v->texcoord[1] *= wRcp;
+
+            // Division of colors (perspective-correct)
+            v->color[0] *= wRcp;
+            v->color[1] *= wRcp;
+            v->color[2] *= wRcp;
+            v->color[3] *= wRcp;
+
+            // Transformation to screen space
+            sw_project_ndc_to_screen(v->screen, v->homogeneous);
+        }
+    }
+}
+
+static inline bool sw_quad_is_axis_aligned(void)
+{
+    // Reject quads with perspective projection
+    // The fast path assumes affine (non-perspective) quads,
+    // so we require all vertices to have homogeneous w = 1.0
+    for (int i = 0; i < 4; i++)
+    {
+        if (RLSW.vertexBuffer[i].homogeneous[3] != 1.0f) return false;
+    }
+
+    // Epsilon tolerance in screen space (pixels)
+    const float epsilon = 0.5f;
+
+    // Fetch screen-space positions for the four quad vertices
+    const float *p0 = RLSW.vertexBuffer[0].screen;
+    const float *p1 = RLSW.vertexBuffer[1].screen;
+    const float *p2 = RLSW.vertexBuffer[2].screen;
+    const float *p3 = RLSW.vertexBuffer[3].screen;
+
+    // Compute edge vectors between consecutive vertices
+    // These define the four sides of the quad in screen space
+    float dx01 = p1[0] - p0[0], dy01 = p1[1] - p0[1];
+    float dx12 = p2[0] - p1[0], dy12 = p2[1] - p1[1];
+    float dx23 = p3[0] - p2[0], dy23 = p3[1] - p2[1];
+    float dx30 = p0[0] - p3[0], dy30 = p0[1] - p3[1];
+
+    // Each edge must be either horizontal or vertical within epsilon tolerance
+    // If any edge deviates significantly from either axis, the quad is not axis-aligned
+    if (!((fabsf(dy01) < epsilon) || (fabsf(dx01) < epsilon))) return false;
+    if (!((fabsf(dy12) < epsilon) || (fabsf(dx12) < epsilon))) return false;
+    if (!((fabsf(dy23) < epsilon) || (fabsf(dx23) < epsilon))) return false;
+    if (!((fabsf(dy30) < epsilon) || (fabsf(dx30) < epsilon))) return false;
+
+    return true;
+}
+
+static inline void sw_quad_sort_cw(const sw_vertex_t* *output)
+{
+    const sw_vertex_t *input = RLSW.vertexBuffer;
+
+    // Calculate the centroid of the quad
+    float cx = (input[0].screen[0] + input[1].screen[0] +
+                input[2].screen[0] + input[3].screen[0])*0.25f;
+    float cy = (input[0].screen[1] + input[1].screen[1] +
+                input[2].screen[1] + input[3].screen[1])*0.25f;
+
+    // Calculate the angle of each vertex relative to the center
+    // and assign them directly to their correct position
+    const sw_vertex_t *corners[4] = { 0 };
+
+    for (int i = 0; i < 4; i++)
+    {
+        float dx = input[i].screen[0] - cx;
+        float dy = input[i].screen[1] - cy;
+
+        // Determine the quadrant (clockwise from top-left)
+        // top-left: dx < 0, dy < 0
+        // top-right: dx >= 0, dy < 0
+        // bottom-right: dx >= 0, dy >= 0
+        // bottom-left: dx < 0, dy >= 0
+
+        int idx;
+        if (dy < 0) idx = (dx < 0)? 0 : 1; // Top row
+        else idx = (dx < 0)? 3 : 2; // Bottom row
+
+        corners[idx] = &input[i];
+    }
+
+    output[0] = corners[0];  // top-left
+    output[1] = corners[1];  // top-right
+    output[2] = corners[2];  // bottom-right
+    output[3] = corners[3];  // bottom-left
+}
+
+// TODO: REVIEW: Could a perfectly aligned quad, where one of the four points has a different depth,
+// still appear perfectly aligned from a certain point of view?
+// Because in that case, we would still need to perform perspective division for textures and colors...
+#define DEFINE_QUAD_RASTER_AXIS_ALIGNED(FUNC_NAME, ENABLE_TEXTURE, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \
+static inline void FUNC_NAME(void)                                              \
+{                                                                               \
+    const sw_vertex_t *sortedVerts[4];                                          \
+    sw_quad_sort_cw(sortedVerts);                                               \
+                                                                                \
+    const sw_vertex_t *v0 = sortedVerts[0];                                     \
+    const sw_vertex_t *v1 = sortedVerts[1];                                     \
+    const sw_vertex_t *v2 = sortedVerts[2];                                     \
+    const sw_vertex_t *v3 = sortedVerts[3];                                     \
+                                                                                \
+    /* Screen bounds (axis-aligned) */                                          \
+    int xMin = (int)v0->screen[0];                                              \
+    int yMin = (int)v0->screen[1];                                              \
+    int xMax = (int)v2->screen[0];                                              \
+    int yMax = (int)v2->screen[1];                                              \
+                                                                                \
+    float w = v2->screen[0] - v0->screen[0];                                    \
+    float h = v2->screen[1] - v0->screen[1];                                    \
+                                                                                \
+    if ((w == 0) || (h == 0)) return;                                           \
+                                                                                \
+    float wRcp = (w > 0.0f)? 1.0f/w : 0.0f;                                     \
+    float hRcp = (h > 0.0f)? 1.0f/h : 0.0f;                                     \
+                                                                                \
+    /* Subpixel corrections */                                                  \
+    float xSubstep = 1.0f - sw_fract(v0->screen[0]);                            \
+    float ySubstep = 1.0f - sw_fract(v0->screen[1]);                            \
+                                                                                \
+    /* Calculation of vertex gradients in X and Y */                            \
+    float dUdx = 0.0f, dVdx = 0.0f;                                             \
+    float dUdy = 0.0f, dVdy = 0.0f;                                             \
+    if (ENABLE_TEXTURE) {                                                       \
+        dUdx = (v1->texcoord[0] - v0->texcoord[0])*wRcp;                        \
+        dVdx = (v1->texcoord[1] - v0->texcoord[1])*wRcp;                        \
+        dUdy = (v3->texcoord[0] - v0->texcoord[0])*hRcp;                        \
+        dVdy = (v3->texcoord[1] - v0->texcoord[1])*hRcp;                        \
+    }                                                                           \
+                                                                                \
+    float dCdx[4], dCdy[4];                                                     \
+    dCdx[0] = (v1->color[0] - v0->color[0])*wRcp;                               \
+    dCdx[1] = (v1->color[1] - v0->color[1])*wRcp;                               \
+    dCdx[2] = (v1->color[2] - v0->color[2])*wRcp;                               \
+    dCdx[3] = (v1->color[3] - v0->color[3])*wRcp;                               \
+    dCdy[0] = (v3->color[0] - v0->color[0])*hRcp;                               \
+    dCdy[1] = (v3->color[1] - v0->color[1])*hRcp;                               \
+    dCdy[2] = (v3->color[2] - v0->color[2])*hRcp;                               \
+    dCdy[3] = (v3->color[3] - v0->color[3])*hRcp;                               \
+                                                                                \
+    float dZdx, dZdy;                                                           \
+    dZdx = (v1->homogeneous[2] - v0->homogeneous[2])*wRcp;                      \
+    dZdy = (v3->homogeneous[2] - v0->homogeneous[2])*hRcp;                      \
+                                                                                \
+    /* Start of quad rasterization */                                           \
+    const sw_texture_t *tex;                                                    \
+    if (ENABLE_TEXTURE) tex = &RLSW.loadedTextures[RLSW.currentTexture];        \
+                                                                                \
+    sw_pixel_t *pixels = RLSW.framebuffer.pixels;                               \
+    int wDst = RLSW.framebuffer.width;                                          \
+                                                                                \
+    float zScanline = v0->homogeneous[2] + dZdx*xSubstep + dZdy*ySubstep;       \
+    float uScanline = v0->texcoord[0] + dUdx*xSubstep + dUdy*ySubstep;          \
+    float vScanline = v0->texcoord[1] + dVdx*xSubstep + dVdy*ySubstep;          \
+                                                                                \
+    float colorScanline[4] = {                                                  \
+        v0->color[0] + dCdx[0]*xSubstep + dCdy[0]*ySubstep,                     \
+        v0->color[1] + dCdx[1]*xSubstep + dCdy[1]*ySubstep,                     \
+        v0->color[2] + dCdx[2]*xSubstep + dCdy[2]*ySubstep,                     \
+        v0->color[3] + dCdx[3]*xSubstep + dCdy[3]*ySubstep                      \
+    };                                                                          \
+                                                                                \
+    for (int y = yMin; y < yMax; y++)                                           \
+    {                                                                           \
+        sw_pixel_t *ptr = pixels + y*wDst + xMin;                               \
+                                                                                \
+        float z = zScanline;                                                    \
+        float u = uScanline;                                                    \
+        float v = vScanline;                                                    \
+                                                                                \
+        float color[4] = {                                                      \
+            colorScanline[0],                                                   \
+            colorScanline[1],                                                   \
+            colorScanline[2],                                                   \
+            colorScanline[3]                                                    \
+        };                                                                      \
+                                                                                \
+        /* Scanline rasterization */                                            \
+        for (int x = xMin; x < xMax; x++)                                       \
+        {                                                                       \
+            /* Pixel color computation */                                       \
+            float srcColor[4] = {                                               \
+                color[0],                                                       \
+                color[1],                                                       \
+                color[2],                                                       \
+                color[3]                                                        \
+            };                                                                  \
+                                                                                \
+            /* Test and write depth */                                          \
+            if (ENABLE_DEPTH_TEST)                                              \
+            {                                                                   \
+                /* TODO: Implement different depth funcs? */                    \
+                float depth =  sw_framebuffer_read_depth(ptr);                  \
+                if (z > depth) goto discard;                                    \
+            }                                                                   \
+                                                                                \
+            /* TODO: Implement depth mask */                                    \
+            sw_framebuffer_write_depth(ptr, z);                                 \
+                                                                                \
+            if (ENABLE_TEXTURE)                                                 \
+            {                                                                   \
+                float texColor[4];                                              \
+                sw_texture_sample(texColor, tex, u, v, dUdx, dUdy, dVdx, dVdy); \
+                srcColor[0] *= texColor[0];                                     \
+                srcColor[1] *= texColor[1];                                     \
+                srcColor[2] *= texColor[2];                                     \
+                srcColor[3] *= texColor[3];                                     \
+            }                                                                   \
+                                                                                \
+            if (ENABLE_COLOR_BLEND)                                             \
+            {                                                                   \
+                float dstColor[4];                                              \
+                sw_framebuffer_read_color(dstColor, ptr);                       \
+                sw_blend_colors(dstColor, srcColor);                            \
+                sw_framebuffer_write_color(ptr, dstColor);                      \
+            }                                                                   \
+            else sw_framebuffer_write_color(ptr, srcColor);                     \
+                                                                                \
+        discard:                                                                \
+            z += dZdx;                                                          \
+            color[0] += dCdx[0];                                                \
+            color[1] += dCdx[1];                                                \
+            color[2] += dCdx[2];                                                \
+            color[3] += dCdx[3];                                                \
+            if (ENABLE_TEXTURE)                                                 \
+            {                                                                   \
+                u += dUdx;                                                      \
+                v += dVdx;                                                      \
+            }                                                                   \
+            ++ptr;                                                              \
+        }                                                                       \
+                                                                                \
+        zScanline += dZdy;                                                      \
+        colorScanline[0] += dCdy[0];                                            \
+        colorScanline[1] += dCdy[1];                                            \
+        colorScanline[2] += dCdy[2];                                            \
+        colorScanline[3] += dCdy[3];                                            \
+                                                                                \
+        if (ENABLE_TEXTURE)                                                     \
+        {                                                                       \
+            uScanline += dUdy;                                                  \
+            vScanline += dVdy;                                                  \
+        }                                                                       \
+    }                                                                           \
+}
+
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned, 0, 0, 0)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX, 1, 0, 0)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH, 0, 1, 0)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_BLEND, 0, 0, 1)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH, 1, 1, 0)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_BLEND, 1, 0, 1)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_DEPTH_BLEND, 0, 1, 1)
+DEFINE_QUAD_RASTER_AXIS_ALIGNED(sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND, 1, 1, 1)
+
+static inline void sw_quad_render(void)
+{
+    if (RLSW.stateFlags & SW_STATE_CULL_FACE)
+    {
+        if (!sw_quad_face_culling()) return;
+    }
+
+    sw_quad_clip_and_project();
+
+    if (RLSW.vertexCounter < 3) return;
+
+    uint32_t state = RLSW.stateFlags;
+    if (RLSW.currentTexture == 0) state &= ~SW_STATE_TEXTURE_2D;
+    if ((RLSW.srcFactor == SW_ONE) && (RLSW.dstFactor == SW_ZERO)) state &= ~SW_STATE_BLEND;
+
+    if ((RLSW.vertexCounter == 4) && sw_quad_is_axis_aligned())
+    {
+        if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_DEPTH_BLEND();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_DEPTH_BLEND();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) sw_quad_raster_axis_aligned_TEX_BLEND();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_TEX_DEPTH();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) sw_quad_raster_axis_aligned_BLEND();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) sw_quad_raster_axis_aligned_DEPTH();
+        else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) sw_quad_raster_axis_aligned_TEX();
+        else sw_quad_raster_axis_aligned();
+        return;
+    }
+
+    #define TRIANGLE_RASTER(RASTER_FUNC)                        \
+    {                                                           \
+        for (int i = 0; i < RLSW.vertexCounter - 2; i++)        \
+        {                                                       \
+            RASTER_FUNC(                                        \
+                &RLSW.vertexBuffer[0],                          \
+                &RLSW.vertexBuffer[i + 1],                      \
+                &RLSW.vertexBuffer[i + 2],                      \
+                &RLSW.loadedTextures[RLSW.currentTexture]       \
+            );                                                  \
+        }                                                       \
+    }
+
+    if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_TEX_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D | SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_TEX_DEPTH)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_BLEND)) TRIANGLE_RASTER(sw_triangle_raster_BLEND)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_DEPTH_TEST)) TRIANGLE_RASTER(sw_triangle_raster_DEPTH)
+    else if (SW_STATE_CHECK_EX(state, SW_STATE_TEXTURE_2D)) TRIANGLE_RASTER(sw_triangle_raster_TEX)
+    else TRIANGLE_RASTER(sw_triangle_raster)
+
+    #undef TRIANGLE_RASTER
+}
+//-------------------------------------------------------------------------------------------
+
+// Line rendering logic
+//-------------------------------------------------------------------------------------------
+static inline bool sw_line_clip_coord(float q, float p, float *t0, float *t1)
+{
+    if (fabsf(p) < SW_CLIP_EPSILON)
+    {
+        // Check if the line is entirely outside the window
+        if (q < -SW_CLIP_EPSILON) return 0; // Completely outside
+        return 1; // Completely inside or on the edges
+    }
+
+    const float r = q/p;
+
+    if (p < 0)
+    {
+        if (r > *t1) return 0;
+        if (r > *t0) *t0 = r;
+    }
+    else
+    {
+        if (r < *t0) return 0;
+        if (r < *t1) *t1 = r;
+    }
+
+    return 1;
+}
+
+static inline bool sw_line_clip(sw_vertex_t *v0, sw_vertex_t *v1)
+{
+    float t0 = 0.0f, t1 = 1.0f;
+    float dH[4], dC[4];
+
+    for (int i = 0; i < 4; i++)
+    {
+        dH[i] = v1->homogeneous[i] - v0->homogeneous[i];
+        dC[i] = v1->color[i] - v0->color[i];
+    }
+
+    // Clipping Liang-Barsky
+    if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[0], -dH[3] + dH[0], &t0, &t1)) return false;
+    if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[0], -dH[3] - dH[0], &t0, &t1)) return false;
+    if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[1], -dH[3] + dH[1], &t0, &t1)) return false;
+    if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[1], -dH[3] - dH[1], &t0, &t1)) return false;
+    if (!sw_line_clip_coord(v0->homogeneous[3] - v0->homogeneous[2], -dH[3] + dH[2], &t0, &t1)) return false;
+    if (!sw_line_clip_coord(v0->homogeneous[3] + v0->homogeneous[2], -dH[3] - dH[2], &t0, &t1)) return false;
+
+    // Clipping Scissor
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        if (!sw_line_clip_coord(v0->homogeneous[0] - RLSW.scClipMin[0]*v0->homogeneous[3], RLSW.scClipMin[0]*dH[3] - dH[0], &t0, &t1)) return false;
+        if (!sw_line_clip_coord(RLSW.scClipMax[0]*v0->homogeneous[3] - v0->homogeneous[0], dH[0] - RLSW.scClipMax[0]*dH[3], &t0, &t1)) return false;
+        if (!sw_line_clip_coord(v0->homogeneous[1] - RLSW.scClipMin[1]*v0->homogeneous[3], RLSW.scClipMin[1]*dH[3] - dH[1], &t0, &t1)) return false;
+        if (!sw_line_clip_coord(RLSW.scClipMax[1]*v0->homogeneous[3] - v0->homogeneous[1], dH[1] - RLSW.scClipMax[1]*dH[3], &t0, &t1)) return false;
+    }
+
+    // Interpolation of new coordinates
+    if (t1 < 1.0f)
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            v1->homogeneous[i] = v0->homogeneous[i] + t1*dH[i];
+            v1->color[i] = v0->color[i] + t1*dC[i];
+        }
+    }
+
+    if (t0 > 0.0f)
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            v0->homogeneous[i] += t0*dH[i];
+            v0->color[i] += t0*dC[i];
+        }
+    }
+
+    return true;
+}
+
+static inline bool sw_line_clip_and_project(sw_vertex_t *v0, sw_vertex_t *v1)
+{
+    if (!sw_line_clip(v0, v1)) return false;
+
+    // Convert homogeneous coordinates to NDC
+    v0->homogeneous[3] = 1.0f/v0->homogeneous[3];
+    v1->homogeneous[3] = 1.0f/v1->homogeneous[3];
+    for (int i = 0; i < 3; i++)
+    {
+        v0->homogeneous[i] *= v0->homogeneous[3];
+        v1->homogeneous[i] *= v1->homogeneous[3];
+    }
+
+    // Convert NDC coordinates to screen space
+    sw_project_ndc_to_screen(v0->screen, v0->homogeneous);
+    sw_project_ndc_to_screen(v1->screen, v1->homogeneous);
+
+    return true;
+}
+
+#define DEFINE_LINE_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND) \
+static inline void FUNC_NAME(const sw_vertex_t *v0, const sw_vertex_t *v1) \
+{                                                                       \
+    float x0 = v0->screen[0];                                           \
+    float y0 = v0->screen[1];                                           \
+    float x1 = v1->screen[0];                                           \
+    float y1 = v1->screen[1];                                           \
+                                                                        \
+    float dx = x1 - x0;                                                 \
+    float dy = y1 - y0;                                                 \
+                                                                        \
+    /* Compute dominant axis and subpixel offset */                     \
+    float steps, substep;                                               \
+    if (fabsf(dx) > fabsf(dy))                                          \
+    {                                                                   \
+        steps = fabsf(dx);                                              \
+        if (steps < 1.0f) return;                                       \
+        substep = (dx >= 0.0f)? (1.0f - sw_fract(x0)) : sw_fract(x0);   \
+    }                                                                   \
+    else                                                                \
+    {                                                                   \
+        steps = fabsf(dy);                                              \
+        if (steps < 1.0f) return;                                       \
+        substep = (dy >= 0.0f)? (1.0f - sw_fract(y0)) : sw_fract(y0);   \
+    }                                                                   \
+                                                                        \
+    /* Compute per pixel increments */                                  \
+    float xInc = dx/steps;                                              \
+    float yInc = dy/steps;                                              \
+    float stepRcp = 1.0f/steps;                                         \
+                                                                        \
+    float zInc = (v1->homogeneous[2] - v0->homogeneous[2])*stepRcp;     \
+    float rInc = (v1->color[0] - v0->color[0])*stepRcp;                 \
+    float gInc = (v1->color[1] - v0->color[1])*stepRcp;                 \
+    float bInc = (v1->color[2] - v0->color[2])*stepRcp;                 \
+    float aInc = (v1->color[3] - v0->color[3])*stepRcp;                 \
+                                                                        \
+    /* Initializing the interpolation starting values  */               \
+    float x = x0 + xInc*substep;                                        \
+    float y = y0 + yInc*substep;                                        \
+    float z = v0->homogeneous[2] + zInc*substep;                        \
+    float r = v0->color[0] + rInc*substep;                              \
+    float g = v0->color[1] + gInc*substep;                              \
+    float b = v0->color[2] + bInc*substep;                              \
+    float a = v0->color[3] + aInc*substep;                              \
+                                                                        \
+    const int fbWidth = RLSW.framebuffer.width;                         \
+    sw_pixel_t *pixels = RLSW.framebuffer.pixels;                       \
+                                                                        \
+    int numPixels = (int)(steps - substep) + 1;                         \
+                                                                        \
+    for (int i = 0; i < numPixels; i++)                                 \
+    {                                                                   \
+        /* REVIEW: May require reviewing projection details */          \
+        int px = (int)(x - 0.5f);                                       \
+        int py = (int)(y - 0.5f);                                       \
+                                                                        \
+        sw_pixel_t *ptr = pixels + py*fbWidth + px;                     \
+                                                                        \
+        if (ENABLE_DEPTH_TEST)                                          \
+        {                                                               \
+            float depth = sw_framebuffer_read_depth(ptr);               \
+            if (z > depth) goto discard;                                \
+        }                                                               \
+                                                                        \
+        sw_framebuffer_write_depth(ptr, z);                             \
+                                                                        \
+        float color[4] = {r, g, b, a};                                  \
+                                                                        \
+        if (ENABLE_COLOR_BLEND)                                         \
+        {                                                               \
+            float dstColor[4];                                          \
+            sw_framebuffer_read_color(dstColor, ptr);                   \
+            sw_blend_colors(dstColor, color);                           \
+            sw_framebuffer_write_color(ptr, dstColor);                  \
+        }                                                               \
+        else sw_framebuffer_write_color(ptr, color);                    \
+                                                                        \
+    discard:                                                            \
+        x += xInc; y += yInc; z += zInc;                                \
+        r += rInc; g += gInc; b += bInc; a += aInc;                     \
+    }                                                                   \
+}
+
+#define DEFINE_LINE_THICK_RASTER(FUNC_NAME, RASTER_FUNC)                \
+void FUNC_NAME(const sw_vertex_t *v1, const sw_vertex_t *v2)            \
+{                                                                       \
+    sw_vertex_t tv1, tv2;                                               \
+                                                                        \
+    int x1 = (int)v1->screen[0];                                        \
+    int y1 = (int)v1->screen[1];                                        \
+    int x2 = (int)v2->screen[0];                                        \
+    int y2 = (int)v2->screen[1];                                        \
+                                                                        \
+    int dx = x2 - x1;                                                   \
+    int dy = y2 - y1;                                                   \
+                                                                        \
+    RASTER_FUNC(v1, v2);                                                \
+                                                                        \
+    if ((dx != 0) && (abs(dy/dx) < 1))                                  \
+    {                                                                   \
+        int wy = (int)((RLSW.lineWidth - 1.0f)*abs(dx)/sqrtf(dx*dx + dy*dy)); \
+        wy >>= 1;                                                       \
+        for (int i = 1; i <= wy; i++)                                   \
+        {                                                               \
+            tv1 = *v1, tv2 = *v2;                                       \
+            tv1.screen[1] -= i;                                         \
+            tv2.screen[1] -= i;                                         \
+            RASTER_FUNC(&tv1, &tv2);                                    \
+            tv1 = *v1, tv2 = *v2;                                       \
+            tv1.screen[1] += i;                                         \
+            tv2.screen[1] += i;                                         \
+            RASTER_FUNC(&tv1, &tv2);                                    \
+        }                                                               \
+    }                                                                   \
+    else if (dy != 0)                                                   \
+    {                                                                   \
+        int wx = (int)((RLSW.lineWidth - 1.0f)*abs(dy)/sqrtf(dx*dx + dy*dy)); \
+        wx >>= 1;                                                       \
+        for (int i = 1; i <= wx; i++)                                   \
+        {                                                               \
+            tv1 = *v1, tv2 = *v2;                                       \
+            tv1.screen[0] -= i;                                         \
+            tv2.screen[0] -= i;                                         \
+            RASTER_FUNC(&tv1, &tv2);                                    \
+            tv1 = *v1, tv2 = *v2;                                       \
+            tv1.screen[0] += i;                                         \
+            tv2.screen[0] += i;                                         \
+            RASTER_FUNC(&tv1, &tv2);                                    \
+        }                                                               \
+    }                                                                   \
+}
+
+DEFINE_LINE_RASTER(sw_line_raster, 0, 0)
+DEFINE_LINE_RASTER(sw_line_raster_DEPTH, 1, 0)
+DEFINE_LINE_RASTER(sw_line_raster_BLEND, 0, 1)
+DEFINE_LINE_RASTER(sw_line_raster_DEPTH_BLEND, 1, 1)
+
+DEFINE_LINE_THICK_RASTER(sw_line_thick_raster, sw_line_raster)
+DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH, sw_line_raster_DEPTH)
+DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_BLEND, sw_line_raster_BLEND)
+DEFINE_LINE_THICK_RASTER(sw_line_thick_raster_DEPTH_BLEND, sw_line_raster_DEPTH_BLEND)
+
+static inline void sw_line_render(sw_vertex_t *vertices)
+{
+    if (!sw_line_clip_and_project(&vertices[0], &vertices[1])) return;
+
+    if (RLSW.lineWidth >= 2.0f)
+    {
+        if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_thick_raster_DEPTH_BLEND(&vertices[0], &vertices[1]);
+        else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_thick_raster_BLEND(&vertices[0], &vertices[1]);
+        else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_thick_raster_DEPTH(&vertices[0], &vertices[1]);
+        else sw_line_thick_raster(&vertices[0], &vertices[1]);
+    }
+    else
+    {
+        if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_line_raster_DEPTH_BLEND(&vertices[0], &vertices[1]);
+        else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_line_raster_BLEND(&vertices[0], &vertices[1]);
+        else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_line_raster_DEPTH(&vertices[0], &vertices[1]);
+        else sw_line_raster(&vertices[0], &vertices[1]);
+    }
+}
+//-------------------------------------------------------------------------------------------
+
+// Point rendering logic
+//-------------------------------------------------------------------------------------------
+static inline bool sw_point_clip_and_project(sw_vertex_t *v)
+{
+    if (v->homogeneous[3] != 1.0f)
+    {
+        for (int_fast8_t i = 0; i < 3; i++)
+        {
+            if ((v->homogeneous[i] < -v->homogeneous[3]) || (v->homogeneous[i] > v->homogeneous[3])) return false;
+        }
+
+        v->homogeneous[3] = 1.0f/v->homogeneous[3];
+        v->homogeneous[0] *= v->homogeneous[3];
+        v->homogeneous[1] *= v->homogeneous[3];
+        v->homogeneous[2] *= v->homogeneous[3];
+    }
+
+    sw_project_ndc_to_screen(v->screen, v->homogeneous);
+
+    const int *min = NULL, *max = NULL;
+
+    if (RLSW.stateFlags & SW_STATE_SCISSOR_TEST)
+    {
+        min = RLSW.scMin;
+        max = RLSW.scMax;
+    }
+    else
+    {
+        min = RLSW.vpMin;
+        max = RLSW.vpMax;
+    }
+
+    bool insideX = (v->screen[0] - RLSW.pointRadius < max[0]) && (v->screen[0] + RLSW.pointRadius > min[0]);
+    bool insideY = (v->screen[1] - RLSW.pointRadius < max[1]) && (v->screen[1] + RLSW.pointRadius > min[1]);
+
+    return (insideX && insideY);
+}
+
+#define DEFINE_POINT_RASTER(FUNC_NAME, ENABLE_DEPTH_TEST, ENABLE_COLOR_BLEND, CHECK_BOUNDS) \
+static inline void FUNC_NAME(int x, int y, float z, const float color[4])   \
+{                                                                           \
+    if (CHECK_BOUNDS == 1)                                                  \
+    {                                                                       \
+        if ((x < RLSW.vpMin[0]) || (x >= RLSW.vpMax[0])) return;            \
+        if ((y < RLSW.vpMin[1]) || (y >= RLSW.vpMax[1])) return;            \
+    }                                                                       \
+    else if (CHECK_BOUNDS == SW_SCISSOR_TEST)                               \
+    {                                                                       \
+        if ((x < RLSW.scMin[0]) || (x >= RLSW.scMax[0])) return;            \
+        if ((y < RLSW.scMin[1]) || (y >= RLSW.scMax[1])) return;            \
+    }                                                                       \
+                                                                            \
+    int offset = y*RLSW.framebuffer.width + x;                              \
+    sw_pixel_t *ptr = RLSW.framebuffer.pixels + offset;                     \
+                                                                            \
+    if (ENABLE_DEPTH_TEST)                                                  \
+    {                                                                       \
+        float depth = sw_framebuffer_read_depth(ptr);                       \
+        if (z > depth) return;                                              \
+    }                                                                       \
+                                                                            \
+    sw_framebuffer_write_depth(ptr, z);                                     \
+                                                                            \
+    if (ENABLE_COLOR_BLEND)                                                 \
+    {                                                                       \
+        float dstColor[4];                                                  \
+        sw_framebuffer_read_color(dstColor, ptr);                           \
+        sw_blend_colors(dstColor, color);                                   \
+        sw_framebuffer_write_color(ptr, dstColor);                          \
+    }                                                                       \
+    else sw_framebuffer_write_color(ptr, color);                            \
+}
+
+#define DEFINE_POINT_THICK_RASTER(FUNC_NAME, RASTER_FUNC)                   \
+static inline void FUNC_NAME(sw_vertex_t *v)                                \
+{                                                                           \
+    int cx = v->screen[0];                                                  \
+    int cy = v->screen[1];                                                  \
+    float cz = v->homogeneous[2];                                           \
+    int radius = RLSW.pointRadius;                                          \
+    const float *color = v->color;                                          \
+                                                                            \
+    int x = 0;                                                              \
+    int y = radius;                                                         \
+    int d = 3 - 2*radius;                                                   \
+                                                                            \
+    while (x <= y)                                                          \
+    {                                                                       \
+        for (int i = -x; i <= x; i++)                                       \
+        {                                                                   \
+            RASTER_FUNC(cx + i, cy + y, cz, color);                         \
+            RASTER_FUNC(cx + i, cy - y, cz, color);                         \
+        }                                                                   \
+        for (int i = -y; i <= y; i++)                                       \
+        {                                                                   \
+            RASTER_FUNC(cx + i, cy + x, cz, color);                         \
+            RASTER_FUNC(cx + i, cy - x, cz, color);                         \
+        }                                                                   \
+        if (d > 0)                                                          \
+        {                                                                   \
+            y--;                                                            \
+            d = d + 4*(x - y) + 10;                                         \
+        }                                                                   \
+        else d = d + 4*x + 6;                                               \
+        x++;                                                                \
+    }                                                                       \
+}
+
+DEFINE_POINT_RASTER(sw_point_raster, 0, 0, 0)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH, 1, 0, 0)
+DEFINE_POINT_RASTER(sw_point_raster_BLEND, 0, 1, 0)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND, 1, 1, 0)
+
+DEFINE_POINT_RASTER(sw_point_raster_CHECK, 0, 0, 1)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK, 1, 0, 1)
+DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK, 0, 1, 1)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK, 1, 1, 1)
+
+DEFINE_POINT_RASTER(sw_point_raster_CHECK_SCISSOR, 0, 0, SW_SCISSOR_TEST)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH_CHECK_SCISSOR, 1, 0, SW_SCISSOR_TEST)
+DEFINE_POINT_RASTER(sw_point_raster_BLEND_CHECK_SCISSOR, 0, 1, SW_SCISSOR_TEST)
+DEFINE_POINT_RASTER(sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR, 1, 1, SW_SCISSOR_TEST)
+
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster, sw_point_raster_CHECK)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH, sw_point_raster_DEPTH_CHECK)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND, sw_point_raster_BLEND_CHECK)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND, sw_point_raster_DEPTH_BLEND_CHECK)
+
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_SCISSOR, sw_point_raster_CHECK_SCISSOR)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_SCISSOR, sw_point_raster_DEPTH_CHECK_SCISSOR)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_BLEND_SCISSOR, sw_point_raster_BLEND_CHECK_SCISSOR)
+DEFINE_POINT_THICK_RASTER(sw_point_thick_raster_DEPTH_BLEND_SCISSOR, sw_point_raster_DEPTH_BLEND_CHECK_SCISSOR)
+
+static inline void sw_point_render(sw_vertex_t *v)
+{
+    if (!sw_point_clip_and_project(v)) return;
+
+    if (RLSW.pointRadius >= 1.0f)
+    {
+        if (SW_STATE_CHECK(SW_STATE_SCISSOR_TEST))
+        {
+            if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND_SCISSOR(v);
+            else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND_SCISSOR(v);
+            else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH_SCISSOR(v);
+            else sw_point_thick_raster_SCISSOR(v);
+        }
+        else
+        {
+            if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_thick_raster_DEPTH_BLEND(v);
+            else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_thick_raster_BLEND(v);
+            else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_thick_raster_DEPTH(v);
+            else sw_point_thick_raster(v);
+        }
+    }
+    else
+    {
+        if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST | SW_STATE_BLEND)) sw_point_raster_DEPTH_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color);
+        else if (SW_STATE_CHECK(SW_STATE_BLEND)) sw_point_raster_BLEND(v->screen[0], v->screen[1], v->homogeneous[2], v->color);
+        else if (SW_STATE_CHECK(SW_STATE_DEPTH_TEST)) sw_point_raster_DEPTH(v->screen[0], v->screen[1], v->homogeneous[2], v->color);
+        else sw_point_raster(v->screen[0], v->screen[1], v->homogeneous[2], v->color);
+    }
+}
+//-------------------------------------------------------------------------------------------
+
+// Polygon modes rendering logic
+//-------------------------------------------------------------------------------------------
+static inline void sw_poly_point_render(void)
+{
+    for (int i = 0; i < RLSW.vertexCounter; i++) sw_point_render(&RLSW.vertexBuffer[i]);
+}
+
+static inline void sw_poly_line_render(void)
+{
+    const sw_vertex_t *vertices = RLSW.vertexBuffer;
+    int cm1 = RLSW.vertexCounter - 1;
+
+    for (int i = 0; i < cm1; i++)
+    {
+        sw_vertex_t verts[2] = { vertices[i], vertices[i + 1] };
+        sw_line_render(verts);
+    }
+
+    sw_vertex_t verts[2] = { vertices[cm1], vertices[0] };
+    sw_line_render(verts);
+}
+
+static inline void sw_poly_fill_render(void)
+{
+    switch (RLSW.drawMode)
+    {
+        case SW_POINTS: sw_point_render(&RLSW.vertexBuffer[0]); break;
+        case SW_LINES: sw_line_render(RLSW.vertexBuffer); break;
+        case SW_TRIANGLES: sw_triangle_render(); break;
+        case SW_QUADS: sw_quad_render(); break;
+    }
+}
+//-------------------------------------------------------------------------------------------
+
+// Immediate rendering logic
+//-------------------------------------------------------------------------------------------
+void sw_immediate_push_vertex(const float position[4], const float color[4], const float texcoord[2])
+{
+    // Copy the attributes in the current vertex
+    sw_vertex_t *vertex = &RLSW.vertexBuffer[RLSW.vertexCounter++];
+    for (int i = 0; i < 4; i++)
+    {
+        vertex->position[i] = position[i];
+        if (i < 2) vertex->texcoord[i] = texcoord[i];
+        vertex->color[i] = color[i];
+    }
+
+    // Calculate homogeneous coordinates
+    const float *m = RLSW.matMVP, *v = vertex->position;
+    vertex->homogeneous[0] = m[0]*v[0] + m[4]*v[1] + m[8]*v[2] + m[12]*v[3];
+    vertex->homogeneous[1] = m[1]*v[0] + m[5]*v[1] + m[9]*v[2] + m[13]*v[3];
+    vertex->homogeneous[2] = m[2]*v[0] + m[6]*v[1] + m[10]*v[2] + m[14]*v[3];
+    vertex->homogeneous[3] = m[3]*v[0] + m[7]*v[1] + m[11]*v[2] + m[15]*v[3];
+
+    // Immediate rendering of the primitive if the required number is reached
+    if (RLSW.vertexCounter == RLSW.reqVertices)
+    {
+        switch (RLSW.polyMode)
+        {
+            case SW_FILL: sw_poly_fill_render(); break;
+            case SW_LINE: sw_poly_line_render(); break;
+            case SW_POINT: sw_poly_point_render(); break;
+            default: break;
+        }
+
+        RLSW.vertexCounter = 0;
+    }
+}
+
+//-------------------------------------------------------------------------------------------
+
+// Validity check helper functions
+//-------------------------------------------------------------------------------------------
+static inline bool sw_is_texture_valid(uint32_t id)
+{
+    bool valid = true;
+
+    if (id == 0) valid = false;
+    else if (id >= SW_MAX_TEXTURES) valid = false;
+    else if (RLSW.loadedTextures[id].pixels == NULL) valid = false;
+
+    return true;
+}
+
+static inline bool sw_is_texture_filter_valid(int filter)
+{
+    return ((filter == SW_NEAREST) || (filter == SW_LINEAR));
+}
+
+static inline bool sw_is_texture_wrap_valid(int wrap)
+{
+    return ((wrap == SW_REPEAT) || (wrap == SW_CLAMP));
+}
+
+static inline bool sw_is_draw_mode_valid(int mode)
+{
+    bool result = false;
+
+    switch (mode)
+    {
+        case SW_POINTS:
+        case SW_LINES:
+        case SW_TRIANGLES:
+        case SW_QUADS: result = true; break;
+        default: break;
+    }
+
+    return result;
+}
+
+static inline bool sw_is_poly_mode_valid(int mode)
+{
+    bool result = false;
+
+    switch (mode)
+    {
+        case SW_POINT:
+        case SW_LINE:
+        case SW_FILL: result = true; break;
+        default: break;
+    }
+
+    return result;
+}
+
+static inline bool sw_is_face_valid(int face)
+{
+    return (face == SW_FRONT || face == SW_BACK);
+}
+
+static inline bool sw_is_blend_src_factor_valid(int blend)
+{
+    bool result = false;
+
+    switch (blend)
+    {
+        case SW_ZERO:
+        case SW_ONE:
+        case SW_SRC_COLOR:
+        case SW_ONE_MINUS_SRC_COLOR:
+        case SW_SRC_ALPHA:
+        case SW_ONE_MINUS_SRC_ALPHA:
+        case SW_DST_ALPHA:
+        case SW_ONE_MINUS_DST_ALPHA:
+        case SW_DST_COLOR:
+        case SW_ONE_MINUS_DST_COLOR:
+        case SW_SRC_ALPHA_SATURATE: result = true; break;
+        default: break;
+    }
+
+    return result;
+}
+
+static inline bool sw_is_blend_dst_factor_valid(int blend)
+{
+    bool result = false;
+
+    switch (blend)
+    {
+        case SW_ZERO:
+        case SW_ONE:
+        case SW_SRC_COLOR:
+        case SW_ONE_MINUS_SRC_COLOR:
+        case SW_SRC_ALPHA:
+        case SW_ONE_MINUS_SRC_ALPHA:
+        case SW_DST_ALPHA:
+        case SW_ONE_MINUS_DST_ALPHA:
+        case SW_DST_COLOR:
+        case SW_ONE_MINUS_DST_COLOR: result = true; break;
+        default: break;
+    }
+
+    return result;
+}
+//-------------------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+bool swInit(int w, int h)
+{
+    if (!sw_framebuffer_load(w, h)) { swClose(); return false; }
+
+    swViewport(0, 0, w, h);
+    swScissor(0, 0, w, h);
+
+    RLSW.loadedTextures = (sw_texture_t *)SW_MALLOC(SW_MAX_TEXTURES*sizeof(sw_texture_t));
+    if (RLSW.loadedTextures == NULL) { swClose(); return false; }
+
+    RLSW.freeTextureIds = (uint32_t *)SW_MALLOC(SW_MAX_TEXTURES*sizeof(uint32_t));
+    if (RLSW.loadedTextures == NULL) { swClose(); return false; }
+
+    const float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+    sw_framebuffer_write_color(&RLSW.clearValue, clearColor);
+    sw_framebuffer_write_depth(&RLSW.clearValue, 1.0f);
+
+    RLSW.currentMatrixMode = SW_MODELVIEW;
+    RLSW.currentMatrix = &RLSW.stackModelview[0];
+
+    sw_matrix_id(RLSW.stackProjection[0]);
+    sw_matrix_id(RLSW.stackModelview[0]);
+    sw_matrix_id(RLSW.stackTexture[0]);
+    sw_matrix_id(RLSW.matMVP);
+
+    RLSW.stackProjectionCounter = 1;
+    RLSW.stackModelviewCounter = 1;
+    RLSW.stackTextureCounter = 1;
+    RLSW.isDirtyMVP = false;
+
+    RLSW.current.texcoord[0] = 0.0f;
+    RLSW.current.texcoord[1] = 0.0f;
+
+    RLSW.current.color[0] = 1.0f;
+    RLSW.current.color[1] = 1.0f;
+    RLSW.current.color[2] = 1.0f;
+    RLSW.current.color[3] = 1.0f;
+
+    RLSW.srcFactor = SW_SRC_ALPHA;
+    RLSW.dstFactor = SW_ONE_MINUS_SRC_ALPHA;
+
+    RLSW.srcFactorFunc = sw_factor_src_alpha;
+    RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha;
+
+    RLSW.polyMode = SW_FILL;
+    RLSW.cullFace = SW_BACK;
+
+    static uint32_t defaultTex[3*2*2] = {
+        0xFFFFFFFF,
+        0xFFFFFFFF,
+        0xFFFFFFFF,
+        0xFFFFFFFF
+    };
+
+    RLSW.loadedTextures[0].pixels = (uint8_t*)defaultTex;
+    RLSW.loadedTextures[0].width = 2;
+    RLSW.loadedTextures[0].height = 2;
+    RLSW.loadedTextures[0].wMinus1 = 1;
+    RLSW.loadedTextures[0].hMinus1 = 1;
+    RLSW.loadedTextures[0].minFilter = SW_NEAREST;
+    RLSW.loadedTextures[0].magFilter = SW_NEAREST;
+    RLSW.loadedTextures[0].sWrap = SW_REPEAT;
+    RLSW.loadedTextures[0].tWrap = SW_REPEAT;
+    RLSW.loadedTextures[0].tx = 0.5f;
+    RLSW.loadedTextures[0].ty = 0.5f;
+
+    RLSW.loadedTextureCount = 1;
+
+    SW_LOG("INFO: RLSW: Software renderer initialized successfully\n");
+#if defined(SW_HAS_FMA_AVX) && defined(SW_HAS_FMA_AVX2)
+    SW_LOG("INFO: RLSW: Using SIMD instructions: FMA AVX\n");
+#endif
+#if defined(SW_HAS_AVX) || defined(SW_HAS_AVX2)
+    SW_LOG("INFO: RLSW: Using SIMD instructions: AVX\n");
+#endif
+#if defined(SW_HAS_SSE) || defined(SW_HAS_SSE2) || defined(SW_HAS_SSE3) || defined(SW_HAS_SSSE3) || defined(SW_HAS_SSE41) || defined(SW_HAS_SSE42)
+    SW_LOG("INFO: RLSW: Using SIMD instructions: SSE\n");
+#endif
+#if defined(SW_HAS_NEON_FMA) || defined(SW_HAS_NEON)
+    SW_LOG("INFO: RLSW: Using SIMD instructions: NEON\n");
+#endif
+#if defined(SW_HAS_RVV)
+    SW_LOG("INFO: RLSW: Using SIMD instructions: RVV\n");
+#endif
+
+    return true;
+}
+
+void swClose(void)
+{
+    // NOTE: Starts at texture 1, texture 0 does not have to be freed
+    for (int i = 1; i < RLSW.loadedTextureCount; i++)
+    {
+        if (sw_is_texture_valid(i))
+        {
+            SW_FREE(RLSW.loadedTextures[i].pixels);
+        }
+    }
+
+    SW_FREE(RLSW.framebuffer.pixels);
+    SW_FREE(RLSW.loadedTextures);
+    SW_FREE(RLSW.freeTextureIds);
+
+    RLSW = SW_CURLY_INIT(sw_context_t) { 0 };
+}
+
+bool swResizeFramebuffer(int w, int h)
+{
+    return sw_framebuffer_resize(w, h);
+}
+
+void swCopyFramebuffer(int x, int y, int w, int h, SWformat format, SWtype type, void *pixels)
+{
+    sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type);
+
+    if (w <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+    if (h <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+
+    if (w > RLSW.framebuffer.width) w = RLSW.framebuffer.width;
+    if (h > RLSW.framebuffer.height) h = RLSW.framebuffer.height;
+
+    x = sw_clampi(x, 0, w);
+    y = sw_clampi(y, 0, h);
+
+    if ((x >= w) || (y >= h)) return;
+
+    if ((x == 0) && (y == 0) && (w == RLSW.framebuffer.width) && (h == RLSW.framebuffer.height))
+    {
+        #if SW_COLOR_BUFFER_BITS == 32
+            if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
+            {
+                sw_framebuffer_copy_fast(pixels);
+                return;
+            }
+        #elif SW_COLOR_BUFFER_BITS == 16
+            if (pFormat == SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5)
+            {
+                sw_framebuffer_copy_fast(pixels);
+                return;
+            }
+        #endif
+    }
+
+    switch (pFormat)
+    {
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_copy_to_GRAYALPHA(x, y, w, h, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_copy_to_R5G6B5(x, y, w, h, (uint16_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_copy_to_R8G8B8(x, y, w, h, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_copy_to_R5G5B5A1(x, y, w, h, (uint16_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_copy_to_R4G4B4A4(x, y, w, h, (uint16_t *)pixels); break;
+        //case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_copy_to_R8G8B8A8(x, y, w, h, (uint8_t *)pixels); break;
+        // Below: not implemented
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+void swBlitFramebuffer(int xDst, int yDst, int wDst, int hDst, int xSrc, int ySrc, int wSrc, int hSrc, SWformat format, SWtype type, void *pixels)
+{
+    sw_pixelformat_t pFormat = (sw_pixelformat_t)sw_get_pixel_format(format, type);
+
+    if (wSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+    if (hSrc <= 0) { RLSW.errCode = SW_INVALID_VALUE; return; }
+
+    if (wSrc > RLSW.framebuffer.width) wSrc = RLSW.framebuffer.width;
+    if (hSrc > RLSW.framebuffer.height) hSrc = RLSW.framebuffer.height;
+
+    xSrc = sw_clampi(xSrc, 0, wSrc);
+    ySrc = sw_clampi(ySrc, 0, hSrc);
+
+    // Check if the sizes are identical after clamping the source to avoid unexpected issues
+    // REVIEW: This repeats the operations if true, so we could make a copy function without these checks
+    if (xDst == xSrc && yDst == ySrc && wDst == wSrc && hDst == hSrc)
+    {
+        swCopyFramebuffer(xSrc, ySrc, wSrc, hSrc, format, type, pixels);
+    }
+
+    switch (pFormat)
+    {
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: sw_framebuffer_blit_to_GRAYALPHA(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G6B5: sw_framebuffer_blit_to_R5G6B5(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8: sw_framebuffer_blit_to_R8G8B8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: sw_framebuffer_blit_to_R5G5B5A1(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: sw_framebuffer_blit_to_R4G4B4A4(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint16_t *)pixels); break;
+        case SW_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: sw_framebuffer_blit_to_R8G8B8A8(xDst, yDst, wDst, hDst, xSrc, ySrc, wSrc, hSrc, (uint8_t *)pixels); break;
+        // Below: not implemented
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16:
+        case SW_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
+        default:
+            RLSW.errCode = SW_INVALID_ENUM;
+            break;
+    }
+}
+
+void swEnable(SWstate state)
+{
+    switch (state)
+    {
+        case SW_SCISSOR_TEST: RLSW.stateFlags |= SW_STATE_SCISSOR_TEST; break;
+        case SW_TEXTURE_2D: RLSW.stateFlags |= SW_STATE_TEXTURE_2D; break;
+        case SW_DEPTH_TEST: RLSW.stateFlags |= SW_STATE_DEPTH_TEST; break;
+        case SW_CULL_FACE: RLSW.stateFlags |= SW_STATE_CULL_FACE; break;
+        case SW_BLEND: RLSW.stateFlags |= SW_STATE_BLEND; break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+void swDisable(SWstate state)
+{
+    switch (state)
+    {
+        case SW_SCISSOR_TEST: RLSW.stateFlags &= ~SW_STATE_SCISSOR_TEST; break;
+        case SW_TEXTURE_2D: RLSW.stateFlags &= ~SW_STATE_TEXTURE_2D; break;
+        case SW_DEPTH_TEST: RLSW.stateFlags &= ~SW_STATE_DEPTH_TEST; break;
+        case SW_CULL_FACE: RLSW.stateFlags &= ~SW_STATE_CULL_FACE; break;
+        case SW_BLEND: RLSW.stateFlags &= ~SW_STATE_BLEND; break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+void swGetIntegerv(SWget name, int *v)
+{
+    switch (name)
+    {
+        case SW_MODELVIEW_STACK_DEPTH: *v = SW_MODELVIEW_STACK_DEPTH; break;
+        case SW_PROJECTION_STACK_DEPTH: *v = SW_PROJECTION_STACK_DEPTH; break;
+        case SW_TEXTURE_STACK_DEPTH: *v = SW_TEXTURE_STACK_DEPTH; break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+void swGetFloatv(SWget name, float *v)
+{
+    switch (name)
+    {
+        case SW_COLOR_CLEAR_VALUE:
+        {
+            sw_framebuffer_read_color(v, &RLSW.clearValue);
+        } break;
+        case SW_DEPTH_CLEAR_VALUE:
+        {
+            v[0] = sw_framebuffer_read_depth(&RLSW.clearValue);
+        } break;
+        case SW_CURRENT_COLOR:
+        {
+            v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[0];
+            v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[1];
+            v[2] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[2];
+            v[3] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].color[3];
+        } break;
+        case SW_CURRENT_TEXTURE_COORDS:
+        {
+            v[0] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[0];
+            v[1] = RLSW.vertexBuffer[RLSW.vertexCounter - 1].texcoord[1];
+        } break;
+        case SW_POINT_SIZE:
+        {
+            v[0] = 2.0f*RLSW.pointRadius;
+        } break;
+        case SW_LINE_WIDTH:
+        {
+            v[0] = RLSW.lineWidth;
+        } break;
+        case SW_MODELVIEW_MATRIX:
+        {
+            for (int i = 0; i < 16; i++) v[i] = RLSW.stackModelview[RLSW.stackModelviewCounter - 1][i];
+
+        } break;
+        case SW_PROJECTION_MATRIX:
+        {
+            for (int i = 0; i < 16; i++) v[i] = RLSW.stackProjection[RLSW.stackProjectionCounter - 1][i];
+
+        } break;
+        case SW_TEXTURE_MATRIX:
+        {
+            for (int i = 0; i < 16; i++) v[i] = RLSW.stackTexture[RLSW.stackTextureCounter - 1][i];
+
+        } break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+const char *swGetString(SWget name)
+{
+    const char *result = NULL;
+
+    switch (name)
+    {
+        case SW_VENDOR: result = "RLSW Header"; break;
+        case SW_RENDERER: result = "RLSW Software Renderer"; break;
+        case SW_VERSION: result = "RLSW 1.0"; break;
+        case SW_EXTENSIONS: result = "None"; break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+
+    return result;
+}
+
+SWerrcode swGetError(void)
+{
+    SWerrcode ret = RLSW.errCode;
+    RLSW.errCode = SW_NO_ERROR;
+    return ret;
+}
+
+void swViewport(int x, int y, int width, int height)
+{
+    if ((width < 0) || (height < 0))
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    RLSW.vpSize[0] = width;
+    RLSW.vpSize[1] = height;
+
+    RLSW.vpHalf[0] = width/2.0f;
+    RLSW.vpHalf[1] = height/2.0f;
+
+    RLSW.vpCenter[0] = (float)x + RLSW.vpHalf[0];
+    RLSW.vpCenter[1] = (float)y + RLSW.vpHalf[1];
+
+    RLSW.vpMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1);
+    RLSW.vpMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1);
+    RLSW.vpMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1);
+    RLSW.vpMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1);
+}
+
+void swScissor(int x, int y, int width, int height)
+{
+    if ((width < 0) || (height < 0))
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    RLSW.scMin[0] = sw_clampi(x, 0, RLSW.framebuffer.width - 1);
+    RLSW.scMin[1] = sw_clampi(y, 0, RLSW.framebuffer.height - 1);
+    RLSW.scMax[0] = sw_clampi(x + width, 0, RLSW.framebuffer.width - 1);
+    RLSW.scMax[1] = sw_clampi(y + height, 0, RLSW.framebuffer.height - 1);
+
+    RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f;
+    RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f;
+    RLSW.scClipMax[1] = 1.0f - (2.0f*(float)RLSW.scMin[1]/(float)RLSW.vpSize[1]);
+    RLSW.scClipMin[1] = 1.0f - (2.0f*(float)RLSW.scMax[1]/(float)RLSW.vpSize[1]);
+}
+
+void swClearColor(float r, float g, float b, float a)
+{
+    float v[4] = { r, g, b, a };
+    sw_framebuffer_write_color(&RLSW.clearValue, v);
+}
+
+void swClearDepth(float depth)
+{
+    sw_framebuffer_write_depth(&RLSW.clearValue, depth);
+}
+
+void swClear(uint32_t bitmask)
+{
+    int size = RLSW.framebuffer.width*RLSW.framebuffer.height;
+
+    if ((bitmask & (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT)) == (SW_COLOR_BUFFER_BIT | SW_DEPTH_BUFFER_BIT))
+    {
+        sw_framebuffer_fill(RLSW.framebuffer.pixels, size, RLSW.clearValue);
+    }
+    else if (bitmask & (SW_COLOR_BUFFER_BIT))
+    {
+        sw_framebuffer_fill_color(RLSW.framebuffer.pixels, size, RLSW.clearValue.color);
+    }
+    else if (bitmask & SW_DEPTH_BUFFER_BIT)
+    {
+        sw_framebuffer_fill_depth(RLSW.framebuffer.pixels, size, RLSW.clearValue.depth);
+    }
+}
+
+void swBlendFunc(SWfactor sfactor, SWfactor dfactor)
+{
+    if (!sw_is_blend_src_factor_valid(sfactor) ||
+        !sw_is_blend_dst_factor_valid(dfactor))
+    {
+        RLSW.errCode = SW_INVALID_ENUM;
+        return;
+    }
+
+    RLSW.srcFactor = sfactor;
+    RLSW.dstFactor = dfactor;
+
+    switch (sfactor)
+    {
+        case SW_ZERO: RLSW.srcFactorFunc = sw_factor_zero; break;
+        case SW_ONE: RLSW.srcFactorFunc = sw_factor_one; break;
+        case SW_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_src_color; break;
+        case SW_ONE_MINUS_SRC_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_src_color; break;
+        case SW_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_src_alpha; break;
+        case SW_ONE_MINUS_SRC_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_src_alpha; break;
+        case SW_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_dst_alpha; break;
+        case SW_ONE_MINUS_DST_ALPHA: RLSW.srcFactorFunc = sw_factor_one_minus_dst_alpha; break;
+        case SW_DST_COLOR: RLSW.srcFactorFunc = sw_factor_dst_color; break;
+        case SW_ONE_MINUS_DST_COLOR: RLSW.srcFactorFunc = sw_factor_one_minus_dst_color; break;
+        case SW_SRC_ALPHA_SATURATE: RLSW.srcFactorFunc = sw_factor_src_alpha_saturate; break;
+        default: break;
+    }
+
+    switch (dfactor)
+    {
+        case SW_ZERO: RLSW.dstFactorFunc = sw_factor_zero; break;
+        case SW_ONE: RLSW.dstFactorFunc = sw_factor_one; break;
+        case SW_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_src_color; break;
+        case SW_ONE_MINUS_SRC_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_src_color; break;
+        case SW_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_src_alpha; break;
+        case SW_ONE_MINUS_SRC_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_src_alpha; break;
+        case SW_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_dst_alpha; break;
+        case SW_ONE_MINUS_DST_ALPHA: RLSW.dstFactorFunc = sw_factor_one_minus_dst_alpha; break;
+        case SW_DST_COLOR: RLSW.dstFactorFunc = sw_factor_dst_color; break;
+        case SW_ONE_MINUS_DST_COLOR: RLSW.dstFactorFunc = sw_factor_one_minus_dst_color; break;
+        case SW_SRC_ALPHA_SATURATE: break;
+        default: break;
+    }
+}
+
+void swPolygonMode(SWpoly mode)
+{
+    if (!sw_is_poly_mode_valid(mode))
+    {
+        RLSW.errCode = SW_INVALID_ENUM;
+        return;
+    }
+
+    RLSW.polyMode = mode;
+}
+
+void swCullFace(SWface face)
+{
+    if (!sw_is_face_valid(face))
+    {
+        RLSW.errCode = SW_INVALID_ENUM;
+        return;
+    }
+
+    RLSW.cullFace = face;
+}
+
+void swPointSize(float size)
+{
+    RLSW.pointRadius = floorf(size*0.5f);
+}
+
+void swLineWidth(float width)
+{
+    RLSW.lineWidth = roundf(width);
+}
+
+void swMatrixMode(SWmatrix mode)
+{
+    switch (mode)
+    {
+        case SW_PROJECTION: RLSW.currentMatrix = &RLSW.stackProjection[RLSW.stackProjectionCounter - 1]; break;
+        case SW_MODELVIEW: RLSW.currentMatrix = &RLSW.stackModelview[RLSW.stackModelviewCounter - 1]; break;
+        case SW_TEXTURE: RLSW.currentMatrix = &RLSW.stackTexture[RLSW.stackTextureCounter - 1]; break;
+        default: RLSW.errCode = SW_INVALID_ENUM; return;
+    }
+
+    RLSW.currentMatrixMode = mode;
+}
+
+void swPushMatrix(void)
+{
+    switch (RLSW.currentMatrixMode)
+    {
+        case SW_PROJECTION:
+        {
+            if (RLSW.stackProjectionCounter >= SW_MAX_PROJECTION_STACK_SIZE)
+            {
+                RLSW.errCode = SW_STACK_OVERFLOW;
+                return;
+            }
+
+            int iOld = RLSW.stackProjectionCounter - 1;
+            int iNew = RLSW.stackProjectionCounter++;
+
+            for (int i = 0; i < 16; i++)
+            {
+                RLSW.stackProjection[iNew][i] = RLSW.stackProjection[iOld][i];
+            }
+
+            RLSW.currentMatrix = &RLSW.stackProjection[iNew];
+        } break;
+        case SW_MODELVIEW:
+        {
+            if (RLSW.stackModelviewCounter >= SW_MAX_MODELVIEW_STACK_SIZE)
+            {
+                RLSW.errCode = SW_STACK_OVERFLOW;
+                return;
+            }
+
+            int iOld = RLSW.stackModelviewCounter - 1;
+            int iNew = RLSW.stackModelviewCounter++;
+
+            for (int i = 0; i < 16; i++)
+            {
+                RLSW.stackModelview[iNew][i] = RLSW.stackModelview[iOld][i];
+            }
+
+            RLSW.currentMatrix = &RLSW.stackModelview[iNew];
+        } break;
+        case SW_TEXTURE:
+        {
+            if (RLSW.stackTextureCounter >= SW_MAX_TEXTURE_STACK_SIZE)
+            {
+                RLSW.errCode = SW_STACK_OVERFLOW;
+                return;
+            }
+
+            int iOld = RLSW.stackTextureCounter - 1;
+            int iNew = RLSW.stackTextureCounter++;
+
+            for (int i = 0; i < 16; i++)
+            {
+                RLSW.stackTexture[iNew][i] = RLSW.stackTexture[iOld][i];
+            }
+
+            RLSW.currentMatrix = &RLSW.stackTexture[iNew];
+        } break;
+        default: break;
+    }
+}
+
+void swPopMatrix(void)
+{
+    switch (RLSW.currentMatrixMode)
+    {
+        case SW_PROJECTION:
+        {
+            if (RLSW.stackProjectionCounter <= 0)
+            {
+                RLSW.errCode = SW_STACK_UNDERFLOW;
+                return;
+            }
+
+            RLSW.currentMatrix = &RLSW.stackProjection[--RLSW.stackProjectionCounter];
+            RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed
+        } break;
+        case SW_MODELVIEW:
+        {
+            if (RLSW.stackModelviewCounter <= 0)
+            {
+                RLSW.errCode = SW_STACK_UNDERFLOW;
+                return;
+            }
+
+            RLSW.currentMatrix = &RLSW.stackModelview[--RLSW.stackModelviewCounter];
+            RLSW.isDirtyMVP = true; //< The MVP is considered to have been changed
+        } break;
+        case SW_TEXTURE:
+        {
+            if (RLSW.stackTextureCounter <= 0)
+            {
+                RLSW.errCode = SW_STACK_UNDERFLOW;
+                return;
+            }
+
+            RLSW.currentMatrix = &RLSW.stackTexture[--RLSW.stackTextureCounter];
+        } break;
+        default: break;
+    }
+}
+
+void swLoadIdentity(void)
+{
+    sw_matrix_id(*RLSW.currentMatrix);
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swTranslatef(float x, float y, float z)
+{
+    sw_matrix_t mat;
+    sw_matrix_id(mat);
+
+    mat[12] = x;
+    mat[13] = y;
+    mat[14] = z;
+
+    sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swRotatef(float angle, float x, float y, float z)
+{
+    angle *= SW_DEG2RAD;
+
+    float lengthSq = x*x + y*y + z*z;
+
+    if ((lengthSq != 1.0f) && (lengthSq != 0.0f))
+    {
+        float invLength = 1.0f/sqrtf(lengthSq);
+        x *= invLength;
+        y *= invLength;
+        z *= invLength;
+    }
+
+    float sinres = sinf(angle);
+    float cosres = cosf(angle);
+    float t = 1.0f - cosres;
+
+    sw_matrix_t mat;
+
+    mat[0] = x*x*t + cosres;
+    mat[1] = y*x*t + z*sinres;
+    mat[2] = z*x*t - y*sinres;
+    mat[3] = 0.0f;
+
+    mat[4] = x*y*t - z*sinres;
+    mat[5] = y*y*t + cosres;
+    mat[6] = z*y*t + x*sinres;
+    mat[7] = 0.0f;
+
+    mat[8] = x*z*t + y*sinres;
+    mat[9] = y*z*t - x*sinres;
+    mat[10] = z*z*t + cosres;
+    mat[11] = 0.0f;
+
+    mat[12] = 0.0f;
+    mat[13] = 0.0f;
+    mat[14] = 0.0f;
+    mat[15] = 1.0f;
+
+    sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swScalef(float x, float y, float z)
+{
+    sw_matrix_t mat;
+
+    mat[0]  = x, mat[1]  = 0, mat[2]  = 0, mat[3]  = 0;
+    mat[4]  = 0, mat[5]  = y, mat[6]  = 0, mat[7]  = 0;
+    mat[8]  = 0, mat[9]  = 0, mat[10] = z, mat[11] = 0;
+    mat[12] = 0, mat[13] = 0, mat[14] = 0, mat[15] = 1;
+
+    sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swMultMatrixf(const float *mat)
+{
+    sw_matrix_mul(*RLSW.currentMatrix, mat, *RLSW.currentMatrix);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swFrustum(double left, double right, double bottom, double top, double znear, double zfar)
+{
+    sw_matrix_t mat;
+
+    double rl = right - left;
+    double tb = top - bottom;
+    double fn = zfar - znear;
+
+    mat[0] = (float)(znear*2.0)/rl;
+    mat[1] = 0.0f;
+    mat[2] = 0.0f;
+    mat[3] = 0.0f;
+
+    mat[4] = 0.0f;
+    mat[5] = (float)(znear*2.0)/tb;
+    mat[6] = 0.0f;
+    mat[7] = 0.0f;
+
+    mat[8] = (float)(right + left)/rl;
+    mat[9] = (float)(top + bottom)/tb;
+    mat[10] = -(float)(zfar + znear)/fn;
+    mat[11] = -1.0f;
+
+    mat[12] = 0.0f;
+    mat[13] = 0.0f;
+    mat[14] = -(zfar*znear*2.0)/fn;
+    mat[15] = 0.0f;
+
+    sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swOrtho(double left, double right, double bottom, double top, double znear, double zfar)
+{
+    sw_matrix_t mat;
+
+    double rl = right - left;
+    double tb = top - bottom;
+    double fn = zfar - znear;
+
+    mat[0] = 2.0f/(float)rl;
+    mat[1] = 0.0f;
+    mat[2] = 0.0f;
+    mat[3] = 0.0f;
+
+    mat[4] = 0.0f;
+    mat[5] = 2.0f/(float)tb;
+    mat[6] = 0.0f;
+    mat[7] = 0.0f;
+
+    mat[8] = 0.0f;
+    mat[9] = 0.0f;
+    mat[10] = -2.0f/(float)fn;
+    mat[11] = 0.0f;
+
+    mat[12] = -(float)(left + right)/rl;
+    mat[13] = -(float)(top + bottom)/tb;
+    mat[14] = -(float)(zfar + znear)/fn;
+    mat[15] = 1.0f;
+
+    sw_matrix_mul(*RLSW.currentMatrix, *RLSW.currentMatrix, mat);
+
+    if (RLSW.currentMatrixMode != SW_TEXTURE) RLSW.isDirtyMVP = true;
+}
+
+void swBegin(SWdraw mode)
+{
+    // Check if the draw mode is valid
+    if (!sw_is_draw_mode_valid(mode))
+    {
+        RLSW.errCode = SW_INVALID_ENUM;
+        return;
+    }
+
+    // Recalculate the MVP if this is needed
+    if (RLSW.isDirtyMVP)
+    {
+        sw_matrix_mul_rst(RLSW.matMVP,
+            RLSW.stackModelview[RLSW.stackModelviewCounter - 1],
+            RLSW.stackProjection[RLSW.stackProjectionCounter - 1]);
+
+        RLSW.isDirtyMVP = false;
+    }
+
+    // Obtain the number of vertices needed for this primitive
+    switch (mode)
+    {
+        case SW_POINTS: RLSW.reqVertices = 1; break;
+        case SW_LINES: RLSW.reqVertices = 2; break;
+        case SW_TRIANGLES: RLSW.reqVertices = 3; break;
+        case SW_QUADS: RLSW.reqVertices = 4; break;
+    }
+
+    // Initialize required values
+    RLSW.vertexCounter = 0;
+    RLSW.drawMode = mode;
+}
+
+void swEnd(void)
+{
+    RLSW.drawMode = (SWdraw)0;
+}
+
+void swVertex2i(int x, int y)
+{
+    const float v[4] = { (float)x, (float)y, 0.0f, 1.0f };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex2f(float x, float y)
+{
+    const float v[4] = { x, y, 0.0f, 1.0f };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex2fv(const float *v)
+{
+    const float v4[4] = { v[0], v[1], 0.0f, 1.0f };
+    sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex3i(int x, int y, int z)
+{
+    const float v[4] = { (float)x, (float)y, (float)z, 1.0f };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex3f(float x, float y, float z)
+{
+    const float v[4] = { x, y, z, 1.0f };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex3fv(const float *v)
+{
+    const float v4[4] = { v[0], v[1], v[2], 1.0f };
+    sw_immediate_push_vertex(v4, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex4i(int x, int y, int z, int w)
+{
+    const float v[4] = { (float)x, (float)y, (float)z, (float)w };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex4f(float x, float y, float z, float w)
+{
+    const float v[4] = { x, y, z, w };
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swVertex4fv(const float *v)
+{
+    sw_immediate_push_vertex(v, RLSW.current.color, RLSW.current.texcoord);
+}
+
+void swColor3ub(uint8_t r, uint8_t g, uint8_t b)
+{
+    float cv[4];
+    cv[0] = (float)r*SW_INV_255;
+    cv[1] = (float)g*SW_INV_255;
+    cv[2] = (float)b*SW_INV_255;
+    cv[3] = 1.0f;
+
+    swColor4fv(cv);
+}
+
+void swColor3ubv(const uint8_t *v)
+{
+    float cv[4];
+    cv[0] = (float)v[0]*SW_INV_255;
+    cv[1] = (float)v[1]*SW_INV_255;
+    cv[2] = (float)v[2]*SW_INV_255;
+    cv[3] = 1.0f;
+
+    swColor4fv(cv);
+}
+
+void swColor3f(float r, float g, float b)
+{
+    float cv[4];
+    cv[0] = r;
+    cv[1] = g;
+    cv[2] = b;
+    cv[3] = 1.0f;
+
+    swColor4fv(cv);
+}
+
+void swColor3fv(const float *v)
+{
+    float cv[4];
+    cv[0] = v[0];
+    cv[1] = v[1];
+    cv[2] = v[2];
+    cv[3] = 1.0f;
+
+    swColor4fv(cv);
+}
+
+void swColor4ub(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+    float cv[4];
+    cv[0] = (float)r*SW_INV_255;
+    cv[1] = (float)g*SW_INV_255;
+    cv[2] = (float)b*SW_INV_255;
+    cv[3] = (float)a*SW_INV_255;
+
+    swColor4fv(cv);
+}
+
+void swColor4ubv(const uint8_t *v)
+{
+    float cv[4];
+    cv[0] = (float)v[0]*SW_INV_255;
+    cv[1] = (float)v[1]*SW_INV_255;
+    cv[2] = (float)v[2]*SW_INV_255;
+    cv[3] = (float)v[3]*SW_INV_255;
+
+    swColor4fv(cv);
+}
+
+void swColor4f(float r, float g, float b, float a)
+{
+    float cv[4];
+    cv[0] = r;
+    cv[1] = g;
+    cv[2] = b;
+    cv[3] = a;
+
+    swColor4fv(cv);
+}
+
+void swColor4fv(const float *v)
+{
+    for (int i = 0; i < 4; i++) RLSW.current.color[i] = v[i];
+}
+
+void swTexCoord2f(float u, float v)
+{
+    const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
+
+    RLSW.current.texcoord[0] = m[0]*u + m[4]*v + m[12];
+    RLSW.current.texcoord[1] = m[1]*u + m[5]*v + m[13];
+}
+
+void swTexCoord2fv(const float *v)
+{
+    const float *m = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
+
+    RLSW.current.texcoord[0] = m[0]*v[0] + m[4]*v[1] + m[12];
+    RLSW.current.texcoord[1] = m[1]*v[0] + m[5]*v[1] + m[13];
+}
+
+void swBindArray(SWarray type, void *buffer)
+{
+    switch (type)
+    {
+        case SW_VERTEX_ARRAY: RLSW.array.positions = (float *)buffer; break;
+        case SW_TEXTURE_COORD_ARRAY: RLSW.array.texcoords = (float *)buffer; break;
+        case SW_COLOR_ARRAY: RLSW.array.colors = (uint8_t *)buffer; break;
+        default: break;
+    }
+}
+
+void swDrawArrays(SWdraw mode, int offset, int count)
+{
+    if (RLSW.array.positions == 0)
+    {
+        RLSW.errCode = SW_INVALID_OPERATION;
+        return;
+    }
+
+    swBegin(mode);
+    {
+        const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
+        const float *defaultTexcoord = RLSW.current.texcoord;
+        const float *defaultColor = RLSW.current.color;
+
+        const float *positions = RLSW.array.positions;
+        const float *texcoords = RLSW.array.texcoords;
+        const uint8_t *colors = RLSW.array.colors;
+
+        int end = offset + count;
+
+        for (int i = offset; i < end; i++)
+        {
+            float u, v;
+            if (texcoords)
+            {
+                int idx = 2*i;
+                u = texcoords[idx];
+                v = texcoords[idx + 1];
+            }
+            else
+            {
+                u = defaultTexcoord[0];
+                v = defaultTexcoord[1];
+            }
+
+            float texcoord[2];
+            texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12];
+            texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13];
+
+            float color[4] = {
+                defaultColor[0],
+                defaultColor[1],
+                defaultColor[2],
+                defaultColor[3]
+            };
+
+            if (colors)
+            {
+                int idx = 4*i;
+                color[0] *= (float)colors[idx]*SW_INV_255;
+                color[1] *= (float)colors[idx + 1]*SW_INV_255;
+                color[2] *= (float)colors[idx + 2]*SW_INV_255;
+                color[3] *= (float)colors[idx + 3]*SW_INV_255;
+            }
+
+            int idx = 3*i;
+            float position[4] = {
+                positions[idx],
+                positions[idx + 1],
+                positions[idx + 2],
+                1.0f
+            };
+
+            sw_immediate_push_vertex(position, color, texcoord);
+        }
+    }
+    swEnd();
+}
+
+void swDrawElements(SWdraw mode, int count, int type, const void *indices)
+{
+    if (RLSW.array.positions == 0)
+    {
+        RLSW.errCode = SW_INVALID_OPERATION;
+        return;
+    }
+
+    if (count < 0)
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    const uint8_t *indicesUb = NULL;
+    const uint16_t *indicesUs = NULL;
+    const uint32_t *indicesUi = NULL;
+
+    switch (type)
+    {
+        case SW_UNSIGNED_BYTE:
+            indicesUb = (const uint8_t *)indices;
+            break;
+        case SW_UNSIGNED_SHORT:
+            indicesUs = (const uint16_t *)indices;
+            break;
+        case SW_UNSIGNED_INT:
+            indicesUi = (const uint32_t *)indices;
+            break;
+        default:
+            RLSW.errCode = SW_INVALID_ENUM;
+            return;
+    }
+
+    swBegin(mode);
+    {
+        const float *texMatrix = RLSW.stackTexture[RLSW.stackTextureCounter - 1];
+        const float *defaultTexcoord = RLSW.current.texcoord;
+        const float *defaultColor = RLSW.current.color;
+
+        const float *positions = RLSW.array.positions;
+        const float *texcoords = RLSW.array.texcoords;
+        const uint8_t *colors = RLSW.array.colors;
+
+        for (int i = 0; i < count; i++)
+        {
+            int index = indicesUb? indicesUb[i] :
+                       (indicesUs? indicesUs[i] : indicesUi[i]);
+
+            float u, v;
+            if (texcoords)
+            {
+                int idx = 2*index;
+                u = texcoords[idx];
+                v = texcoords[idx + 1];
+            }
+            else
+            {
+                u = defaultTexcoord[0];
+                v = defaultTexcoord[1];
+            }
+
+            float texcoord[2];
+            texcoord[0] = texMatrix[0]*u + texMatrix[4]*v + texMatrix[12];
+            texcoord[1] = texMatrix[1]*u + texMatrix[5]*v + texMatrix[13];
+
+            float color[4] = {
+                defaultColor[0],
+                defaultColor[1],
+                defaultColor[2],
+                defaultColor[3]
+            };
+
+            if (colors)
+            {
+                int idx = 4*index;
+                color[0] *= (float)colors[idx]*SW_INV_255;
+                color[1] *= (float)colors[idx + 1]*SW_INV_255;
+                color[2] *= (float)colors[idx + 2]*SW_INV_255;
+                color[3] *= (float)colors[idx + 3]*SW_INV_255;
+            }
+
+            int idx = 3*index;
+            float position[4] = {
+                positions[idx],
+                positions[idx + 1],
+                positions[idx + 2],
+                1.0f
+            };
+
+            sw_immediate_push_vertex(position, color, texcoord);
+        }
+    }
+    swEnd();
+}
+
+void swGenTextures(int count, uint32_t *textures)
+{
+    if ((count == 0) || (textures == NULL)) return;
+
+    for (int i = 0; i < count; i++)
+    {
+        if (RLSW.loadedTextureCount >= SW_MAX_TEXTURES)
+        {
+            RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory, not really stack overflow
+            return;
+        }
+
+        uint32_t id = 0;
+        if (RLSW.freeTextureIdCount > 0) id = RLSW.freeTextureIds[--RLSW.freeTextureIdCount];
+        else id = RLSW.loadedTextureCount++;
+
+        RLSW.loadedTextures[id] = RLSW.loadedTextures[0];
+        textures[i] = id;
+    }
+}
+
+void swDeleteTextures(int count, uint32_t *textures)
+{
+    if ((count == 0) || (textures == NULL)) return;
+
+    for (int i = 0; i < count; i++)
+    {
+        if (!sw_is_texture_valid(textures[i]))
+        {
+            RLSW.errCode = SW_INVALID_VALUE;
+            continue;
+        }
+
+        SW_FREE(RLSW.loadedTextures[textures[i]].pixels);
+
+        RLSW.loadedTextures[textures[i]].pixels = NULL;
+        RLSW.freeTextureIds[RLSW.freeTextureIdCount++] = textures[i];
+    }
+}
+
+void swTexImage2D(int width, int height, SWformat format, SWtype type, const void *data)
+{
+    uint32_t id = RLSW.currentTexture;
+
+    if (!sw_is_texture_valid(id))
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    int pixelFormat = sw_get_pixel_format(format, type);
+
+    if (pixelFormat <= SW_PIXELFORMAT_UNKNOWN)
+    {
+        RLSW.errCode = SW_INVALID_ENUM;
+        return;
+    }
+
+    sw_texture_t *texture = &RLSW.loadedTextures[id];
+
+    int size = width*height;
+    texture->pixels = SW_MALLOC(4*size);
+
+    if (texture->pixels == NULL)
+    {
+        RLSW.errCode = SW_STACK_OVERFLOW; // WARNING: Out of memory...
+        return;
+    }
+
+    for (int i = 0; i < size; i++)
+    {
+        uint32_t *dst = &((uint32_t*)texture->pixels)[i];
+        sw_get_pixel((uint8_t*)dst, data, i, pixelFormat);
+    }
+
+    texture->width = width;
+    texture->height = height;
+    texture->wMinus1 = width - 1;
+    texture->hMinus1 = height - 1;
+    texture->tx = 1.0f/width;
+    texture->ty = 1.0f/height;
+}
+
+void swTexParameteri(int param, int value)
+{
+    uint32_t id = RLSW.currentTexture;
+
+    if (!sw_is_texture_valid(id))
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    sw_texture_t *texture = &RLSW.loadedTextures[id];
+
+    switch (param)
+    {
+        case SW_TEXTURE_MIN_FILTER:
+        {
+            if (!sw_is_texture_filter_valid(value))
+            {
+                RLSW.errCode = SW_INVALID_ENUM;
+                return;
+            }
+
+            texture->minFilter = (SWfilter)value;
+        } break;
+        case SW_TEXTURE_MAG_FILTER:
+        {
+            if (!sw_is_texture_filter_valid(value))
+            {
+                RLSW.errCode = SW_INVALID_ENUM;
+                return;
+            }
+
+            texture->magFilter = (SWfilter)value;
+        } break;
+        case SW_TEXTURE_WRAP_S:
+        {
+            if (!sw_is_texture_wrap_valid(value))
+            {
+                RLSW.errCode = SW_INVALID_ENUM;
+                return;
+            }
+
+            texture->sWrap = (SWwrap)value;
+        } break;
+        case SW_TEXTURE_WRAP_T:
+        {
+            if (!sw_is_texture_wrap_valid(value))
+            {
+                RLSW.errCode = SW_INVALID_ENUM;
+                return;
+            }
+
+            texture->tWrap = (SWwrap)value;
+        } break;
+        default: RLSW.errCode = SW_INVALID_ENUM; break;
+    }
+}
+
+void swBindTexture(uint32_t id)
+{
+    if (id >= SW_MAX_TEXTURES)
+    {
+        RLSW.errCode = SW_INVALID_VALUE;
+        return;
+    }
+
+    if (RLSW.loadedTextures[id].pixels == NULL)
+    {
+        RLSW.errCode = SW_INVALID_OPERATION;
+        return;
+    }
+
+    RLSW.currentTexture = id;
+}
+
+#endif // RLSW_IMPLEMENTATION
diff --git a/raylib/src/external/rprand.h b/raylib/src/external/rprand.h
--- a/raylib/src/external/rprand.h
+++ b/raylib/src/external/rprand.h
@@ -15,15 +15,15 @@
 *       - Support 64 bits generation
 *
 *   ADDITIONAL NOTES:
-*     This library implements two pseudo-random number generation algorithms: 
+*     This library implements two pseudo-random number generation algorithms:
 *
 *         - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c
 *         - SplitMix64   : https://prng.di.unimi.it/splitmix64.c
 *
 *     SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed
 *
-*     It's suggested to use SplitMix64 to initialize the state of the generators starting from 
-*     a 64-bit seed, as research has shown that initialization must be performed with a generator 
+*     It's suggested to use SplitMix64 to initialize the state of the generators starting from
+*     a 64-bit seed, as research has shown that initialization must be performed with a generator
 *     radically different in nature from the one initialized to avoid correlation on similar seeds.
 *
 *   CONFIGURATION:
@@ -31,7 +31,7 @@
 *           Generates the implementation of the library into the included file.
 *           If not defined, the library is in header only mode and can be included in other headers
 *           or source files without problems. But only ONE file should hold the implementation.
-* 
+*
 *   DEPENDENCIES: none
 *
 *   VERSIONS HISTORY:
@@ -90,7 +90,7 @@
     #define RPRAND_FREE(ptr)          free(ptr)
 #endif
 
-// Simple log system to avoid RPNG_LOG() calls if required
+// Simple log system to avoid log calls if required
 // NOTE: Avoiding those calls, also avoids const strings memory usage
 #define RPRAND_SHOW_LOG_INFO
 #if defined(RPNG_SHOW_LOG_INFO)
@@ -153,16 +153,16 @@
     0x218b21e5,
     0xaa91febd,
     0x976414d4
-};        
+};
 
 //----------------------------------------------------------------------------------
-// Module internal functions declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static uint32_t rprand_xoshiro(void);           // Xoshiro128** generator (uses global rprand_state)
 static uint64_t rprand_splitmix64(void);        // SplitMix64 generator (uses seed to generate rprand_state)
 
 //----------------------------------------------------------------------------------
-// Module functions definition
+// Module Functions Definition
 //----------------------------------------------------------------------------------
 // Set rprand_state for Xoshiro128**
 // NOTE: We use a custom generation algorithm using SplitMix64
@@ -190,8 +190,8 @@
 int *rprand_load_sequence(unsigned int count, int min, int max)
 {
     int *sequence = NULL;
-    
-    if (count > (unsigned int)(abs(max - min) + 1)) 
+
+    if (count > (unsigned int)(abs(max - min) + 1))
     {
         RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n");
         //count = (max - min);
@@ -236,7 +236,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module internal functions definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 static inline uint32_t rprand_rotate_left(const uint32_t x, int k)
 {
@@ -244,26 +244,26 @@
 }
 
 // Xoshiro128** generator info:
-//   
+//
 //   Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
-//   
+//
 //   To the extent possible under law, the author has dedicated all copyright
 //   and related and neighboring rights to this software to the public domain
 //   worldwide. This software is distributed without any warranty.
-//   
+//
 //   See <http://creativecommons.org/publicdomain/zero/1.0/>.
-//   
+//
 //   This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid
 //   generators. It has excellent speed, a state size (128 bits) that is
 //   large enough for mild parallelism, and it passes all tests we are aware
 //   of.
-// 
+//
 //   Note that version 1.0 had mistakenly s[0] instead of s[1] as state
 //   word passed to the scrambler.
-// 
+//
 //   For generating just single-precision (i.e., 32-bit) floating-point
 //   numbers, xoshiro128+ is even faster.
-// 
+//
 //   The state must be seeded so that it is not everywhere zero.
 //
 uint32_t rprand_xoshiro(void)
@@ -275,29 +275,29 @@
     rprand_state[3] ^= rprand_state[1];
     rprand_state[1] ^= rprand_state[2];
     rprand_state[0] ^= rprand_state[3];
-    
+
     rprand_state[2] ^= t;
-    
+
     rprand_state[3] = rprand_rotate_left(rprand_state[3], 11);
 
     return result;
 }
 
 // SplitMix64 generator info:
-//   
+//
 //   Written in 2015 by Sebastiano Vigna (vigna@acm.org)
-//   
+//
 //   To the extent possible under law, the author has dedicated all copyright
 //   and related and neighboring rights to this software to the public domain
 //   worldwide. This software is distributed without any warranty.
-//   
+//
 //   See <http://creativecommons.org/publicdomain/zero/1.0/>.
-//   
 //
+//
 //   This is a fixed-increment version of Java 8's SplittableRandom generator
 //   See http://dx.doi.org/10.1145/2714064.2660195 and
 //   http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
-//   
+//
 //   It is a very fast generator passing BigCrush, and it can be useful if
 //   for some reason you absolutely want 64 bits of state.
 uint64_t rprand_splitmix64()
diff --git a/raylib/src/external/sdefl.h b/raylib/src/external/sdefl.h
--- a/raylib/src/external/sdefl.h
+++ b/raylib/src/external/sdefl.h
@@ -198,7 +198,7 @@
 static int
 sdefl_ilog2(int n) {
   if (!n) return 0;
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER)   // @raysan5, address PR #5367
   unsigned long msbp = 0;
   _BitScanReverse(&msbp, (unsigned long)n);
   return (int)msbp;
diff --git a/raylib/src/external/sinfl.h b/raylib/src/external/sinfl.h
--- a/raylib/src/external/sinfl.h
+++ b/raylib/src/external/sinfl.h
@@ -171,10 +171,11 @@
 
 static int
 sinfl_bsr(unsigned n) {
-#if defined(_MSC_VER) && !defined(__clang__)
-  _BitScanReverse(&n, n);
-  return n;
-#elif defined(__GNUC__) || defined(__clang__)
+#if defined(_MSC_VER) && !defined(__llvm__) && !defined(__INTEL_COMPILER)   // @raysan5, address PR #5367
+  unsigned long uln = 0;
+  _BitScanReverse(&uln, n);
+  return (int)(uln);
+#else // defined(__GNUC__) || defined(__clang__) || defined(__TINYC__)
   return 31 - __builtin_clz(n);
 #endif
 }
diff --git a/raylib/src/external/stb_image.h b/raylib/src/external/stb_image.h
--- a/raylib/src/external/stb_image.h
+++ b/raylib/src/external/stb_image.h
@@ -1,4 +1,4 @@
-/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb
+/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
                                   no warranty implied; use at your own risk
 
    Do this:
@@ -48,6 +48,8 @@
 
 RECENT REVISION HISTORY:
 
+      2.30  (2024-05-31) avoid erroneous gcc warning
+      2.29  (2023-05-xx) optimizations
       2.28  (2023-01-29) many error fixes, security errors, just tons of stuff
       2.27  (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
       2.26  (2020-07-13) many minor fixes
@@ -1072,8 +1074,8 @@
    return a <= INT_MAX - b;
 }
 
-// returns 1 if the product of two signed shorts is valid, 0 on overflow.
-static int stbi__mul2shorts_valid(short a, short b)
+// returns 1 if the product of two ints fits in a signed short, 0 on overflow.
+static int stbi__mul2shorts_valid(int a, int b)
 {
    if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow
    if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid
@@ -3384,13 +3386,13 @@
    return 1;
 }
 
-static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
+static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j)
 {
    // some JPEGs have junk at end, skip over it but if we find what looks
    // like a valid marker, resume there
    while (!stbi__at_eof(j->s)) {
-      int x = stbi__get8(j->s);
-      while (x == 255) { // might be a marker
+      stbi_uc x = stbi__get8(j->s);
+      while (x == 0xff) { // might be a marker
          if (stbi__at_eof(j->s)) return STBI__MARKER_none;
          x = stbi__get8(j->s);
          if (x != 0x00 && x != 0xff) {
@@ -4176,6 +4178,7 @@
 {
    stbi_uc *zbuffer, *zbuffer_end;
    int num_bits;
+   int hit_zeof_once;
    stbi__uint32 code_buffer;
 
    char *zout;
@@ -4242,9 +4245,20 @@
    int b,s;
    if (a->num_bits < 16) {
       if (stbi__zeof(a)) {
-         return -1;   /* report error for unexpected end of data. */
+         if (!a->hit_zeof_once) {
+            // This is the first time we hit eof, insert 16 extra padding btis
+            // to allow us to keep going; if we actually consume any of them
+            // though, that is invalid data. This is caught later.
+            a->hit_zeof_once = 1;
+            a->num_bits += 16; // add 16 implicit zero bits
+         } else {
+            // We already inserted our extra 16 padding bits and are again
+            // out, this stream is actually prematurely terminated.
+            return -1;
+         }
+      } else {
+         stbi__fill_bits(a);
       }
-      stbi__fill_bits(a);
    }
    b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
    if (b) {
@@ -4309,6 +4323,13 @@
          int len,dist;
          if (z == 256) {
             a->zout = zout;
+            if (a->hit_zeof_once && a->num_bits < 16) {
+               // The first time we hit zeof, we inserted 16 extra zero bits into our bit
+               // buffer so the decoder can just do its speculative decoding. But if we
+               // actually consumed any of those bits (which is the case when num_bits < 16),
+               // the stream actually read past the end so it is malformed.
+               return stbi__err("unexpected end","Corrupt PNG");
+            }
             return 1;
          }
          if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data
@@ -4320,7 +4341,7 @@
          dist = stbi__zdist_base[z];
          if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
          if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
-         if (zout + len > a->zout_end) {
+         if (len > a->zout_end - zout) {
             if (!stbi__zexpand(a, zout, len)) return 0;
             zout = a->zout;
          }
@@ -4464,6 +4485,7 @@
       if (!stbi__parse_zlib_header(a)) return 0;
    a->num_bits = 0;
    a->code_buffer = 0;
+   a->hit_zeof_once = 0;
    do {
       final = stbi__zreceive(a,1);
       type = stbi__zreceive(a,2);
@@ -4619,9 +4641,8 @@
    STBI__F_up=2,
    STBI__F_avg=3,
    STBI__F_paeth=4,
-   // synthetic filters used for first scanline to avoid needing a dummy row of 0s
-   STBI__F_avg_first,
-   STBI__F_paeth_first
+   // synthetic filter used for first scanline to avoid needing a dummy row of 0s
+   STBI__F_avg_first
 };
 
 static stbi_uc first_row_filter[5] =
@@ -4630,29 +4651,56 @@
    STBI__F_sub,
    STBI__F_none,
    STBI__F_avg_first,
-   STBI__F_paeth_first
+   STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub
 };
 
 static int stbi__paeth(int a, int b, int c)
 {
-   int p = a + b - c;
-   int pa = abs(p-a);
-   int pb = abs(p-b);
-   int pc = abs(p-c);
-   if (pa <= pb && pa <= pc) return a;
-   if (pb <= pc) return b;
-   return c;
+   // This formulation looks very different from the reference in the PNG spec, but is
+   // actually equivalent and has favorable data dependencies and admits straightforward
+   // generation of branch-free code, which helps performance significantly.
+   int thresh = c*3 - (a + b);
+   int lo = a < b ? a : b;
+   int hi = a < b ? b : a;
+   int t0 = (hi <= thresh) ? lo : c;
+   int t1 = (thresh <= lo) ? hi : t0;
+   return t1;
 }
 
 static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
 
+// adds an extra all-255 alpha channel
+// dest == src is legal
+// img_n must be 1 or 3
+static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n)
+{
+   int i;
+   // must process data backwards since we allow dest==src
+   if (img_n == 1) {
+      for (i=x-1; i >= 0; --i) {
+         dest[i*2+1] = 255;
+         dest[i*2+0] = src[i];
+      }
+   } else {
+      STBI_ASSERT(img_n == 3);
+      for (i=x-1; i >= 0; --i) {
+         dest[i*4+3] = 255;
+         dest[i*4+2] = src[i*3+2];
+         dest[i*4+1] = src[i*3+1];
+         dest[i*4+0] = src[i*3+0];
+      }
+   }
+}
+
 // create the png data from post-deflated data
 static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
 {
-   int bytes = (depth == 16? 2 : 1);
+   int bytes = (depth == 16 ? 2 : 1);
    stbi__context *s = a->s;
    stbi__uint32 i,j,stride = x*out_n*bytes;
    stbi__uint32 img_len, img_width_bytes;
+   stbi_uc *filter_buf;
+   int all_ok = 1;
    int k;
    int img_n = s->img_n; // copy it into a local for later
 
@@ -4664,8 +4712,11 @@
    a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
    if (!a->out) return stbi__err("outofmem", "Out of memory");
 
+   // note: error exits here don't need to clean up a->out individually,
+   // stbi__do_png always does on error.
    if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
    img_width_bytes = (((img_n * x * depth) + 7) >> 3);
+   if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG");
    img_len = (img_width_bytes + 1) * y;
 
    // we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
@@ -4673,189 +4724,137 @@
    // so just check for raw_len < img_len always.
    if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
 
+   // Allocate two scan lines worth of filter workspace buffer.
+   filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0);
+   if (!filter_buf) return stbi__err("outofmem", "Out of memory");
+
+   // Filtering for low-bit-depth images
+   if (depth < 8) {
+      filter_bytes = 1;
+      width = img_width_bytes;
+   }
+
    for (j=0; j < y; ++j) {
-      stbi_uc *cur = a->out + stride*j;
-      stbi_uc *prior;
+      // cur/prior filter buffers alternate
+      stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes;
+      stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes;
+      stbi_uc *dest = a->out + stride*j;
+      int nk = width * filter_bytes;
       int filter = *raw++;
 
-      if (filter > 4)
-         return stbi__err("invalid filter","Corrupt PNG");
-
-      if (depth < 8) {
-         if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
-         cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
-         filter_bytes = 1;
-         width = img_width_bytes;
+      // check filter type
+      if (filter > 4) {
+         all_ok = stbi__err("invalid filter","Corrupt PNG");
+         break;
       }
-      prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
 
       // if first row, use special filter that doesn't sample previous row
       if (j == 0) filter = first_row_filter[filter];
 
-      // handle first byte explicitly
-      for (k=0; k < filter_bytes; ++k) {
-         switch (filter) {
-            case STBI__F_none       : cur[k] = raw[k]; break;
-            case STBI__F_sub        : cur[k] = raw[k]; break;
-            case STBI__F_up         : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
-            case STBI__F_avg        : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
-            case STBI__F_paeth      : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
-            case STBI__F_avg_first  : cur[k] = raw[k]; break;
-            case STBI__F_paeth_first: cur[k] = raw[k]; break;
-         }
-      }
-
-      if (depth == 8) {
-         if (img_n != out_n)
-            cur[img_n] = 255; // first pixel
-         raw += img_n;
-         cur += out_n;
-         prior += out_n;
-      } else if (depth == 16) {
-         if (img_n != out_n) {
-            cur[filter_bytes]   = 255; // first pixel top byte
-            cur[filter_bytes+1] = 255; // first pixel bottom byte
-         }
-         raw += filter_bytes;
-         cur += output_bytes;
-         prior += output_bytes;
-      } else {
-         raw += 1;
-         cur += 1;
-         prior += 1;
+      // perform actual filtering
+      switch (filter) {
+      case STBI__F_none:
+         memcpy(cur, raw, nk);
+         break;
+      case STBI__F_sub:
+         memcpy(cur, raw, filter_bytes);
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]);
+         break;
+      case STBI__F_up:
+         for (k = 0; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + prior[k]);
+         break;
+      case STBI__F_avg:
+         for (k = 0; k < filter_bytes; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1));
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1));
+         break;
+      case STBI__F_paeth:
+         for (k = 0; k < filter_bytes; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0)
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes]));
+         break;
+      case STBI__F_avg_first:
+         memcpy(cur, raw, filter_bytes);
+         for (k = filter_bytes; k < nk; ++k)
+            cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1));
+         break;
       }
 
-      // this is a little gross, so that we don't switch per-pixel or per-component
-      if (depth < 8 || img_n == out_n) {
-         int nk = (width - 1)*filter_bytes;
-         #define STBI__CASE(f) \
-             case f:     \
-                for (k=0; k < nk; ++k)
-         switch (filter) {
-            // "none" filter turns into a memcpy here; make that explicit.
-            case STBI__F_none:         memcpy(cur, raw, nk); break;
-            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
-            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
-            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
-            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
-            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
-            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
-         }
-         #undef STBI__CASE
-         raw += nk;
-      } else {
-         STBI_ASSERT(img_n+1 == out_n);
-         #define STBI__CASE(f) \
-             case f:     \
-                for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
-                   for (k=0; k < filter_bytes; ++k)
-         switch (filter) {
-            STBI__CASE(STBI__F_none)         { cur[k] = raw[k]; } break;
-            STBI__CASE(STBI__F_sub)          { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
-            STBI__CASE(STBI__F_up)           { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
-            STBI__CASE(STBI__F_avg)          { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
-            STBI__CASE(STBI__F_paeth)        { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
-            STBI__CASE(STBI__F_avg_first)    { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
-            STBI__CASE(STBI__F_paeth_first)  { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
-         }
-         #undef STBI__CASE
-
-         // the loop above sets the high byte of the pixels' alpha, but for
-         // 16 bit png files we also need the low byte set. we'll do that here.
-         if (depth == 16) {
-            cur = a->out + stride*j; // start at the beginning of the row again
-            for (i=0; i < x; ++i,cur+=output_bytes) {
-               cur[filter_bytes+1] = 255;
-            }
-         }
-      }
-   }
+      raw += nk;
 
-   // we make a separate pass to expand bits to pixels; for performance,
-   // this could run two scanlines behind the above code, so it won't
-   // intefere with filtering but will still be in the cache.
-   if (depth < 8) {
-      for (j=0; j < y; ++j) {
-         stbi_uc *cur = a->out + stride*j;
-         stbi_uc *in  = a->out + stride*j + x*out_n - img_width_bytes;
-         // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
-         // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
+      // expand decoded bits in cur to dest, also adding an extra alpha channel if desired
+      if (depth < 8) {
          stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
-
-         // note that the final byte might overshoot and write more data than desired.
-         // we can allocate enough data that this never writes out of memory, but it
-         // could also overwrite the next scanline. can it overwrite non-empty data
-         // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
-         // so we need to explicitly clamp the final ones
+         stbi_uc *in = cur;
+         stbi_uc *out = dest;
+         stbi_uc inb = 0;
+         stbi__uint32 nsmp = x*img_n;
 
+         // expand bits to bytes first
          if (depth == 4) {
-            for (k=x*img_n; k >= 2; k-=2, ++in) {
-               *cur++ = scale * ((*in >> 4)       );
-               *cur++ = scale * ((*in     ) & 0x0f);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 1) == 0) inb = *in++;
+               *out++ = scale * (inb >> 4);
+               inb <<= 4;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 4)       );
          } else if (depth == 2) {
-            for (k=x*img_n; k >= 4; k-=4, ++in) {
-               *cur++ = scale * ((*in >> 6)       );
-               *cur++ = scale * ((*in >> 4) & 0x03);
-               *cur++ = scale * ((*in >> 2) & 0x03);
-               *cur++ = scale * ((*in     ) & 0x03);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 3) == 0) inb = *in++;
+               *out++ = scale * (inb >> 6);
+               inb <<= 2;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 6)       );
-            if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
-            if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
-         } else if (depth == 1) {
-            for (k=x*img_n; k >= 8; k-=8, ++in) {
-               *cur++ = scale * ((*in >> 7)       );
-               *cur++ = scale * ((*in >> 6) & 0x01);
-               *cur++ = scale * ((*in >> 5) & 0x01);
-               *cur++ = scale * ((*in >> 4) & 0x01);
-               *cur++ = scale * ((*in >> 3) & 0x01);
-               *cur++ = scale * ((*in >> 2) & 0x01);
-               *cur++ = scale * ((*in >> 1) & 0x01);
-               *cur++ = scale * ((*in     ) & 0x01);
+         } else {
+            STBI_ASSERT(depth == 1);
+            for (i=0; i < nsmp; ++i) {
+               if ((i & 7) == 0) inb = *in++;
+               *out++ = scale * (inb >> 7);
+               inb <<= 1;
             }
-            if (k > 0) *cur++ = scale * ((*in >> 7)       );
-            if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
-            if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
-            if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
-            if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
-            if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
-            if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
          }
-         if (img_n != out_n) {
-            int q;
-            // insert alpha = 255
-            cur = a->out + stride*j;
+
+         // insert alpha=255 values if desired
+         if (img_n != out_n)
+            stbi__create_png_alpha_expand8(dest, dest, x, img_n);
+      } else if (depth == 8) {
+         if (img_n == out_n)
+            memcpy(dest, cur, x*img_n);
+         else
+            stbi__create_png_alpha_expand8(dest, cur, x, img_n);
+      } else if (depth == 16) {
+         // convert the image data from big-endian to platform-native
+         stbi__uint16 *dest16 = (stbi__uint16*)dest;
+         stbi__uint32 nsmp = x*img_n;
+
+         if (img_n == out_n) {
+            for (i = 0; i < nsmp; ++i, ++dest16, cur += 2)
+               *dest16 = (cur[0] << 8) | cur[1];
+         } else {
+            STBI_ASSERT(img_n+1 == out_n);
             if (img_n == 1) {
-               for (q=x-1; q >= 0; --q) {
-                  cur[q*2+1] = 255;
-                  cur[q*2+0] = cur[q];
+               for (i = 0; i < x; ++i, dest16 += 2, cur += 2) {
+                  dest16[0] = (cur[0] << 8) | cur[1];
+                  dest16[1] = 0xffff;
                }
             } else {
                STBI_ASSERT(img_n == 3);
-               for (q=x-1; q >= 0; --q) {
-                  cur[q*4+3] = 255;
-                  cur[q*4+2] = cur[q*3+2];
-                  cur[q*4+1] = cur[q*3+1];
-                  cur[q*4+0] = cur[q*3+0];
+               for (i = 0; i < x; ++i, dest16 += 4, cur += 6) {
+                  dest16[0] = (cur[0] << 8) | cur[1];
+                  dest16[1] = (cur[2] << 8) | cur[3];
+                  dest16[2] = (cur[4] << 8) | cur[5];
+                  dest16[3] = 0xffff;
                }
             }
          }
       }
-   } else if (depth == 16) {
-      // force the image data from big-endian to platform-native.
-      // this is done in a separate pass due to the decoding relying
-      // on the data being untouched, but could probably be done
-      // per-line during decode if care is taken.
-      stbi_uc *cur = a->out;
-      stbi__uint16 *cur16 = (stbi__uint16*)cur;
-
-      for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
-         *cur16 = (cur[0] << 8) | cur[1];
-      }
    }
 
+   STBI_FREE(filter_buf);
+   if (!all_ok) return 0;
+
    return 1;
 }
 
@@ -5161,9 +5160,11 @@
                // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
                if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
                if (z->depth == 16) {
-                  for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
+                  for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
+                     tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
                } else {
-                  for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
+                  for (k = 0; k < s->img_n && k < 3; ++k)
+                     tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
                }
             }
             break;
diff --git a/raylib/src/external/stb_image_resize2.h b/raylib/src/external/stb_image_resize2.h
--- a/raylib/src/external/stb_image_resize2.h
+++ b/raylib/src/external/stb_image_resize2.h
@@ -1,4 +1,4 @@
-/* stb_image_resize2 - v2.10 - public domain image resizing
+/* stb_image_resize2 - v2.12 - public domain image resizing
 
    by Jeff Roberts (v2) and Jorge L Rodriguez
    http://github.com/nothings/stb
@@ -11,35 +11,6 @@
          #define STB_IMAGE_RESIZE_IMPLEMENTATION
       before the #include. That will create the implementation in that file.
 
-   PORTING FROM VERSION 1
-
-      The API has changed. You can continue to use the old version of stb_image_resize.h,
-      which is available in the "deprecated/" directory.
-
-      If you're using the old simple-to-use API, porting is straightforward.
-      (For more advanced APIs, read the documentation.)
-
-        stbir_resize_uint8():
-          - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout`
-
-        stbir_resize_float():
-          - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout`
-
-        stbir_resize_uint8_srgb():
-          - function name is unchanged
-          - cast channel count to `stbir_pixel_layout`
-          - above is sufficient unless your image has alpha and it's not RGBA/BGRA
-            - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode
-
-        stbir_resize_uint8_srgb_edgemode()
-          - switch to the "medium complexity" API
-          - stbir_resize(), very similar API but a few more parameters:
-            - pixel_layout: cast channel count to `stbir_pixel_layout`
-            - data_type:    STBIR_TYPE_UINT8_SRGB
-            - edge:         unchanged (STBIR_EDGE_WRAP, etc.)
-            - filter:       STBIR_FILTER_DEFAULT
-          - which channel is alpha is specified in stbir_pixel_layout, see enum for details
-
    EASY API CALLS:
      Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge.
 
@@ -296,6 +267,34 @@
       ASSERT
          Define STBIR_ASSERT(boolval) to override assert() and not use assert.h
 
+     PORTING FROM VERSION 1
+        The API has changed. You can continue to use the old version of stb_image_resize.h,
+        which is available in the "deprecated/" directory.
+
+        If you're using the old simple-to-use API, porting is straightforward.
+        (For more advanced APIs, read the documentation.)
+
+          stbir_resize_uint8():
+            - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout`
+
+          stbir_resize_float():
+            - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout`
+
+          stbir_resize_uint8_srgb():
+            - function name is unchanged
+            - cast channel count to `stbir_pixel_layout`
+            - above is sufficient unless your image has alpha and it's not RGBA/BGRA
+              - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode
+
+          stbir_resize_uint8_srgb_edgemode()
+            - switch to the "medium complexity" API
+            - stbir_resize(), very similar API but a few more parameters:
+              - pixel_layout: cast channel count to `stbir_pixel_layout`
+              - data_type:    STBIR_TYPE_UINT8_SRGB
+              - edge:         unchanged (STBIR_EDGE_WRAP, etc.)
+              - filter:       STBIR_FILTER_DEFAULT
+            - which channel is alpha is specified in stbir_pixel_layout, see enum for details
+
       FUTURE TODOS
         *  For polyphase integral filters, we just memcpy the coeffs to dupe
            them, but we should indirect and use the same coeff memory.
@@ -328,6 +327,10 @@
       Nathan Reed: warning fixes for 1.0
 
    REVISIONS
+      2.12 (2024-10-18) fix incorrect use of user_data with STBIR_FREE
+      2.11 (2024-09-08) fix harmless asan warnings in 2-channel and 3-channel mode
+                          with AVX-2, fix some weird scaling edge conditions with
+                          point sample mode.
       2.10 (2024-07-27) fix the defines GCC and mingw for loop unroll control,
                           fix MSVC 32-bit arm half float routines.
       2.09 (2024-06-19) fix the defines for 32-bit ARM GCC builds (was selecting
@@ -3247,6 +3250,7 @@
 
   first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f));
   last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f));
+  if ( last < first ) last = first; // point sample mode can span a value *right* at 0.5, and cause these to cross
 
   if ( edge == STBIR_EDGE_WRAP )
   {
@@ -3282,6 +3286,11 @@
 
     stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge );
 
+    // make sure we never generate a range larger than our precalculated coeff width
+    //   this only happens in point sample mode, but it's a good safe thing to do anyway
+    if ( ( in_last_pixel - in_first_pixel + 1 ) > coefficient_width )
+      in_last_pixel = in_first_pixel + coefficient_width - 1;
+
     last_non_zero = -1;
     for (i = 0; i <= in_last_pixel - in_first_pixel; i++)
     {
@@ -3317,19 +3326,22 @@
   }
 }
 
-static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff )
+static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff, int max_width )
 {
   if ( new_pixel <= contribs->n1 )  // before the end
   {
     if ( new_pixel < contribs->n0 ) // before the front?
     {
-      int j, o = contribs->n0 - new_pixel;
-      for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- )
-        coeffs[ j + o ] = coeffs[ j ];
-      for ( j = 1 ; j < o ; j-- )
-        coeffs[ j ] = coeffs[ 0 ];
-      coeffs[ 0 ] = new_coeff;
-      contribs->n0 = new_pixel;
+      if ( ( contribs->n1 - new_pixel + 1 ) <= max_width )
+      { 
+        int j, o = contribs->n0 - new_pixel;
+        for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- )
+          coeffs[ j + o ] = coeffs[ j ];
+        for ( j = 1 ; j < o ; j-- )
+          coeffs[ j ] = coeffs[ 0 ];
+        coeffs[ 0 ] = new_coeff;
+        contribs->n0 = new_pixel;
+      }
     }
     else
     {
@@ -3338,12 +3350,15 @@
   }
   else
   {
-    int j, e = new_pixel - contribs->n0;
-    for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any
-      coeffs[j] = 0;
+    if ( ( new_pixel - contribs->n0 + 1 ) <= max_width )
+    {
+      int j, e = new_pixel - contribs->n0;
+      for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any
+        coeffs[j] = 0;
 
-    coeffs[ e ] = new_coeff;
-    contribs->n1 = new_pixel;
+      coeffs[ e ] = new_coeff;
+      contribs->n1 = new_pixel;
+    }
   }
 }
 
@@ -3522,6 +3537,7 @@
 
   coeffs = coefficient_group;
   contribs = contributors;
+
   for (n = 0; n < num_contributors; n++)
   {
     int i;
@@ -3561,7 +3577,7 @@
         int endi = contribs->n1;
         contribs->n1 = input_last_n1;
         for( i = input_size; i <= endi; i++ )
-          stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start] );
+          stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start], coefficient_width );
       }
 
       // now check left hand edge
@@ -3573,7 +3589,7 @@
 
         // reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist)
         for( i = -1 ; i > contribs->n0 ; i-- )
-          stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c-- );
+          stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c--, coefficient_width );
         save_n0 = contribs->n0;
         save_n0_coeff = c[0]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)!
 
@@ -3583,7 +3599,7 @@
           coeffs[i] = coeffs[i-save_n0];
 
         // now that we have shrunk down the contribs, we insert the first one safely
-        stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff );
+        stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff, coefficient_width );
       }
     }
 
@@ -3592,6 +3608,7 @@
       int diff = contribs->n1 - contribs->n0 + 1;
       while ( diff && ( coeffs[ diff-1 ] == 0.0f ) )
         --diff;
+
       contribs->n1 = contribs->n0 + diff - 1;
 
       if ( contribs->n0 <= contribs->n1 )
@@ -3964,7 +3981,7 @@
               }
               else
               {
-                stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc );
+                stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc, scatter_coefficient_width );
               }
               STBIR_ASSERT( ( scatter_contributors->n1 - scatter_contributors->n0 + 1 ) <= scatter_coefficient_width );
             }
@@ -4810,12 +4827,13 @@
     stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 );
 
 #define stbir__1_coeff_remnant( ofs )                \
-    { stbir__simdf t;                                \
+    { stbir__simdf t,d;                              \
     stbir__simdf_load1z( t, hc + (ofs) );            \
+    stbir__simdf_load2( d, decode + (ofs) * 2 );     \
     stbir__simdf_0123to0011( t, t );                 \
-    stbir__simdf_mult_mem( t, t, decode+(ofs)*2 );   \
+    stbir__simdf_mult( t, t, d );                    \
     stbir__simdf8_add4( tot0, tot0, t ); }
-
+ 
 #define stbir__2_coeff_remnant( ofs )                \
     { stbir__simdf t;                                \
     stbir__simdf_load2( t, hc + (ofs) );             \
@@ -6714,7 +6732,7 @@
     STBIR__FREE_AND_CLEAR( info->horizontal.coefficients );
     STBIR__FREE_AND_CLEAR( info->horizontal.contributors );
     STBIR__FREE_AND_CLEAR( info->alloced_mem );
-    STBIR__FREE_AND_CLEAR( info );
+    STBIR_FREE( info, info->user_data );
   #endif
   }
 
@@ -7112,6 +7130,11 @@
 
 #ifdef STBIR__SEPARATE_ALLOCATIONS
       temp_mem_amt = decode_buffer_size;
+
+      #ifdef STBIR_SIMD8
+      if ( effective_channels == 3 )
+        --temp_mem_amt; // avx in 3 channel mode needs one float at the start of the buffer
+      #endif
 #else
       temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits;
 #endif
@@ -7217,6 +7240,12 @@
         int t, ofs, start;
 
         ofs = decode_buffer_size / 4;
+
+        #if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8)
+        if ( effective_channels == 3 ) 
+          --ofs; // avx in 3 channel mode needs one float at the start of the buffer, so we snap back for clearing
+        #endif
+
         start = ofs - 4;
         if ( start < 0 ) start = 0;
 
diff --git a/raylib/src/external/stb_truetype.h b/raylib/src/external/stb_truetype.h
--- a/raylib/src/external/stb_truetype.h
+++ b/raylib/src/external/stb_truetype.h
@@ -54,7 +54,7 @@
 //       Hou Qiming                 Derek Vinyard
 //       Rob Loach                  Cort Stratton
 //       Kenney Phillis Jr.         Brian Costabile
-//       Ken Voskuil (kaesve)
+//       Ken Voskuil (kaesve)       Yakov Galka
 //
 // VERSION HISTORY
 //
@@ -1863,11 +1863,11 @@
                stbtt_vertex* v = &comp_verts[i];
                stbtt_vertex_type x,y;
                x=v->x; y=v->y;
-               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+               v->x = (stbtt_vertex_type)(mtx[0]*x + mtx[2]*y + mtx[4]*m);
+               v->y = (stbtt_vertex_type)(mtx[1]*x + mtx[3]*y + mtx[5]*n);
                x=v->cx; y=v->cy;
-               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
+               v->cx = (stbtt_vertex_type)(mtx[0]*x + mtx[2]*y + mtx[4]*m);
+               v->cy = (stbtt_vertex_type)(mtx[1]*x + mtx[3]*y + mtx[5]*n);
             }
             // Append vertices.
             tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
@@ -4604,6 +4604,8 @@
    scale_y = -scale_y;
 
    {
+      // distance from singular values (in the same units as the pixel grid)
+      const float eps = 1./1024, eps2 = eps*eps;
       int x,y,i,j;
       float *precompute;
       stbtt_vertex *verts;
@@ -4616,15 +4618,15 @@
             float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
             float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
             float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
-            precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
+            precompute[i] = (dist < eps) ? 0.0f : 1.0f / dist;
          } else if (verts[i].type == STBTT_vcurve) {
             float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
             float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
             float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
             float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
             float len2 = bx*bx + by*by;
-            if (len2 != 0.0f)
-               precompute[i] = 1.0f / (bx*bx + by*by);
+            if (len2 >= eps2)
+               precompute[i] = 1.0f / len2;
             else
                precompute[i] = 0.0f;
          } else
@@ -4689,8 +4691,8 @@
                         float a = 3*(ax*bx + ay*by);
                         float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
                         float c = mx*ax+my*ay;
-                        if (a == 0.0) { // if a is 0, it's linear
-                           if (b != 0.0) {
+                        if (STBTT_fabs(a) < eps2) { // if a is 0, it's linear
+                           if (STBTT_fabs(b) >= eps2) {
                               res[num++] = -c/b;
                            }
                         } else {
diff --git a/raylib/src/external/tinyobj_loader_c.h b/raylib/src/external/tinyobj_loader_c.h
--- a/raylib/src/external/tinyobj_loader_c.h
+++ b/raylib/src/external/tinyobj_loader_c.h
@@ -454,7 +454,7 @@
 }
 
 static unsigned int my_strnlen(const char *s, unsigned int n) {
-    const char *p = memchr(s, 0, n);
+    const char *p = (const char *)memchr(s, 0, n);
     return p ? (unsigned int)(p - s) : n;
 }
 
diff --git a/raylib/src/external/vox_loader.h b/raylib/src/external/vox_loader.h
--- a/raylib/src/external/vox_loader.h
+++ b/raylib/src/external/vox_loader.h
@@ -151,8 +151,8 @@
 /////////////////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////////////////
 //									Implementation
-///////////////////////////////////////////////////////////////////////////////////////////// 
 /////////////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////////////
 
 #ifdef VOX_LOADER_IMPLEMENTATION
 
@@ -165,7 +165,7 @@
 
 static void initArrayUShort(ArrayUShort* a, int initialSize)
 {
-	a->array = VOX_MALLOC(initialSize * sizeof(unsigned short));
+	a->array = (unsigned short *)VOX_MALLOC(initialSize * sizeof(unsigned short));
 	a->used = 0;
 	a->size = initialSize;
 }
@@ -175,7 +175,7 @@
 	if (a->used == a->size)
 	{
 		a->size *= 2;
-		a->array = VOX_REALLOC(a->array, a->size * sizeof(unsigned short));
+		a->array = (unsigned short *)VOX_REALLOC(a->array, a->size * sizeof(unsigned short));
 	}
 	a->array[a->used++] = element;
 }
@@ -194,7 +194,7 @@
 
 static void initArrayVector3(ArrayVector3* a, int initialSize)
 {
-	a->array = VOX_MALLOC(initialSize * sizeof(VoxVector3));
+	a->array = (VoxVector3 *)VOX_MALLOC(initialSize * sizeof(VoxVector3));
 	a->used = 0;
 	a->size = initialSize;
 }
@@ -204,7 +204,7 @@
 	if (a->used == a->size)
 	{
 		a->size *= 2;
-		a->array = VOX_REALLOC(a->array, a->size * sizeof(VoxVector3));
+		a->array = (VoxVector3 *)VOX_REALLOC(a->array, a->size * sizeof(VoxVector3));
 	}
 	a->array[a->used++] = element;
 }
@@ -222,7 +222,7 @@
 
 static void initArrayColor(ArrayColor* a, int initialSize)
 {
-	a->array = VOX_MALLOC(initialSize * sizeof(VoxColor));
+	a->array = (VoxColor *)VOX_MALLOC(initialSize * sizeof(VoxColor));
 	a->used = 0;
 	a->size = initialSize;
 }
@@ -232,7 +232,7 @@
 	if (a->used == a->size)
 	{
 		a->size *= 2;
-		a->array = VOX_REALLOC(a->array, a->size * sizeof(VoxColor));
+		a->array = (VoxColor *)VOX_REALLOC(a->array, a->size * sizeof(VoxColor));
 	}
 	a->array[a->used++] = element;
 }
@@ -327,7 +327,7 @@
 
 	// Alloc chunks array
 	int size = sizeof(CubeChunk3D) * chx * chy * chz;
-	pvoxarray->m_arrayChunks = VOX_MALLOC(size);
+	pvoxarray->m_arrayChunks = (CubeChunk3D *)VOX_MALLOC(size);
 	pvoxarray->arrayChunksSize = size;
 
 	// Init chunks array
@@ -366,7 +366,7 @@
 	if (chunk->m_array == 0)
 	{
 		int size = CHUNKSIZE * CHUNKSIZE * CHUNKSIZE;
-		chunk->m_array = VOX_MALLOC(size);
+		chunk->m_array = (unsigned char *)VOX_MALLOC(size);
 		chunk->arraySize = size;
 		memset(chunk->m_array, 0, size);
 
diff --git a/raylib/src/external/win32_clipboard.h b/raylib/src/external/win32_clipboard.h
new file mode 100644
--- /dev/null
+++ b/raylib/src/external/win32_clipboard.h
@@ -0,0 +1,365 @@
+#if !defined(_WIN32)
+#   error "This module is only made for Windows OS"
+#endif
+
+#ifndef WIN32_CLIPBOARD_
+#define WIN32_CLIPBOARD_
+unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize);
+#endif // WIN32_CLIPBOARD_
+
+#ifdef WIN32_CLIPBOARD_IMPLEMENTATION
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+
+// NOTE: These search for architecture is taken from "Windows.h", and it's necessary if we really don't wanna import windows.h
+// and still make it compile on msvc, because import indirectly importing "winnt.h" (e.g. <minwindef.h>) can cause problems is these are not defined.
+#if !defined(_X86_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IX86)
+#define _X86_
+#if !defined(_CHPE_X86_ARM64_) && defined(_M_HYBRID)
+#define _CHPE_X86_ARM64_
+#endif
+#endif
+
+#if !defined(_AMD64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && (defined(_M_AMD64) || defined(_M_ARM64EC))
+#define _AMD64_
+#endif
+
+#if !defined(_ARM_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM)
+#define _ARM_
+#endif
+
+#if !defined(_ARM64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64EC_) && defined(_M_ARM64)
+#define _ARM64_
+#endif
+
+#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_ARM64EC)
+#define _ARM64EC_
+#endif
+
+#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_M68K)
+#define _68K_
+#endif
+
+#if !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_IA64_) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_MPPC)
+#define _MPPC_
+#endif
+
+#if !defined(_IA64_) && !defined(_68K_) && !defined(_MPPC_) && !defined(_X86_) && !defined(_M_IX86) && !defined(_AMD64_) && !defined(_ARM_) && !defined(_ARM64_) && !defined(_ARM64EC_) && defined(_M_IA64)
+#define _IA64_
+#endif
+
+
+#define WIN32_LEAN_AND_MEAN
+// #include <sdkddkver.h>
+// #include <windows.h>
+// #include <winuser.h>
+#include <minwindef.h>
+// #include <minwinbase.h>
+
+#ifndef WINAPI
+#if defined(_ARM_)
+#define WINAPI
+#else
+#define WINAPI __stdcall
+#endif
+#endif
+
+#ifndef WINAPI
+#if defined(_ARM_)
+#define WINAPI
+#else
+#define WINAPI __stdcall
+#endif
+#endif
+
+#ifndef WINBASEAPI
+#ifndef _KERNEL32_
+#define WINBASEAPI DECLSPEC_IMPORT
+#else
+#define WINBASEAPI
+#endif
+#endif
+
+#ifndef WINUSERAPI
+#ifndef _USER32_
+#define WINUSERAPI __declspec (dllimport)
+#else
+#define WINUSERAPI
+#endif
+#endif
+
+typedef int WINBOOL;
+
+
+#if !defined(_WINUSER_) || !defined(WINUSER_ALREADY_INCLUDED)
+WINUSERAPI WINBOOL WINAPI OpenClipboard(HWND hWndNewOwner);
+WINUSERAPI WINBOOL WINAPI CloseClipboard(VOID);
+WINUSERAPI DWORD   WINAPI GetClipboardSequenceNumber(VOID);
+WINUSERAPI HWND    WINAPI GetClipboardOwner(VOID);
+WINUSERAPI HWND    WINAPI SetClipboardViewer(HWND hWndNewViewer);
+WINUSERAPI HWND    WINAPI GetClipboardViewer(VOID);
+WINUSERAPI WINBOOL WINAPI ChangeClipboardChain(HWND hWndRemove, HWND hWndNewNext);
+WINUSERAPI HANDLE  WINAPI SetClipboardData(UINT uFormat, HANDLE hMem);
+WINUSERAPI HANDLE  WINAPI GetClipboardData(UINT uFormat);
+WINUSERAPI UINT    WINAPI RegisterClipboardFormatA(LPCSTR  lpszFormat);
+WINUSERAPI UINT    WINAPI RegisterClipboardFormatW(LPCWSTR lpszFormat);
+WINUSERAPI int     WINAPI CountClipboardFormats(VOID);
+WINUSERAPI UINT    WINAPI EnumClipboardFormats(UINT format);
+WINUSERAPI int     WINAPI GetClipboardFormatNameA(UINT format, LPSTR  lpszFormatName, int cchMaxCount);
+WINUSERAPI int     WINAPI GetClipboardFormatNameW(UINT format, LPWSTR lpszFormatName, int cchMaxCount);
+WINUSERAPI WINBOOL WINAPI EmptyClipboard(VOID);
+WINUSERAPI WINBOOL WINAPI IsClipboardFormatAvailable(UINT format);
+WINUSERAPI int     WINAPI GetPriorityClipboardFormat(UINT *paFormatPriorityList, int cFormats);
+WINUSERAPI HWND    WINAPI GetOpenClipboardWindow(VOID);
+#endif
+
+#ifndef HGLOBAL
+#define HGLOBAL void*
+#endif
+
+#if !defined(_WINBASE_) || !defined(WINBASE_ALREADY_INCLUDED)
+WINBASEAPI SIZE_T  WINAPI GlobalSize (HGLOBAL hMem);
+WINBASEAPI LPVOID  WINAPI GlobalLock (HGLOBAL hMem);
+WINBASEAPI WINBOOL WINAPI GlobalUnlock (HGLOBAL hMem);
+#endif
+
+
+#if !defined(_WINGDI_) || !defined(WINGDI_ALREADY_INCLUDED)
+#ifndef BITMAPINFOHEADER_ALREADY_DEFINED
+#define BITMAPINFOHEADER_ALREADY_DEFINED
+// Does this header need to be packed ? by the windowps header it doesnt seem to be
+#pragma pack(push, 1)
+typedef struct tagBITMAPINFOHEADER {
+    DWORD biSize;
+    LONG biWidth;
+    LONG biHeight;
+    WORD biPlanes;
+    WORD biBitCount;
+    DWORD biCompression;
+    DWORD biSizeImage;
+    LONG biXPelsPerMeter;
+    LONG biYPelsPerMeter;
+    DWORD biClrUsed;
+    DWORD biClrImportant;
+} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;
+#pragma pack(pop)
+#endif
+
+#ifndef BITMAPFILEHEADER_ALREADY_DEFINED
+#define BITMAPFILEHEADER_ALREADY_DEFINED
+#pragma pack(push, 1)
+typedef struct tagBITMAPFILEHEADER {
+    WORD bfType;
+    DWORD bfSize;
+    WORD bfReserved1;
+    WORD bfReserved2;
+    DWORD bfOffBits;
+} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
+#pragma pack(pop)
+#endif
+
+#ifndef RGBQUAD_ALREADY_DEFINED
+#define RGBQUAD_ALREADY_DEFINED
+typedef struct tagRGBQUAD {
+  BYTE rgbBlue;
+  BYTE rgbGreen;
+  BYTE rgbRed;
+  BYTE rgbReserved;
+} RGBQUAD, *LPRGBQUAD;
+#endif
+
+
+// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wmf/4e588f70-bd92-4a6f-b77f-35d0feaf7a57
+#define BI_RGB       0x0000
+#define BI_RLE8      0x0001
+#define BI_RLE4      0x0002
+#define BI_BITFIELDS 0x0003
+#define BI_JPEG      0x0004
+#define BI_PNG       0x0005
+#define BI_CMYK      0x000B
+#define BI_CMYKRLE8  0x000C
+#define BI_CMYKRLE4  0x000D
+
+#endif
+
+// https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats
+#define CF_DIB 8
+
+// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setsystemcursor
+// #define OCR_NORMAL      32512 // Normal     select
+// #define OCR_IBEAM       32513 // Text       select
+// #define OCR_WAIT        32514 // Busy
+// #define OCR_CROSS       32515 // Precision  select
+// #define OCR_UP          32516 // Alternate  select
+// #define OCR_SIZENWSE    32642 // Diagonal   resize 1
+// #define OCR_SIZENESW    32643 // Diagonal   resize 2
+// #define OCR_SIZEWE      32644 // Horizontal resize
+// #define OCR_SIZENS      32645 // Vertical   resize
+// #define OCR_SIZEALL     32646 // Move
+// #define OCR_NO          32648 // Unavailable
+// #define OCR_HAND        32649 // Link       select
+// #define OCR_APPSTARTING 32650 //
+
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+
+
+static BOOL           OpenClipboardRetrying(HWND handle); // Open clipboard with a number of retries
+static int            GetPixelDataOffset(BITMAPINFOHEADER bih);
+
+unsigned char* Win32GetClipboardImageData(int* width, int* height, unsigned long long int *dataSize)
+{
+    HWND win = NULL; // Get from somewhere but is doesnt seem to matter
+    const char* msgString = "";
+    int severity = LOG_INFO;
+    BYTE* bmpData = NULL;
+    if (!OpenClipboardRetrying(win)) {
+        severity = LOG_ERROR;
+        msgString = "Couldn't open clipboard";
+        goto end;
+    }
+
+    HGLOBAL clipHandle = (HGLOBAL)GetClipboardData(CF_DIB);
+    if (!clipHandle) {
+        severity = LOG_ERROR;
+        msgString = "Clipboard data is not an Image";
+        goto close;
+    }
+
+    BITMAPINFOHEADER *bmpInfoHeader = (BITMAPINFOHEADER *)GlobalLock(clipHandle);
+    if (!bmpInfoHeader) {
+        // Mapping from HGLOBAL to our local *address space* failed
+        severity = LOG_ERROR;
+        msgString = "Clipboard data failed to be locked";
+        goto unlock;
+    }
+
+    *width = bmpInfoHeader->biWidth;
+    *height = bmpInfoHeader->biHeight;
+
+    SIZE_T clipDataSize = GlobalSize(clipHandle);
+    if (clipDataSize < sizeof(BITMAPINFOHEADER)) {
+        // Format CF_DIB needs space for BITMAPINFOHEADER struct.
+        msgString = "Clipboard has Malformed data";
+        severity = LOG_ERROR;
+        goto unlock;
+    }
+
+    // Denotes where the pixel data starts from the bmpInfoHeader pointer
+    int pixelOffset = GetPixelDataOffset(*bmpInfoHeader);
+
+    //--------------------------------------------------------------------------------//
+    //
+    // The rest of the section is about create the bytes for a correct BMP file
+    // Then we copy the data and to a pointer
+    //
+    //--------------------------------------------------------------------------------//
+
+    BITMAPFILEHEADER bmpFileHeader = {0};
+    SIZE_T bmpFileSize = sizeof(bmpFileHeader) + clipDataSize;
+    *dataSize = bmpFileSize;
+
+    bmpFileHeader.bfType = 0x4D42; //https://stackoverflow.com/questions/601430/multibyte-character-constants-and-bitmap-file-header-type-constants#601536
+
+    bmpFileHeader.bfSize = (DWORD)bmpFileSize; // Up to 4GB works fine
+    bmpFileHeader.bfOffBits = sizeof(bmpFileHeader) + pixelOffset;
+
+    //
+    // Each process has a default heap provided by the system
+    // Memory objects allocated by GlobalAlloc and LocalAlloc are in private,
+    // committed pages with read/write access that cannot be accessed by other processes.
+    //
+    // This may be wrong since we might be allocating in a DLL and freeing from another module, the main application
+    // that may cause heap corruption. We could create a FreeImage function
+    //
+    bmpData = (BYTE *)malloc(sizeof(bmpFileHeader) + clipDataSize);
+    // First we add the header for a bmp file
+    memcpy(bmpData, &bmpFileHeader, sizeof(bmpFileHeader));
+    // Then we add the header for the bmp itself + the pixel data
+    memcpy(bmpData + sizeof(bmpFileHeader), bmpInfoHeader, clipDataSize);
+    msgString = "Clipboad image acquired successfully";
+
+
+unlock:
+    GlobalUnlock(clipHandle);
+close:
+    CloseClipboard();
+end:
+
+    TRACELOG(severity, msgString);
+    return bmpData;
+}
+
+static BOOL OpenClipboardRetrying(HWND hWnd)
+{
+    static const int maxTries = 20;
+    static const int sleepTimeMS = 60;
+    for (int _ = 0; _ < maxTries; ++_)
+    {
+        // Might be being hold by another process
+        // Or yourself forgot to CloseClipboard
+        if (OpenClipboard(hWnd)) {
+            return true;
+        }
+        Sleep(sleepTimeMS);
+    }
+    return false;
+}
+
+// Based off of researching microsoft docs and reponses from this question https://stackoverflow.com/questions/30552255/how-to-read-a-bitmap-from-the-windows-clipboard#30552856
+// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
+// Get the byte offset where does the pixels data start (from a packed DIB)
+static int GetPixelDataOffset(BITMAPINFOHEADER bih)
+{
+    int offset = 0;
+    const unsigned int rgbaSize = sizeof(RGBQUAD);
+
+    // biSize Specifies the number of bytes required by the structure
+    // We expect to always be 40 because it should be packed
+    if (40 == bih.biSize && 40 == sizeof(BITMAPINFOHEADER))
+    {
+        //
+        // biBitCount Specifies the number of bits per pixel.
+        // Might exist some bit masks *after* the header and *before* the pixel offset
+        // we're looking, but only if we have more than
+        // 8 bits per pixel, so we need to ajust for that
+        //
+        if (bih.biBitCount > 8)
+        {
+            // if bih.biCompression is RBG we should NOT offset more
+
+            if (bih.biCompression == BI_BITFIELDS)
+            {
+                offset += 3 * rgbaSize;
+            } else if (bih.biCompression == 6 /* BI_ALPHABITFIELDS */)
+            {
+                // Not widely supported, but valid.
+                offset += 4 * rgbaSize;
+            }
+        }
+    }
+
+    //
+    // biClrUsed Specifies the number of color indices in the color table that are actually used by the bitmap.
+    // If this value is zero, the bitmap uses the maximum number of colors
+    // corresponding to the value of the biBitCount member for the compression mode specified by biCompression.
+    // If biClrUsed is nonzero and the biBitCount member is less than 16
+    // the biClrUsed member specifies the actual number of colors
+    //
+    if (bih.biClrUsed > 0) {
+        offset += bih.biClrUsed * rgbaSize;
+    } else {
+        if (bih.biBitCount < 16)
+        {
+            offset = offset + (rgbaSize << bih.biBitCount);
+        }
+    }
+
+    return bih.biSize + offset;
+}
+#endif // WIN32_CLIPBOARD_IMPLEMENTATION
+// EOF
diff --git a/raylib/src/platforms/rcore_android.c b/raylib/src/platforms/rcore_android.c
--- a/raylib/src/platforms/rcore_android.c
+++ b/raylib/src/platforms/rcore_android.c
@@ -13,9 +13,6 @@
 *       - Improvement 01
 *       - Improvement 02
 *
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
-*
 *   CONFIGURATION:
 *       #define RCORE_PLATFORM_CUSTOM_FLAG
 *           Custom flag for rcore on target platform -not used-
@@ -27,7 +24,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -48,9 +45,13 @@
 
 #include <android_native_app_glue.h>    // Required for: android_app struct and activity management
 #include <android/window.h>             // Required for: AWINDOW_FLAG_FULLSCREEN definition and others
+#include <android/log.h>                // Required for: Android log system: __android_log_vprint()
+#include <android/asset_manager.h>      // Required for: AAssetManager
 //#include <android/sensor.h>           // Required for: Android sensors functions (accelerometer, gyroscope, light...)
-#include <jni.h>                        // Required for: JNIEnv and JavaVM [Used in OpenURL()]
 
+#include <errno.h>                      // Required for: error types
+#include <jni.h>                        // Required for: JNIEnv and JavaVM [Used in OpenURL() and GetCurrentMonitor()]
+
 #include <EGL/egl.h>                    // Native platform windowing system interface
 
 //----------------------------------------------------------------------------------
@@ -70,11 +71,20 @@
     EGLConfig config;                   // Graphic config
 } PlatformData;
 
+typedef struct {
+    // Store data for both Hover and Touch events
+    // Used to ignore Hover events which are interpreted as Touch events
+    int32_t pointCount;                             // Number of touch points active
+    int32_t pointId[MAX_TOUCH_POINTS];              // Point identifiers
+    Vector2 position[MAX_TOUCH_POINTS];             // Touch position on screen
+
+    int32_t hoverPoints[MAX_TOUCH_POINTS];          // Hover Points
+} TouchRaw;
+
 //----------------------------------------------------------------------------------
 // Global Variables Definition
 //----------------------------------------------------------------------------------
 extern CoreData CORE;                   // Global CORE state context
-extern bool isGpuReady;                 // Flag to note GPU has been initialized successfully
 static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
@@ -246,6 +256,8 @@
     KEY_KP_EQUAL        // AKEYCODE_NUMPAD_EQUALS
 };
 
+static TouchRaw touchRaw = { 0 };
+
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
@@ -256,6 +268,19 @@
 static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event);   // Process Android inputs
 static GamepadButton AndroidTranslateGamepadButton(int button);                     // Map Android gamepad button to raylib gamepad button
 
+static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
+
+static int android_read(void *cookie, char *buf, int size);
+static int android_write(void *cookie, const char *buf, int size);
+static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
+static int android_close(void *cookie);
+
+FILE *android_fopen(const char *fileName, const char *mode); // Replacement for fopen() -> Read-only!
+FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
+              fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
+
+#define fopen(name, mode) android_fopen(name, mode)
+
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
@@ -337,7 +362,7 @@
     TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
 }
 
-// Set window state: not minimized/maximized
+// Restore window from being minimized/maximized
 void RestoreWindow(void)
 {
     TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
@@ -346,13 +371,17 @@
 // Set window configuration state using flags
 void SetWindowState(unsigned int flags)
 {
-    TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
+
+    // State change: FLAG_WINDOW_ALWAYS_RUN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
 }
 
 // Clear window configuration state flags
 void ClearWindowState(unsigned int flags)
 {
-    TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
+    // State change: FLAG_WINDOW_ALWAYS_RUN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
 }
 
 // Set icon for window
@@ -431,11 +460,38 @@
     return 1;
 }
 
-// Get number of monitors
+// Get current monitor where window is placed
 int GetCurrentMonitor(void)
 {
-    TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
-    return 0;
+    int displayId = -1;
+    JNIEnv *env = NULL;
+    JavaVM *vm = platform.app->activity->vm;
+    (*vm)->AttachCurrentThread(vm, &env, NULL);
+
+    jobject activity = platform.app->activity->clazz;
+    jclass activityClass = (*env)->GetObjectClass(env, activity);
+
+    jmethodID getDisplayMethod = (*env)->GetMethodID(env, activityClass, "getDisplay", "()Landroid/view/Display;");
+
+    jobject display = (*env)->CallObjectMethod(env, activity, getDisplayMethod);
+
+    if (display == NULL)
+    {
+        TRACELOG(LOG_ERROR, "GetCurrentMonitor() couldn't get the display object");
+    }
+    else
+    {
+        jclass displayClass = (*env)->FindClass(env, "android/view/Display");
+        jmethodID getDisplayIdMethod = (*env)->GetMethodID(env, displayClass, "getDisplayId", "()I");
+        displayId = (int)(*env)->CallIntMethod(env, display, getDisplayIdMethod);
+        (*env)->DeleteLocalRef(env, displayClass);
+    }
+
+    (*env)->DeleteLocalRef(env, activityClass);
+    (*env)->DeleteLocalRef(env, display);
+
+    (*vm)->DetachCurrentThread(vm);
+    return displayId;
 }
 
 // Get selected monitor position
@@ -460,17 +516,21 @@
 }
 
 // Get selected monitor physical width in millimetres
+// NOTE: It seems to return a slightly underestimated value on some devices
 int GetMonitorPhysicalWidth(int monitor)
 {
-    TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
-    return 0;
+    int widthPixels = ANativeWindow_getWidth(platform.app->window);
+    float dpi = AConfiguration_getDensity(platform.app->config);
+    return (widthPixels/dpi)*25.4f;
 }
 
 // Get selected monitor physical height in millimetres
+// NOTE: It seems to return a slightly underestimated value on some devices
 int GetMonitorPhysicalHeight(int monitor)
 {
-    TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
-    return 0;
+    int heightPixels = ANativeWindow_getHeight(platform.app->window);
+    float dpi = AConfiguration_getDensity(platform.app->config);
+    return (heightPixels/dpi)*25.4f;
 }
 
 // Get selected monitor refresh rate
@@ -497,8 +557,9 @@
 // Get window scale DPI factor for current monitor
 Vector2 GetWindowScaleDPI(void)
 {
-    TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
-    return (Vector2){ 1.0f, 1.0f };
+    int density = AConfiguration_getDensity(platform.app->config);
+    float scale = (float)density/160;
+    return (Vector2){ scale, scale };
 }
 
 // Set clipboard text content
@@ -515,6 +576,16 @@
     return NULL;
 }
 
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
 // Show mouse cursor
 void ShowCursor(void)
 {
@@ -533,7 +604,7 @@
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
-    CORE.Input.Mouse.cursorHidden = false;
+    CORE.Input.Mouse.cursorLocked = false;
 }
 
 // Disables cursor (lock cursor)
@@ -542,13 +613,13 @@
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
-    CORE.Input.Mouse.cursorHidden = true;
+    CORE.Input.Mouse.cursorLocked = true;
 }
 
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
-    eglSwapBuffers(platform.device, platform.surface);
+    if (platform.surface != EGL_NO_SURFACE) eglSwapBuffers(platform.device, platform.surface);
 }
 
 //----------------------------------------------------------------------------------
@@ -569,10 +640,9 @@
 }
 
 // Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
 void OpenURL(const char *url)
 {
     // Security check to (partially) avoid malicious code
@@ -617,7 +687,7 @@
 // Set gamepad vibration
 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
 {
-    TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
 }
 
 // Set mouse position XY
@@ -633,7 +703,7 @@
     TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
 }
 
-// Get physical key name.
+// Get physical key name
 const char *GetKeyName(int key)
 {
     TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@@ -688,15 +758,15 @@
     int pollEvents = 0;
 
     // Poll Events (registered events) until we reach TIMEOUT which indicates there are no events left to poll
-    // NOTE: Activity is paused if not enabled (platform.appEnabled)
-    while ((pollResult = ALooper_pollOnce(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
+    // NOTE: Activity is paused if not enabled (platform.appEnabled) and always run flag is not set (FLAG_WINDOW_ALWAYS_RUN)
+    while ((pollResult = ALooper_pollOnce((platform.appEnabled || FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN))? 0 : -1, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
     {
         // Process this event
         if (platform.source != NULL) platform.source->process(platform.app, platform.source);
 
-        // NOTE: Allow closing the window in case a configuration change happened.
+        // NOTE: Allow closing the window in case a configuration change happened
         // The android_main function should be allowed to return to its caller in order for the
-        // Android OS to relaunch the activity.
+        // Android OS to relaunch the activity
         if (platform.app->destroyRequested != 0)
         {
             CORE.Window.shouldClose = true;
@@ -742,10 +812,10 @@
     //AConfiguration_getScreenLong(platform.app->config);
 
     // Set some default window flags
-    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
-    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
-    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
-    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);       // false
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);    // false
+    FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);      // true
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);    // false
     //----------------------------------------------------------------------------
 
     // Initialize App command system
@@ -761,8 +831,6 @@
 
     // Initialize storage system
     //----------------------------------------------------------------------------
-    InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath);   // Initialize assets manager
-
     CORE.Storage.basePath = platform.app->activity->internalDataPath;   // Define base path for storage
     //----------------------------------------------------------------------------
 
@@ -775,17 +843,19 @@
     // Wait for window to be initialized (display and context)
     while (!CORE.Window.ready)
     {
-        // Process events until we reach TIMEOUT, which indicates no more events queued.
-        while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, (void**)&platform.source)) > ALOOPER_POLL_TIMEOUT)
+        // Process events until we reach TIMEOUT, which indicates no more events queued
+        while ((pollResult = ALooper_pollOnce(0, NULL, &pollEvents, ((void **)&platform.source)) > ALOOPER_POLL_TIMEOUT))
         {
             // Process this event
             if (platform.source != NULL) platform.source->process(platform.app, platform.source);
 
-            // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle.
+            // NOTE: It's highly likely destroyRequested will never be non-zero at the start of the activity lifecycle
             //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true;
         }
     }
 
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) touchRaw.hoverPoints[i] = -1;
+
     return 0;
 }
 
@@ -813,8 +883,9 @@
         platform.device = EGL_NO_DISPLAY;
     }
 
-    // NOTE: Reset global state in case the activity is being relaunched.
-    if (platform.app->destroyRequested != 0) {
+    // NOTE: Reset global state in case the activity is being relaunched
+    if (platform.app->destroyRequested != 0)
+    {
         CORE = (CoreData){0};
         platform = (PlatformData){0};
     }
@@ -826,34 +897,31 @@
 // NOTE: returns false in case graphic device could not be created
 static int InitGraphicsDevice(void)
 {
-    CORE.Window.fullscreen = true;
-    CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+    FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
 
     EGLint samples = 0;
     EGLint sampleBuffer = 0;
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
     {
         samples = 4;
         sampleBuffer = 1;
         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
     }
 
-    const EGLint framebufferAttribs[] =
-    {
-        EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,      // Type of context support
+    const EGLint framebufferAttribs[] = {
+        EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
         EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
         EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
         EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
         //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
-        EGL_DEPTH_SIZE, 16,         // Depth buffer size (Required to use Depth testing!)
+        EGL_DEPTH_SIZE, 24,         // Depth buffer size (Required to use Depth testing!)
         //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
-        EGL_SAMPLE_BUFFERS, sampleBuffer,    // Activate MSAA
+        EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
         EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
         EGL_NONE
     };
 
-    const EGLint contextAttribs[] =
-    {
+    const EGLint contextAttribs[] = {
         EGL_CONTEXT_CLIENT_VERSION, 2,
         EGL_NONE
     };
@@ -862,6 +930,7 @@
 
     // Get an EGL device connection
     platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
     if (platform.device == EGL_NO_DISPLAY)
     {
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
@@ -871,7 +940,7 @@
     // Initialize the EGL device connection
     if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
     {
-        // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
+        // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred
         TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
         return -1;
     }
@@ -938,7 +1007,7 @@
 
     CORE.Window.ready = true;
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
 
     return 0;
 }
@@ -988,7 +1057,6 @@
                     // Initialize OpenGL context (states and resources)
                     // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
                     rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-                    isGpuReady = true;
 
                     // Setup default viewport
                     // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height
@@ -1005,7 +1073,7 @@
                     // Set font white rectangle for shapes drawing, so shapes and text can be batched together
                     // WARNING: rshapes module is required, if not available, default internal white rectangle is used
                     Rectangle rec = GetFontDefault().recs[95];
-                    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+                    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
                     {
                         // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
                         SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
@@ -1027,41 +1095,26 @@
 
                     // Initialize random seed
                     SetRandomSeed((unsigned int)time(NULL));
-
-                    // TODO: GPU assets reload in case of lost focus (lost context)
-                    // NOTE: This problem has been solved just unbinding and rebinding context from display
-                    /*
-                    if (assetsReloadRequired)
-                    {
-                        for (int i = 0; i < assetCount; i++)
-                        {
-                            // TODO: Unload old asset if required
-
-                            // Load texture again to pointed texture
-                            (*textureAsset + i) = LoadTexture(assetPath[i]);
-                        }
-                    }
-                    */
                 }
             }
         } break;
         case APP_CMD_GAINED_FOCUS:
         {
             platform.appEnabled = true;
-            CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
+            FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
             //ResumeMusicStream();
         } break;
         case APP_CMD_PAUSE: break;
         case APP_CMD_LOST_FOCUS:
         {
             platform.appEnabled = false;
-            CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
+            FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
             //PauseMusicStream();
         } break;
         case APP_CMD_TERM_WINDOW:
         {
             // Detach OpenGL context and destroy display surface
-            // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming.
+            // NOTE 1: This case is used when the user exits the app without closing it, context is detached to ensure everything is recoverable upon resuming
             // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...)
             // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :(
             if (platform.device != EGL_NO_DISPLAY)
@@ -1125,16 +1178,16 @@
 static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
 {
     // If additional inputs are required check:
-    // https://developer.android.com/ndk/reference/group/input
-    // https://developer.android.com/training/game-controllers/controller-input
+    // REF: https://developer.android.com/ndk/reference/group/input
+    // REF: https://developer.android.com/training/game-controllers/controller-input
 
     int type = AInputEvent_getType(event);
     int source = AInputEvent_getSource(event);
 
     if (type == AINPUT_EVENT_TYPE_MOTION)
     {
-        if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
-            ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
+        if (FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) ||
+            FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD))
         {
             // For now we'll assume a single gamepad which we "detect" on its input event
             CORE.Input.Gamepad.ready[0] = true;
@@ -1197,8 +1250,11 @@
         //int32_t AKeyEvent_getMetaState(event);
 
         // Handle gamepad button presses and releases
-        if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) ||
-            ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD))
+        // NOTE: Skip gamepad handling if this is a keyboard event, as some devices
+        // report both AINPUT_SOURCE_KEYBOARD and AINPUT_SOURCE_GAMEPAD flags
+        if ((FLAG_IS_SET(source, AINPUT_SOURCE_JOYSTICK) ||
+             FLAG_IS_SET(source, AINPUT_SOURCE_GAMEPAD)) &&
+            !FLAG_IS_SET(source, AINPUT_SOURCE_KEYBOARD))
         {
             // For now we'll assume a single gamepad which we "detect" on its input event
             CORE.Input.Gamepad.ready[0] = true;
@@ -1216,7 +1272,7 @@
             return 1; // Handled gamepad button
         }
 
-        KeyboardKey key = (keycode > 0 && keycode < KEYCODE_MAP_SIZE)? mapKeycode[keycode] : KEY_NULL;
+        KeyboardKey key = ((keycode > 0) && (keycode < KEYCODE_MAP_SIZE))? mapKeycode[keycode] : KEY_NULL;
         if (key != KEY_NULL)
         {
             // Save current key and its state
@@ -1236,7 +1292,7 @@
         {
             // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS
             // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS
-            // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected.
+            // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected
             // NOTE: AndroidManifest.xml must have <activity android:configChanges="orientation|keyboardHidden|screenSize" >
             // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour
             return 0;
@@ -1256,30 +1312,45 @@
     }
 
     // Register touch points count
-    CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event);
+    touchRaw.pointCount = AMotionEvent_getPointerCount(event);
 
-    for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+    for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++)
     {
         // Register touch points id
-        CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i);
+        touchRaw.pointId[i] = AMotionEvent_getPointerId(event, i);
 
         // Register touch points position
-        CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
+        touchRaw.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) };
 
         // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height
         float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x)/(float)CORE.Window.display.width;
         float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y)/(float)CORE.Window.display.height;
-        CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
-        CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
+        touchRaw.position[i].x = touchRaw.position[i].x*widthRatio - (float)CORE.Window.renderOffset.x/2;
+        touchRaw.position[i].y = touchRaw.position[i].y*heightRatio - (float)CORE.Window.renderOffset.y/2;
     }
 
     int32_t action = AMotionEvent_getAction(event);
     unsigned int flags = action & AMOTION_EVENT_ACTION_MASK;
+    int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
 
+    if (flags == AMOTION_EVENT_ACTION_HOVER_ENTER)
+    {
+        // The new pointer is hover
+        // So add it to hoverPoints
+        for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+        {
+            if (touchRaw.hoverPoints[i] == -1)
+            {
+                touchRaw.hoverPoints[i] = touchRaw.pointId[pointerIndex];
+                break;
+            }
+        }
+    }
+
 #if defined(SUPPORT_GESTURES_SYSTEM)
     GestureEvent gestureEvent = { 0 };
 
-    gestureEvent.pointCount = CORE.Input.Touch.pointCount;
+    gestureEvent.pointCount = 0;
 
     // Register touch actions
     if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
@@ -1287,30 +1358,75 @@
     else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
     else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
 
-    for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+    for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++)
     {
-        gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
-        gestureEvent.position[i] = CORE.Input.Touch.position[i];
-        gestureEvent.position[i].x /= (float)GetScreenWidth();
-        gestureEvent.position[i].y /= (float)GetScreenHeight();
+        // If the touchPoint is hover, Ignore it
+        bool hover = false;
+        for (int j = 0; j < MAX_TOUCH_POINTS; j++)
+        {
+            // Check if the touchPoint is in hoverPointers
+            if (touchRaw.hoverPoints[j] == touchRaw.pointId[i])
+            {
+                hover = true;
+                break;
+            }
+        }
+        if (hover) continue;
+
+        gestureEvent.pointId[gestureEvent.pointCount] = touchRaw.pointId[i];
+        gestureEvent.position[gestureEvent.pointCount] = touchRaw.position[i];
+        gestureEvent.position[gestureEvent.pointCount].x /= (float)GetScreenWidth();
+        gestureEvent.position[gestureEvent.pointCount].y /= (float)GetScreenHeight();
+        gestureEvent.pointCount++;
     }
 
     // Gesture data is sent to gestures system for processing
     ProcessGestureEvent(gestureEvent);
 #endif
 
-    int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
+    if (flags == AMOTION_EVENT_ACTION_HOVER_EXIT)
+    {
+        // Hover exited. So, remove it from hoverPoints
+        for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+        {
+            if (touchRaw.hoverPoints[i] == touchRaw.pointId[pointerIndex])
+            {
+                touchRaw.hoverPoints[i] = -1;
+                break;
+            }
+        }
+    }
 
-    if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP)
+    if ((flags == AMOTION_EVENT_ACTION_POINTER_UP) || (flags == AMOTION_EVENT_ACTION_UP))
     {
         // One of the touchpoints is released, remove it from touch point arrays
-        for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++)
+        for (int i = pointerIndex; (i < touchRaw.pointCount - 1) && (i < MAX_TOUCH_POINTS - 1); i++)
         {
-            CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1];
-            CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1];
+            touchRaw.pointId[i] = touchRaw.pointId[i+1];
+            touchRaw.position[i] = touchRaw.position[i+1];
         }
+        touchRaw.pointCount--;
+    }
 
-        CORE.Input.Touch.pointCount--;
+    CORE.Input.Touch.pointCount = 0;
+    for (int i = 0; (i < touchRaw.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+    {
+        // If the touchPoint is hover, Ignore it
+        bool hover = false;
+        for (int j = 0; j < MAX_TOUCH_POINTS; j++)
+        {
+            // Check if the touchPoint is in hoverPointers
+            if (touchRaw.hoverPoints[j] == touchRaw.pointId[i])
+            {
+                hover = true;
+                break;
+            }
+        }
+        if (hover) continue;
+
+        CORE.Input.Touch.pointId[CORE.Input.Touch.pointCount] = touchRaw.pointId[i];
+        CORE.Input.Touch.position[CORE.Input.Touch.pointCount] = touchRaw.position[i];
+        CORE.Input.Touch.pointCount++;
     }
 
     // When all touchpoints are tapped and released really quickly, this event is generated
@@ -1319,20 +1435,154 @@
     if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1;
     else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0;
 
-    // Stores the previous position of touch[0] only while it's active to calculate the delta.
-    if (flags == AMOTION_EVENT_ACTION_MOVE)
+    // Stores the previous position of touch[0] only while it's active to calculate the delta
+    if (flags == AMOTION_EVENT_ACTION_MOVE) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+    else CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
+
+    // Map touch[0] as mouse input for convenience
+    CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
+    CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
+
+    return 0;
+}
+
+// Compute framebuffer size relative to screen size and display size
+// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
+static void SetupFramebuffer(int width, int height)
+{
+    // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
+    if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
     {
-        CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+        TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+        // Downscaling to fit display with border-bars
+        float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
+        float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
+
+        if (widthRatio <= heightRatio)
+        {
+            CORE.Window.render.width = CORE.Window.display.width;
+            CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
+            CORE.Window.renderOffset.x = 0;
+            CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
+        }
+        else
+        {
+            CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
+            CORE.Window.render.height = CORE.Window.display.height;
+            CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
+            CORE.Window.renderOffset.y = 0;
+        }
+
+        // Screen scaling required
+        float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
+        CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
+
+        // NOTE: We render to full display resolution!
+        // We just need to calculate above parameters for downscale matrix and offsets
+        CORE.Window.render.width = CORE.Window.display.width;
+        CORE.Window.render.height = CORE.Window.display.height;
+
+        TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
     }
+    else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
+    {
+        // Required screen size is smaller than display size
+        TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+        if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
+        {
+            CORE.Window.screen.width = CORE.Window.display.width;
+            CORE.Window.screen.height = CORE.Window.display.height;
+        }
+
+        // Upscaling to fit display with border-bars
+        float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
+        float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
+
+        if (displayRatio <= screenRatio)
+        {
+            CORE.Window.render.width = CORE.Window.screen.width;
+            CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
+            CORE.Window.renderOffset.x = 0;
+            CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
+        }
+        else
+        {
+            CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
+            CORE.Window.render.height = CORE.Window.screen.height;
+            CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
+            CORE.Window.renderOffset.y = 0;
+        }
+    }
     else
     {
-        CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0];
+        CORE.Window.render.width = CORE.Window.screen.width;
+        CORE.Window.render.height = CORE.Window.screen.height;
+        CORE.Window.renderOffset.x = 0;
+        CORE.Window.renderOffset.y = 0;
     }
+}
 
-    // Map touch[0] as mouse input for convenience
-    CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0];
-    CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
+// Replacement for fopen()
+// REF: https://developer.android.com/ndk/reference/group/asset
+FILE *android_fopen(const char *fileName, const char *mode)
+{
+    FILE *file = NULL;
+    
+    if (mode[0] == 'w')
+    {
+        // NOTE: fopen() is mapped to android_fopen() that only grants read access to
+        // assets directory through AAssetManager but we want to also be able to
+        // write data when required using the standard stdio FILE access functions
+        // REF: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only
+        #undef fopen
+        file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
+        #define fopen(name, mode) android_fopen(name, mode)
+    }
+    else
+    {
+        // NOTE: AAsset provides access to read-only asset
+        AAsset *asset = AAssetManager_open(platform.app->activity->assetManager, fileName, AASSET_MODE_UNKNOWN);
 
+        if (asset != NULL)
+        {
+            // Get pointer to file in the assets
+            file = funopen(asset, android_read, android_write, android_seek, android_close);
+        }
+        else
+        {
+            #undef fopen
+            // Just do a regular open if file is not found in the assets
+            file = fopen(TextFormat("%s/%s", platform.app->activity->internalDataPath, fileName), mode);
+            if (file == NULL) file = fopen(fileName, mode);
+            #define fopen(name, mode) android_fopen(name, mode)
+        }
+    }
+    
+    return file;
+}
+
+static int android_read(void *cookie, char *data, int dataSize)
+{
+    return AAsset_read((AAsset *)cookie, data, dataSize);
+}
+
+static int android_write(void *cookie, const char *data, int dataSize)
+{
+    TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");
+
+    return EACCES;
+}
+
+static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
+{
+    return AAsset_seek((AAsset *)cookie, offset, whence);
+}
+
+static int android_close(void *cookie)
+{
+    AAsset_close((AAsset *)cookie);
     return 0;
 }
 
diff --git a/raylib/src/platforms/rcore_desktop_glfw.c b/raylib/src/platforms/rcore_desktop_glfw.c
--- a/raylib/src/platforms/rcore_desktop_glfw.c
+++ b/raylib/src/platforms/rcore_desktop_glfw.c
@@ -1,6 +1,6 @@
 /**********************************************************************************************
 *
-*   rcore_desktop - Functions to manage window, graphics device and inputs
+*   rcore_desktop_glfw - Functions to manage window, graphics device and inputs
 *
 *   PLATFORM: DESKTOP: GLFW
 *       - Windows (Win32, Win64)
@@ -16,21 +16,18 @@
 *       - Improvement 01
 *       - Improvement 02
 *
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
-*
 *   CONFIGURATION:
 *       #define RCORE_PLATFORM_CUSTOM_FLAG
 *           Custom flag for rcore on target platform -not used-
 *
 *   DEPENDENCIES:
-*       - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...)
+*       - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX/macOS, FreeBSD...)
 *       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
 *
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -56,25 +53,48 @@
 
 // Support retrieving native window handlers
 #if defined(_WIN32)
-    typedef void *PVOID;
-    typedef PVOID HANDLE;
-    typedef HANDLE HWND;
+    #if !defined(HWND) && !defined(_MSVC_LANG)
+        #define HWND void*
+    #elif !defined(HWND) && defined(_MSVC_LANG)
+        typedef struct HWND__ *HWND;
+    #endif
+
+    #include "../external/win32_clipboard.h" // Clipboard image copy-paste
+
     #define GLFW_EXPOSE_NATIVE_WIN32
     #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h
     #include "GLFW/glfw3native.h"
 
     #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
         // NOTE: Those functions require linking with winmm library
-        unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
-        unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+        //#pragma warning(disable: 4273)
+        __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+        //#pragma warning(default: 4273)
     #endif
 #endif
 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
     #include <sys/time.h>               // Required for: timespec, nanosleep(), select() - POSIX
 
-    //#define GLFW_EXPOSE_NATIVE_X11      // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type
-    //#define GLFW_EXPOSE_NATIVE_WAYLAND
-    #include "GLFW/glfw3native.h"       // Required for: glfwGetX11Window()
+    #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
+                                        // Set appropriate expose macros based on available backends
+        #if defined(_GLFW_X11)
+            #define GLFW_EXPOSE_NATIVE_X11
+                #define Font X11Font    // Hack to fix 'Font' name collision
+                                        // The definition and references to the X11 Font type will be replaced by 'X11Font'
+                                        // Works as long as the current file consistently references any X11 Font as X11Font
+                                        // Since it is never referenced (as of writing), this does not pose an issue
+        #endif
+
+        #if defined(_GLFW_WAYLAND)
+            #define GLFW_EXPOSE_NATIVE_WAYLAND
+        #endif
+
+        #include "GLFW/glfw3native.h"   // Include native header only once, regardless of how many backends are defined
+                                        // Required for: glfwGetX11Window() and glfwGetWaylandWindow()
+        #if defined(_GLFW_X11)          // Clean up X11-specific hacks
+            #undef Font                 // Revert hack and allow normal raylib Font usage
+        #endif
+    #endif
 #endif
 #if defined(__APPLE__)
     #include <unistd.h>                 // Required for: usleep()
@@ -84,6 +104,8 @@
     #include "GLFW/glfw3native.h"       // Required for: glfwGetCocoaWindow()
 #endif
 
+#include <stddef.h>  // Required for: size_t
+
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
 //----------------------------------------------------------------------------------
@@ -105,26 +127,32 @@
 void ClosePlatform(void);        // Close platform
 
 // Error callback event
-static void ErrorCallback(int error, const char *description);                             // GLFW3 Error Callback, runs on GLFW3 error
+static void ErrorCallback(int error, const char *description);                          // GLFW3 Error Callback, runs on GLFW3 error
 
 // Window callbacks events
-static void WindowSizeCallback(GLFWwindow *window, int width, int height);                 // GLFW3 WindowSize Callback, runs when window is resized
-static void WindowPosCallback(GLFWwindow* window, int x, int y);                     // GLFW3 WindowPos Callback, runs when window is moved
-static void WindowIconifyCallback(GLFWwindow *window, int iconified);                      // GLFW3 WindowIconify Callback, runs when window is minimized/restored
-static void WindowMaximizeCallback(GLFWwindow* window, int maximized);                     // GLFW3 Window Maximize Callback, runs when window is maximized
-static void WindowFocusCallback(GLFWwindow *window, int focused);                          // GLFW3 WindowFocus Callback, runs when window get/lose focus
-static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);         // GLFW3 Window Drop Callback, runs when drop files into window
-static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley);    // GLFW3 Window Content Scale Callback, runs when window changes scale
+static void WindowSizeCallback(GLFWwindow *window, int width, int height);              // GLFW3 WindowSize Callback, runs when window is resized
+static void FramebufferSizeCallback(GLFWwindow *window, int width, int height);         // GLFW3 FramebufferSize Callback, runs when window is resized
+static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
+static void WindowPosCallback(GLFWwindow *window, int x, int y);                        // GLFW3 WindowPos Callback, runs when window is moved
+static void WindowIconifyCallback(GLFWwindow *window, int iconified);                   // GLFW3 WindowIconify Callback, runs when window is minimized/restored
+static void WindowMaximizeCallback(GLFWwindow *window, int maximized);                  // GLFW3 Window Maximize Callback, runs when window is maximized
+static void WindowFocusCallback(GLFWwindow *window, int focused);                       // GLFW3 WindowFocus Callback, runs when window get/lose focus
+static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);      // GLFW3 Window Drop Callback, runs when drop files into window
 
 // Input callbacks events
-static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);  // GLFW3 Keyboard Callback, runs on key pressed
-static void CharCallback(GLFWwindow *window, unsigned int codepoint);                      // GLFW3 Char Callback, runs on key pressed (get codepoint value)
-static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);     // GLFW3 Mouse Button Callback, runs on mouse button pressed
-static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);                // GLFW3 Cursor Position Callback, runs on mouse move
-static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);       // GLFW3 Scrolling Callback, runs on mouse wheel
-static void CursorEnterCallback(GLFWwindow *window, int enter);                            // GLFW3 Cursor Enter Callback, cursor enters client area
-static void JoystickCallback(int jid, int event);                                           // GLFW3 Joystick Connected/Disconnected Callback
+static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
+static void CharCallback(GLFWwindow *window, unsigned int codepoint);                   // GLFW3 Char Callback, runs on key pressed (get codepoint value)
+static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);  // GLFW3 Mouse Button Callback, runs on mouse button pressed
+static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);             // GLFW3 Cursor Position Callback, runs on mouse move
+static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);    // GLFW3 Scrolling Callback, runs on mouse wheel
+static void CursorEnterCallback(GLFWwindow *window, int enter);                         // GLFW3 Cursor Enter Callback, cursor enters client area
+static void JoystickCallback(int jid, int event);                                       // GLFW3 Joystick Connected/Disconnected Callback
 
+// Memory allocator wrappers [used by glfwInitAllocator()]
+static void *AllocateWrapper(size_t size, void *user);                                  // GLFW3 GLFWallocatefun, wrapps around RL_CALLOC macro
+static void *ReallocateWrapper(void *block, size_t size, void *user);                   // GLFW3 GLFWreallocatefun, wrapps around RL_REALLOC macro
+static void DeallocateWrapper(void *block, void *user);                                 // GLFW3 GLFWdeallocatefun, wraps around RL_FREE macro
+
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
@@ -145,124 +173,149 @@
 // Toggle fullscreen mode
 void ToggleFullscreen(void)
 {
-    if (!CORE.Window.fullscreen)
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
     {
-        // Store previous window position (in case we exit fullscreen)
+        // Store previous screen data (in case exiting fullscreen)
         CORE.Window.previousPosition = CORE.Window.position;
+        CORE.Window.previousScreen = CORE.Window.screen;
 
+        // Use current monitor the window is on to get fullscreen required size
         int monitorCount = 0;
         int monitorIndex = GetCurrentMonitor();
         GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
-
-        // Use current monitor, so we correctly get the display the window is on
         GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL;
 
-        if (monitor == NULL)
+        if (monitor != NULL)
         {
-            TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
+            // Get current monitor video mode
+            const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitorIndex]);
+            CORE.Window.display.width = mode->width;
+            CORE.Window.display.height = mode->height;
 
-            CORE.Window.fullscreen = false;
-            CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+            CORE.Window.position = (Point){ 0, 0 };
+            CORE.Window.screen = CORE.Window.display;
 
-            glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
-        }
-        else
-        {
-            CORE.Window.fullscreen = true;
-            CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+            // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly
+            FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
 
+#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
+            // NOTE: X11 requires undecorating the window before switching to
+            // fullscreen to avoid issues with framebuffer scaling
+            glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
+            FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
+#endif
+            // WARNING: This function launches FramebufferSizeCallback()
             glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
         }
-
+        else TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor");
     }
     else
     {
-        CORE.Window.fullscreen = false;
-        CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+        // Restore previous window position and size
+        CORE.Window.position = CORE.Window.previousPosition;
+        CORE.Window.screen = CORE.Window.previousScreen;
 
-        glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+        // Set fullscreen flag to be processed on FramebufferSizeCallback() accordingly
+        // and considered by GetWindowScaleDPI()
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
 
-        // we update the window position right away
-        CORE.Window.position.x = CORE.Window.previousPosition.x;
-        CORE.Window.position.y = CORE.Window.previousPosition.y;
+#if !defined(__APPLE__)
+        // Make sure to restore render size considering HighDPI scaling
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+        {
+            Vector2 scaleDpi = GetWindowScaleDPI();
+            CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width * scaleDpi.x);
+            CORE.Window.screen.height = (unsigned int)(CORE.Window.screen.height * scaleDpi.y);
+        }
+#endif
+
+        // WARNING: This function launches FramebufferSizeCallback()
+        glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y,
+            CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
+
+#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
+        // NOTE: X11 requires restoring the decorated window after switching from
+        // fullscreen to avoid issues with framebuffer scaling
+        glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
+#endif
     }
 
     // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
     // NOTE: V-Sync can be enabled by graphic driver configuration
-    if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) glfwSwapInterval(1);
 }
 
 // Toggle borderless windowed mode
 void ToggleBorderlessWindowed(void)
 {
     // Leave fullscreen before attempting to set borderless windowed mode
-    bool wasOnFullscreen = false;
-    if (CORE.Window.fullscreen)
-    {
-        // fullscreen already saves the previous position so it does not need to be set here again
-        ToggleFullscreen();
-        wasOnFullscreen = true;
-    }
+    // NOTE: Fullscreen already saves the previous position so it does not need to be set again later
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen();
 
-    const int monitor = GetCurrentMonitor();
-    int monitorCount;
+    int monitorCount = 0;
     GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+    const int monitor = GetCurrentMonitor();
 
     if ((monitor >= 0) && (monitor < monitorCount))
     {
         const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
 
-        if (mode)
+        if (mode != NULL)
         {
-            if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE))
+            if (!FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
             {
                 // Store screen position and size
                 // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here
-                if (!wasOnFullscreen) CORE.Window.previousPosition = CORE.Window.position;
+                CORE.Window.previousPosition = CORE.Window.position;
                 CORE.Window.previousScreen = CORE.Window.screen;
 
-                // Set undecorated and topmost modes and flags
+                // Set undecorated flag
                 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
-                CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
-                glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
-                CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
+                FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
 
                 // Get monitor position and size
-                int monitorPosX = 0;
-                int monitorPosY = 0;
-                glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY);
-                const int monitorWidth = mode->width;
-                const int monitorHeight = mode->height;
+                glfwGetMonitorPos(monitors[monitor], &CORE.Window.position.x, &CORE.Window.position.y);
+                CORE.Window.screen.width = mode->width;
+                CORE.Window.screen.height = mode->height;
 
                 // Set screen position and size
-                glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY);
-                glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight);
+                glfwSetWindowMonitor(platform.handle, monitors[monitor], CORE.Window.position.x, CORE.Window.position.y,
+                    CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
 
                 // Refocus window
                 glfwFocusWindow(platform.handle);
 
-                CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
+                FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
             }
             else
             {
-                // Remove topmost and undecorated modes and flags
-                glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
-                CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
+                // Restore previous screen values
+                CORE.Window.position = CORE.Window.previousPosition;
+                CORE.Window.screen = CORE.Window.previousScreen;
+
+                // Remove undecorated flag
                 glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
-                CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
+                FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
 
-                // Return previous screen size and position
-                // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly
-                glfwSetWindowSize(platform.handle,  CORE.Window.previousScreen.width, CORE.Window.previousScreen.height);
-                glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y);
+            #if !defined(__APPLE__)
+                // Make sure to restore size considering HighDPI scaling
+                if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+                {
+                    Vector2 scaleDpi = GetWindowScaleDPI();
+                    CORE.Window.screen.width = (unsigned int)(CORE.Window.screen.width * scaleDpi.x);
+                    CORE.Window.screen.height = (unsigned int)(CORE.Window.screen.height * scaleDpi.y);
+                }
+            #endif
 
+                // Return to previous screen size and position
+                glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y,
+                    CORE.Window.screen.width, CORE.Window.screen.height, mode->refreshRate);
+
                 // Refocus window
                 glfwFocusWindow(platform.handle);
 
-                CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
-
-                CORE.Window.position.x = CORE.Window.previousPosition.x;
-                CORE.Window.position.y = CORE.Window.previousPosition.y;
+                FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
             }
         }
         else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
@@ -276,7 +329,7 @@
     if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
     {
         glfwMaximizeWindow(platform.handle);
-        CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
     }
 }
 
@@ -287,130 +340,132 @@
     glfwIconifyWindow(platform.handle);
 }
 
-// Set window state: not minimized/maximized
+// Restore window from being minimized/maximized
 void RestoreWindow(void)
 {
     if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
     {
         // Restores the specified window if it was previously iconified (minimized) or maximized
         glfwRestoreWindow(platform.handle);
-        CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
-        CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
     }
 }
 
 // Set window configuration state using flags
 void SetWindowState(unsigned int flags)
 {
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
+
     // Check previous state and requested state to apply required changes
     // NOTE: In most cases the functions already change the flags internally
 
     // State change: FLAG_VSYNC_HINT
-    if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT) != FLAG_IS_SET(flags, FLAG_VSYNC_HINT)) && FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
     {
         glfwSwapInterval(1);
-        CORE.Window.flags |= FLAG_VSYNC_HINT;
+        FLAG_SET(CORE.Window.flags, FLAG_VSYNC_HINT);
     }
 
     // State change: FLAG_BORDERLESS_WINDOWED_MODE
     // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
-    if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) != FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)) && FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
         ToggleBorderlessWindowed();     // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_FULLSCREEN_MODE
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) != FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)) && FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
     {
         ToggleFullscreen();     // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_RESIZABLE
-    if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
-        CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
     }
 
     // State change: FLAG_WINDOW_UNDECORATED
-    if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED) != FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)) && FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE);
-        CORE.Window.flags |= FLAG_WINDOW_UNDECORATED;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
     }
 
     // State change: FLAG_WINDOW_HIDDEN
-    if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN) != FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)) && FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
     {
         glfwHideWindow(platform.handle);
-        CORE.Window.flags |= FLAG_WINDOW_HIDDEN;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
     }
 
     // State change: FLAG_WINDOW_MINIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
     {
         //GLFW_ICONIFIED
         MinimizeWindow();       // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_MAXIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
     {
         //GLFW_MAXIMIZED
         MaximizeWindow();       // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_UNFOCUSED
-    if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED) != FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)) && FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE);
-        CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
     }
 
     // State change: FLAG_WINDOW_TOPMOST
-    if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST) != FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)) && FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE);
-        CORE.Window.flags |= FLAG_WINDOW_TOPMOST;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST);
     }
 
     // State change: FLAG_WINDOW_ALWAYS_RUN
-    if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN) != FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)) && FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
     {
-        CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
     }
 
     // The following states can not be changed after window creation
 
     // State change: FLAG_WINDOW_TRANSPARENT
-    if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT) != FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)) && FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
     {
         TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
     }
 
     // State change: FLAG_WINDOW_HIGHDPI
-    if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) != FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)) && FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
     {
         TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
     }
 
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
-    if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH) != FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) && FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
-        CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH);
     }
 
     // State change: FLAG_MSAA_4X_HINT
-    if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT) != FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)) && FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
     {
         TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
     }
 
     // State change: FLAG_INTERLACED_HINT
-    if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT) != FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)) && FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
     {
-        TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
+        TRACELOG(LOG_WARNING, "WINDOW: Interlaced mode can only be configured before window initialization");
     }
 }
 
@@ -421,107 +476,107 @@
     // NOTE: In most cases the functions already change the flags internally
 
     // State change: FLAG_VSYNC_HINT
-    if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) && (FLAG_IS_SET(flags, FLAG_VSYNC_HINT)))
     {
         glfwSwapInterval(0);
-        CORE.Window.flags &= ~FLAG_VSYNC_HINT;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_VSYNC_HINT);
     }
 
     // State change: FLAG_BORDERLESS_WINDOWED_MODE
     // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running
-    if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) && (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE)))
     {
-        ToggleBorderlessWindowed();     // NOTE: Window state flag updated inside function
+        ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_FULLSCREEN_MODE
-    if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) && (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE)))
     {
-        ToggleFullscreen();     // NOTE: Window state flag updated inside function
+        ToggleFullscreen(); // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_RESIZABLE
-    if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
-        CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
     }
 
     // State change: FLAG_WINDOW_HIDDEN
-    if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) && (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN)))
     {
         glfwShowWindow(platform.handle);
-        CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
     }
 
     // State change: FLAG_WINDOW_MINIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)))
     {
         RestoreWindow();       // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_MAXIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
     {
         RestoreWindow();       // NOTE: Window state flag updated inside function
     }
 
     // State change: FLAG_WINDOW_UNDECORATED
-    if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE);
-        CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNDECORATED);
     }
 
     // State change: FLAG_WINDOW_UNFOCUSED
-    if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE);
-        CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
     }
 
     // State change: FLAG_WINDOW_TOPMOST
-    if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) && (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE);
-        CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_TOPMOST);
     }
 
     // State change: FLAG_WINDOW_ALWAYS_RUN
-    if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)) && (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN)))
     {
-        CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
     }
 
     // The following states can not be changed after window creation
 
     // State change: FLAG_WINDOW_TRANSPARENT
-    if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) && (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT)))
     {
         TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
     }
 
     // State change: FLAG_WINDOW_HIGHDPI
-    if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) && (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI)))
     {
         TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
     }
 
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
-    if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
-        CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH);
     }
 
     // State change: FLAG_MSAA_4X_HINT
-    if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) && (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT)))
     {
         TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization");
     }
 
     // State change: FLAG_INTERLACED_HINT
-    if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT)) && (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT)))
     {
         TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
     }
@@ -569,7 +624,7 @@
     else
     {
         int valid = 0;
-        GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage));
+        GLFWimage *icons = (GLFWimage *)RL_CALLOC(count, sizeof(GLFWimage));
 
         for (int i = 0; i < count; i++)
         {
@@ -614,7 +669,7 @@
 
     if ((monitor >= 0) && (monitor < monitorCount))
     {
-        if (CORE.Window.fullscreen)
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
         {
             TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor]));
 
@@ -698,6 +753,12 @@
     glfwFocusWindow(platform.handle);
 }
 
+#if defined(__linux__) && defined(_GLFW_X11)
+// Local storage for the window handle returned by glfwGetX11Window
+// This is needed as X11 handles are integers and may not fit inside a pointer depending on platform
+// Storing the handle locally and returning a pointer in GetWindowHandle allows the code to work regardless of pointer width
+static XID X11WindowHandle;
+#endif
 // Get native window handle
 void *GetWindowHandle(void)
 {
@@ -706,12 +767,27 @@
     return glfwGetWin32Window(platform.handle);
 #endif
 #if defined(__linux__)
-    // NOTE: Returned handle is: unsigned long Window (X.h)
-    // typedef unsigned long XID;
-    // typedef XID Window;
-    //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle);
-    //return NULL;    // TODO: Find a way to return value... cast to void *?
-    return (void *)platform.handle;
+    #if defined(_GLFW_WAYLAND)
+        #if defined(_GLFW_X11)
+            int platformID = glfwGetPlatform();
+            if (platformID == GLFW_PLATFORM_WAYLAND)
+            {
+                return glfwGetWaylandWindow(platform.handle);
+            }
+            else
+            {
+                X11WindowHandle = glfwGetX11Window(platform.handle);
+                return &X11WindowHandle;
+            }
+        #else
+            return glfwGetWaylandWindow(platform.handle);
+        #endif
+    #elif defined(_GLFW_X11)
+        // Store the window handle localy and return a pointer to the variable instead
+        // Reasoning detailed in the declaration of X11WindowHandle
+        X11WindowHandle = glfwGetX11Window(platform.handle);
+        return &X11WindowHandle;
+    #endif
 #endif
 #if defined(__APPLE__)
     // NOTE: Returned handle is: (objc_object *)
@@ -731,7 +807,7 @@
     return monitorCount;
 }
 
-// Get number of monitors
+// Get current monitor where window is placed
 int GetCurrentMonitor(void)
 {
     int index = 0;
@@ -829,7 +905,8 @@
 
     if ((monitor >= 0) && (monitor < monitorCount))
     {
-        int x, y;
+        int x = 0;
+        int y = 0;
         glfwGetMonitorPos(monitors[monitor], &x, &y);
 
         return (Vector2){ (float)x, (float)y };
@@ -911,8 +988,11 @@
 
     if ((monitor >= 0) && (monitor < monitorCount))
     {
-        const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]);
-        refresh = vidmode->refreshRate;
+        const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]);
+
+        if (mode) refresh = mode->refreshRate;
+        else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor");
+
     }
     else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor");
 
@@ -936,19 +1016,15 @@
 // Get window position XY on monitor
 Vector2 GetWindowPosition(void)
 {
-    int x = 0;
-    int y = 0;
-
-    glfwGetWindowPos(platform.handle, &x, &y);
-
-    return (Vector2){ (float)x, (float)y };
+    return (Vector2){ (float)CORE.Window.position.x, (float)CORE.Window.position.y };
 }
 
 // Get window scale DPI factor for current monitor
 Vector2 GetWindowScaleDPI(void)
 {
-    Vector2 scale = {0};
-    glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
+    Vector2 scale = { 1.0f, 1.0f };
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI) && !FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+        glfwGetWindowContentScale(platform.handle, &scale.x, &scale.y);
     return scale;
 }
 
@@ -965,10 +1041,35 @@
     return glfwGetClipboardString(platform.handle);
 }
 
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if defined(_WIN32)
+    unsigned long long int dataSize = 0;
+    void *fileData = NULL;
+    int width = 0;
+    int height = 0;
+
+    fileData  = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
+
+    if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data.");
+    else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, (int)dataSize);
+#else
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+#endif
+#endif // SUPPORT_CLIPBOARD_IMAGE
+
+    return image;
+}
+
 // Show mouse cursor
 void ShowCursor(void)
 {
     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+
     CORE.Input.Mouse.cursorHidden = false;
 }
 
@@ -976,6 +1077,7 @@
 void HideCursor(void)
 {
     glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+
     CORE.Input.Mouse.cursorHidden = true;
 }
 
@@ -990,19 +1092,21 @@
     if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
 
     CORE.Input.Mouse.cursorHidden = false;
+    CORE.Input.Mouse.cursorLocked = false;
 }
 
 // Disables cursor (lock cursor)
 void DisableCursor(void)
 {
-    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
-
-    // Set cursor position in the middle
+    // Reset mouse position within the window area before disabling cursor
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
+    glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+
     if (glfwRawMouseMotionSupported()) glfwSetInputMode(platform.handle, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
 
     CORE.Input.Mouse.cursorHidden = true;
+    CORE.Input.Mouse.cursorLocked = true;
 }
 
 // Swap back buffer with front buffer (screen drawing)
@@ -1023,10 +1127,10 @@
 }
 
 // Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
+// REF: https://github.com/raysan5/raylib/issues/686
 void OpenURL(const char *url)
 {
     // Security check to (partially) avoid malicious code
@@ -1062,7 +1166,7 @@
 // Set gamepad vibration
 void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
 {
-    TRACELOG(LOG_WARNING, "GamepadSetVibration() not available on target platform");
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() not available on target platform");
 }
 
 // Set mouse position XY
@@ -1087,7 +1191,7 @@
     }
 }
 
-// Get physical key name.
+// Get physical key name
 const char *GetKeyName(int key)
 {
     return glfwGetKeyName(key, glfwGetKeyScancode(key));
@@ -1137,9 +1241,9 @@
 
     // Map touch position to mouse position for convenience
     // WARNING: If the target desktop device supports touch screen, this behaviour should be reviewed!
-    // TODO: GLFW does not support multi-touch input just yet
-    // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
-    // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
+    // TODO: GLFW does not support multi-touch input yet
+    // REF: https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch
+    // REF: https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
     CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
 
     // Check if gamepads are ready
@@ -1161,7 +1265,13 @@
             // Get current gamepad state
             // NOTE: There is no callback available, so we get it manually
             GLFWgamepadstate state = { 0 };
-            glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller
+            int result = glfwGetGamepadState(i, &state); // This remaps all gamepads so they have their buttons mapped like an xbox controller
+            if (result == GLFW_FALSE) // No joystick is connected, no gamepad mapping or an error occurred
+            {
+                // Setting axes to expected resting value instead of GLFW 0.0f default when gamepad is not connected
+                state.axes[GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+                state.axes[GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+            }
 
             const unsigned char *buttons = state.buttons;
 
@@ -1204,7 +1314,7 @@
                 }
             }
 
-            // Get current axis state
+            // Get current state of axes
             const float *axes = state.axes;
 
             for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1); k++)
@@ -1212,9 +1322,19 @@
                 CORE.Input.Gamepad.axisState[i][k] = axes[k];
             }
 
-            // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis)
-            CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f);
-            CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f);
+            // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather as axes)
+            if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f)
+            {
+                CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 1;
+                CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_LEFT_TRIGGER_2;
+            }
+            else CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = 0;
+            if (CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f)
+            {
+                CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = 1;
+                CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
+            }
+            else CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = 0;
 
             CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
         }
@@ -1222,12 +1342,14 @@
 
     CORE.Window.resizedLastFrame = false;
 
-    if (CORE.Window.eventWaiting) glfwWaitEvents();     // Wait for in input events before continue (drawing is paused)
+    if ((CORE.Window.eventWaiting) ||
+        (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)))
+    {
+        glfwWaitEvents();     // Wait for in input events before continue (drawing is paused)
+        CORE.Time.previous = GetTime();
+    }
     else glfwPollEvents();      // Poll input events: keyboard/mouse/window events (callbacks) -> Update keys state
 
-    // While window minimized, stop loop execution
-    while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents();
-
     CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle);
 
     // Reset close status for next frame
@@ -1237,35 +1359,38 @@
 //----------------------------------------------------------------------------------
 // Module Internal Functions Definition
 //----------------------------------------------------------------------------------
-
-static void SetDimensionsFromMonitor(GLFWmonitor *monitor)
+// Function wrappers around RL_*alloc macros, used by glfwInitAllocator() inside of InitPlatform()
+// We need to provide these because GLFWallocator expects function pointers with specific signatures
+// REF: https://www.glfw.org/docs/latest/intro_guide.html#init_allocator
+static void *AllocateWrapper(size_t size, void *user)
 {
-  const GLFWvidmode *mode = glfwGetVideoMode(monitor);
-
-  // Default display resolution to that of the current mode
-  CORE.Window.display.width = mode->width;
-  CORE.Window.display.height = mode->height;
-
-  // Set screen width/height to the display width/height if they are 0
-  if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
-  if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
+    (void)user;
+    return RL_CALLOC(size, 1);
 }
+static void *ReallocateWrapper(void *block, size_t size, void *user)
+{
+    (void)user;
+    return RL_REALLOC(block, size);
+}
+static void DeallocateWrapper(void *block, void *user)
+{
+    (void)user;
+    RL_FREE(block);
+}
 
 // Initialize platform: graphics, inputs and more
 int InitPlatform(void)
 {
     glfwSetErrorCallback(ErrorCallback);
-/*
-    // TODO: Setup GLFW custom allocators to match raylib ones
+
     const GLFWallocator allocator = {
-        .allocate = MemAlloc,
-        .deallocate = MemFree,
-        .reallocate = MemRealloc,
-        .user = NULL
+        .allocate = AllocateWrapper,
+        .reallocate = ReallocateWrapper,
+        .deallocate = DeallocateWrapper,
+        .user = NULL, // RL_*ALLOC macros are not capable of handling user-provided data
     };
 
     glfwInitAllocator(&allocator);
-*/
 
 #if defined(__APPLE__)
     glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE);
@@ -1291,51 +1416,62 @@
     // additionally auto iconify restores the hardware resolution of the monitor if the window that loses focus is a fullscreen window
     glfwWindowHint(GLFW_AUTO_ICONIFY, 0);
 
-    // Check window creation flags
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
+    // Window flags requested before initialization to be applied after initialization
+    unsigned int requestedWindowFlags = CORE.Window.flags;
 
-    if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
+    // Check window creation flags
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
     else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);     // Window initially hidden
 
-    if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
     else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);   // Decorated window
 
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
     else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);  // Avoid window being resizable
 
     // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
 
     // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
-    if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
     else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
     else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
 
     // NOTE: Some GLFW flags are not supported on HTML5
-    if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);     // Transparent framebuffer
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);     // Transparent framebuffer
     else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE);  // Opaque framebuffer
 
-    if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
     {
-        // Resize window content area based on the monitor content scale.
-        // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11.
-        // On platforms like macOS the resolution of the framebuffer is changed independently of the window size.
-        glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);   // Scale content area based on the monitor content scale where window is placed on
 #if defined(__APPLE__)
-        glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
+        glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
 #endif
+        // Resize window content area based on the monitor content scale
+        // NOTE: This hint only has an effect on platforms where screen coordinates and
+        // pixels always map 1:1 such as Windows and X11
+        // On platforms like macOS the resolution of the framebuffer is changed independently of the window size
+        glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+#if defined(__APPLE__)
+        glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
+#endif
     }
-    else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
+    else
+    {
+        glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
+#if defined(__APPLE__)
+        glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_FALSE);
+#endif
+    }
 
     // Mouse passthrough
-    if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
     else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
 
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
     {
         // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
@@ -1343,8 +1479,8 @@
     }
 
     // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
-    // with backward compatibility to older OpenGL versions.
-    // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
+    // with backward compatibility to older OpenGL versions
+    // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context
 
     // Check selection OpenGL version
     if (rlGetVersion() == RL_OPENGL_21)
@@ -1390,94 +1526,76 @@
         glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
     }
 
-    // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions.
-    // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn.
-    // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience.
+    // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions
+    // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn
+    // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience
     // REF: https://github.com/raysan5/raylib/issues/1554
     glfwSetJoystickCallback(NULL);
 
-    GLFWmonitor *monitor = NULL;
-    if (CORE.Window.fullscreen)
-    {
-        // According to glfwCreateWindow(), if the user does not have a choice, fullscreen applications
-        // should default to the primary monitor.
+    if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0)) FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
 
-        monitor = glfwGetPrimaryMonitor();
+    // Init window in fullscreen mode if requested
+    // NOTE: Keeping original screen size for toggle
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+    {
+        // NOTE: Fullscreen applications default to the primary monitor
+        GLFWmonitor *monitor = glfwGetPrimaryMonitor();
         if (!monitor)
         {
-          TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
-          return -1;
+            TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor");
+            return -1;
         }
 
-        SetDimensionsFromMonitor(monitor);
+        // Set dimensions from monitor
+        const GLFWvidmode *mode = glfwGetVideoMode(monitor);
 
-        // Remember center for switching from fullscreen to window
-        if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
+        // Default display resolution to that of the current mode
+        CORE.Window.display.width = mode->width;
+        CORE.Window.display.height = mode->height;
+
+        // Check if user requested some screen size
+        if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
         {
-            // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
-            // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
-            CORE.Window.position.x = CORE.Window.display.width/4;
-            CORE.Window.position.y = CORE.Window.display.height/4;
+            // Set some default screen size in case user decides to exit fullscreen mode
+            CORE.Window.previousScreen.width = 800;
+            CORE.Window.previousScreen.height = 450;
+            CORE.Window.previousPosition.x = CORE.Window.display.width/2 - 800/2;
+            CORE.Window.previousPosition.y = CORE.Window.display.height/2 - 450/2;
+
+            // Set screen width/height to the display width/height
+            if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
+            if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
         }
         else
         {
-            CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2;
-            CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2;
+            CORE.Window.previousScreen = CORE.Window.screen;
+            CORE.Window.screen = CORE.Window.display;
         }
 
-        if (CORE.Window.position.x < 0) CORE.Window.position.x = 0;
-        if (CORE.Window.position.y < 0) CORE.Window.position.y = 0;
-
-        // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor
-        int count = 0;
-        const GLFWvidmode *modes = glfwGetVideoModes(monitor, &count);
-
-        // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height
-        for (int i = 0; i < count; i++)
+        platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
+        if (!platform.handle)
         {
-            if ((unsigned int)modes[i].width >= CORE.Window.screen.width)
-            {
-                if ((unsigned int)modes[i].height >= CORE.Window.screen.height)
-                {
-                    CORE.Window.display.width = modes[i].width;
-                    CORE.Window.display.height = modes[i].height;
-                    break;
-                }
-            }
+            glfwTerminate();
+            TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
+            return -1;
         }
-
-        TRACELOG(LOG_INFO, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
-
-        // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
-        // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
-        // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
-        // by the sides to fit all monitor space...
-
-        // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
-        // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
-        // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
-        // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
-        // HighDPI monitors are properly considered in a following similar function: SetupViewport()
-        SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
-
-        platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", monitor, NULL);
-
-        // NOTE: Full-screen change, not working properly...
-        //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE);
     }
     else
     {
-        // No-fullscreen window creation
-        bool requestWindowedFullscreen = (CORE.Window.screen.height == 0) && (CORE.Window.screen.width == 0);
-
-        // Default to at least one pixel in size, as creation with a zero dimension is not allowed.
-        int creationWidth = CORE.Window.screen.width != 0 ? CORE.Window.screen.width : 1;
-        int creationHeight = CORE.Window.screen.height != 0 ? CORE.Window.screen.height : 1;
+        // Default to at least one pixel in size, as creation with a zero dimension is not allowed
+        if (CORE.Window.screen.width == 0) CORE.Window.screen.width = 1;
+        if (CORE.Window.screen.height == 0) CORE.Window.screen.height = 1;
 
-        platform.handle = glfwCreateWindow(creationWidth, creationHeight, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
+        platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL);
+        if (!platform.handle)
+        {
+            glfwTerminate();
+            TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
+            return -1;
+        }
 
-        // After the window was created, determine the monitor that the window manager assigned.
-        // Derive display sizes, and, if possible, window size in case it was zero at beginning.
+        // After the window was created, determine the monitor that the window manager assigned
+        // Derive display sizes and, if possible, window size in case it was zero at beginning
 
         int monitorCount = 0;
         int monitorIndex = GetCurrentMonitor();
@@ -1485,47 +1603,46 @@
 
         if (monitorIndex < monitorCount)
         {
-            monitor = monitors[monitorIndex];
-            SetDimensionsFromMonitor(monitor);
+            GLFWmonitor *monitor = monitors[monitorIndex];
+            const GLFWvidmode *mode = glfwGetVideoMode(monitor);
 
-            if (requestWindowedFullscreen) glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
+            // Default display resolution to that of the current mode
+            CORE.Window.display.width = mode->width;
+            CORE.Window.display.height = mode->height;
+
+            // Set screen width/height to the display width/height if they are 0
+            if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width;
+            if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height;
+
+            glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
         }
         else
         {
-            // The monitor for the window-manager-created window can not be determined, so it can not be centered.
+            // The monitor for the window-manager-created window can not be determined, so it can not be centered
             glfwTerminate();
             TRACELOG(LOG_WARNING, "GLFW: Failed to determine Monitor to center Window");
             return -1;
         }
 
-        if (platform.handle)
-        {
-            CORE.Window.render.width = CORE.Window.screen.width;
-            CORE.Window.render.height = CORE.Window.screen.height;
-        }
-    }
-
-    if (!platform.handle)
-    {
-        glfwTerminate();
-        TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
-        return -1;
+        // NOTE: Not considering scale factor now, considered below
+        CORE.Window.render.width = CORE.Window.screen.width;
+        CORE.Window.render.height = CORE.Window.screen.height;
     }
 
     glfwMakeContextCurrent(platform.handle);
     result = glfwGetError(NULL);
+    if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) CORE.Window.ready = true; // Checking context activation
 
-    // Check context activation
-    if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR))
+    if (CORE.Window.ready)
     {
-        CORE.Window.ready = true;
+        // Setup additional windows configs and register required window size info
 
-        glfwSwapInterval(0);        // No V-Sync by default
+        glfwSwapInterval(0); // No V-Sync by default
 
         // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
         // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need
-        // to be activated on web platforms since VSync is enforced there.
-        if (CORE.Window.flags & FLAG_VSYNC_HINT)
+        // to be activated on web platforms since VSync is enforced there
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT))
         {
             // WARNING: It seems to hit a critical render path in Intel HD Graphics
             glfwSwapInterval(1);
@@ -1535,19 +1652,20 @@
         int fbWidth = CORE.Window.screen.width;
         int fbHeight = CORE.Window.screen.height;
 
-        if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
         {
-            // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling.
-            // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE);
-    #if !defined(__APPLE__)
+            // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
+            // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
+
+            // Get current framebuffer size, on high-dpi it could be bigger than screen size
             glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight);
 
             // Screen scaling matrix is required in case desired screen area is different from display area
             CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f);
-
+#if !defined(__APPLE__)
             // Mouse input scaling for the new screen size
             SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight);
-    #endif
+#endif
         }
 
         CORE.Window.render.width = fbWidth;
@@ -1560,38 +1678,39 @@
         TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
         TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
         TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
-    }
-    else
-    {
-        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
-        return -1;
-    }
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
-
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
-    else
-    {
         // Try to center window on screen but avoiding window-bar outside of screen
+        int monitorCount = 0;
+        int monitorIndex = GetCurrentMonitor();
+        GLFWmonitor **monitors = glfwGetMonitors(&monitorCount);
+        GLFWmonitor *monitor = monitors[monitorIndex];
+
         int monitorX = 0;
         int monitorY = 0;
         int monitorWidth = 0;
         int monitorHeight = 0;
         glfwGetMonitorWorkarea(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
 
-        // Here CORE.Window.render.width/height should be used instead of CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled.
-        int posX = monitorX + (monitorWidth - (int)CORE.Window.render.width)/2;
-        int posY = monitorY + (monitorHeight - (int)CORE.Window.render.height)/2;
-        if (posX < monitorX) posX = monitorX;
-        if (posY < monitorY) posY = monitorY;
-        SetWindowPosition(posX, posY);
+        // TODO: Here CORE.Window.render.width/height should be used instead of
+        // CORE.Window.screen.width/height to center the window correctly when the high dpi flag is enabled
+        CORE.Window.position.x = monitorX + (monitorWidth - (int)CORE.Window.screen.width)/2;
+        CORE.Window.position.y = monitorY + (monitorHeight - (int)CORE.Window.screen.height)/2;
+        //if (CORE.Window.position.x < monitorX) CORE.Window.position.x = monitorX;
+        //if (CORE.Window.position.y < monitorY) CORE.Window.position.y = monitorY;
 
-        // Update CORE.Window.position here so it is correct from the start
-        CORE.Window.position.x = posX;
-        CORE.Window.position.y = posY;
+        SetWindowPosition(CORE.Window.position.x, CORE.Window.position.y);
+
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
     }
+    else
+    {
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+        return -1;
+    }
 
+    // Apply window flags requested previous to initialization
+    SetWindowState(requestedWindowFlags);
+
     // Load OpenGL extensions
     // NOTE: GL procedures address loader is required to load extensions
     rlLoadExtensions(glfwGetProcAddress);
@@ -1600,33 +1719,36 @@
     // Initialize input events callbacks
     //----------------------------------------------------------------------------
     // Set window callback events
-    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);      // NOTE: Resizing not allowed by default!
+    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing is not enabled by default
+    glfwSetFramebufferSizeCallback(platform.handle, FramebufferSizeCallback);
     glfwSetWindowPosCallback(platform.handle, WindowPosCallback);
     glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback);
     glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
     glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
     glfwSetDropCallback(platform.handle, WindowDropCallback);
-
-    if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
-    {
-       glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
-    }
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
 
     // Set input callback events
     glfwSetKeyCallback(platform.handle, KeyCallback);
     glfwSetCharCallback(platform.handle, CharCallback);
     glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
-    glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback);   // Track mouse position changes
+    glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
     glfwSetScrollCallback(platform.handle, MouseScrollCallback);
     glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
     glfwSetJoystickCallback(JoystickCallback);
-
-    glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE);    // Enable lock keys modifiers (CAPS, NUM)
+    glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM)
 
     // Retrieve gamepad names
     for (int i = 0; i < MAX_GAMEPADS; i++)
     {
-        if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i));
+        // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
+        // we can get a not-NULL terminated string, so, we only copy up to (MAX_GAMEPAD_NAME_LENGTH - 1)
+        if (glfwJoystickPresent(i))
+        {
+          CORE.Input.Gamepad.ready[i] = true;
+          CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1;
+          strncpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i), MAX_GAMEPAD_NAME_LENGTH - 1);
+        }
     }
     //----------------------------------------------------------------------------
 
@@ -1642,7 +1764,7 @@
 
 #if defined(__NetBSD__)
     // Workaround for NetBSD
-    char *glfwPlatform = "X11";
+    char *glfwPlatform = "X11 (NetBSD)";
 #else
     char *glfwPlatform = "";
     switch (glfwGetPlatform())
@@ -1672,65 +1794,134 @@
 #endif
 }
 
-// GLFW3 Error Callback, runs on GLFW3 error
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+// NOTE: Those functions are only required for current platform
+//----------------------------------------------------------------------------------
+
+// GLFW3: Error callback, runs on GLFW3 error
 static void ErrorCallback(int error, const char *description)
 {
     TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
 }
 
-// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
-// NOTE: Window resizing not allowed by default
+// GLFW3: Window size change callback, runs when window is resized
+// NOTE: Window resizing not enabled by default, use SetConfigFlags()
 static void WindowSizeCallback(GLFWwindow *window, int width, int height)
 {
+    // Nothing to do for now on window resize...
+    //TRACELOG(LOG_INFO, "GLFW3: Window size callback called [%i,%i]", width, height);
+}
+
+// GLFW3: Framebuffer size change callback, runs when framebuffer is resized
+// WARNING: If FLAG_WINDOW_HIGHDPI is set, WindowContentScaleCallback() is called before this function
+static void FramebufferSizeCallback(GLFWwindow *window, int width, int height)
+{
+    //TRACELOG(LOG_INFO, "GLFW3: Window framebuffer size callback called [%i,%i]", width, height);
+
+    // WARNING: On window minimization, callback is called,
+    // but we don't want to change internal screen values, it breaks things
+    if ((width == 0) || (height == 0)) return;
+
     // Reset viewport and projection matrix for new size
+    // NOTE: Stores current render size: CORE.Window.render
     SetupViewport(width, height);
 
+    // Set render size
     CORE.Window.currentFbo.width = width;
     CORE.Window.currentFbo.height = height;
     CORE.Window.resizedLastFrame = true;
 
-    if (IsWindowFullscreen()) return;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+    {
+        // On fullscreen mode, strategy is ignoring high-dpi and
+        // use the all available display size
 
-    // Set current screen size
+        // Set screen size to render size (physical pixel size)
+        CORE.Window.screen.width = width;
+        CORE.Window.screen.height = height;
+        CORE.Window.screenScale = MatrixScale(1.0f, 1.0f, 1.0f);
+        SetMouseScale(1.0f, 1.0f);
+    }
+    else // Window mode (including borderless window)
+    {
+        // Check if render size was actually scaled for high-dpi
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+        {
+            // Set screen size to logical pixel size, considering content scaling
+            Vector2 scaleDpi = GetWindowScaleDPI();
+            CORE.Window.screen.width = (int)((float)width/scaleDpi.x);
+            CORE.Window.screen.height = (int)((float)height/scaleDpi.y);
+            CORE.Window.screenScale = MatrixScale(scaleDpi.x, scaleDpi.y, 1.0f);
+#if !defined(__APPLE__)
+            // Mouse input scaling for the new screen size
+            SetMouseScale(1.0f/scaleDpi.x, 1.0f/scaleDpi.y);
+#endif
+        }
+        else
+        {
+            // Set screen size to render size (physical pixel size)
+            CORE.Window.screen.width = width;
+            CORE.Window.screen.height = height;
+        }
+    }
 
-    CORE.Window.screen.width = width;
-    CORE.Window.screen.height = height;
+    // WARNING: If using a render texture, it is not scaled to new size
+}
 
-    // NOTE: Postprocessing texture is not scaled to new size
+// GLFW3: Window content scale callback, runs on monitor content scale change detected
+// WARNING: If FLAG_WINDOW_HIGHDPI is not set, this function is not called
+static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
+{
+    //TRACELOG(LOG_INFO, "GLFW3: Window content scale changed, scale: [%.2f,%.2f]", scalex, scaley);
+
+    float fbWidth = (float)CORE.Window.screen.width*scalex;
+    float fbHeight = (float)CORE.Window.screen.height*scaley;
+
+    // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling
+    // Framebuffer scaling is activated with: glfwWindowHint(GLFW_SCALE_FRAMEBUFFER, GLFW_TRUE);
+    CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
+
+#if !defined(__APPLE__)
+    // Mouse input scaling for the new screen size
+    SetMouseScale(1.0f/scalex, 1.0f/scaley);
+#endif
+
+    CORE.Window.render.width = (int)fbWidth;
+    CORE.Window.render.height = (int)fbHeight;
+    CORE.Window.currentFbo = CORE.Window.render;
 }
-static void WindowPosCallback(GLFWwindow* window, int x, int y)
+
+// GLFW3: Window position callback, runs when window position changes
+static void WindowPosCallback(GLFWwindow *window, int x, int y)
 {
     // Set current window position
     CORE.Window.position.x = x;
     CORE.Window.position.y = y;
 }
-static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
-{
-    CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
-}
 
-// GLFW3 WindowIconify Callback, runs when window is minimized/restored
+// GLFW3: Window iconify callback, runs when window is minimized/restored
 static void WindowIconifyCallback(GLFWwindow *window, int iconified)
 {
-    if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;  // The window was iconified
-    else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;           // The window was restored
+    if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);   // The window was iconified
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);           // The window was restored
 }
 
-// GLFW3 WindowMaximize Callback, runs when window is maximized/restored
+// GLFW3: Window maximize callback, runs when window is maximized/restored
 static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
 {
-    if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;  // The window was maximized
-    else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;           // The window was restored
+    if (maximized) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);  // The window was maximized
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);          // The window was restored
 }
 
-// GLFW3 WindowFocus Callback, runs when window get/lose focus
+// GLFW3: Window focus callback, runs when window get/lose focus
 static void WindowFocusCallback(GLFWwindow *window, int focused)
 {
-    if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;   // The window was focused
-    else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;            // The window lost focus
+    if (focused) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);    // The window was focused
+    else FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);          // The window lost focus
 }
 
-// GLFW3 Window Drop Callback, runs when drop files into window
+// GLFW3: Window drop callback, runs when files are dropped into window
 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
 {
     if (count > 0)
@@ -1753,12 +1944,12 @@
         for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
         {
             CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
-            strcpy(CORE.Window.dropFilepaths[i], paths[i]);
+            strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
         }
     }
 }
 
-// GLFW3 Keyboard Callback, runs on key pressed
+// GLFW3: Keyboard callback, runs on key pressed
 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
 {
     if (key < 0) return;    // Security check, macOS fn key generates -1
@@ -1766,12 +1957,12 @@
     // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
     // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
     if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
-    else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
-    else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
+    else if (action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
+    else if (action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
 
     // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys
-    if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) ||
-        ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1;
+    if (((key == KEY_CAPS_LOCK) && (FLAG_IS_SET(mods, GLFW_MOD_CAPS_LOCK))) ||
+        ((key == KEY_NUM_LOCK) && (FLAG_IS_SET(mods, GLFW_MOD_NUM_LOCK)))) CORE.Input.Keyboard.currentKeyState[key] = 1;
 
     // Check if there is space available in the key queue
     if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
@@ -1785,15 +1976,13 @@
     if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
 }
 
-// GLFW3 Char Callback, get unicode codepoint value
+// GLFW3: Char callback, runs on key pressed to get unicode codepoint value
 static void CharCallback(GLFWwindow *window, unsigned int codepoint)
 {
-    //TRACELOG(LOG_DEBUG, "Char Callback: Codepoint: %i", codepoint);
-
     // NOTE: Registers any key down considering OS keyboard layout but
     // does not detect action events, those should be managed by user...
-    // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
-    // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
+    // REF: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
+    // REF: https://www.glfw.org/docs/latest/input_guide.html#input_char
 
     // Check if there is space available in the queue
     if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@@ -1804,7 +1993,7 @@
     }
 }
 
-// GLFW3 Mouse Button Callback, runs on mouse button pressed
+// GLFW3: Mouse button callback, runs on mouse button pressed
 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
 {
     // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
@@ -1840,7 +2029,7 @@
 #endif
 }
 
-// GLFW3 Cursor Position Callback, runs on mouse move
+// GLFW3: Cursor position callback, runs on mouse movement
 static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
 {
     CORE.Input.Mouse.currentPosition.x = (float)x;
@@ -1871,30 +2060,37 @@
 #endif
 }
 
-// GLFW3 Scrolling Callback, runs on mouse wheel
+// GLFW3: Mouse wheel scroll callback, runs on mouse wheel changes
 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
 {
     CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
 }
 
-// GLFW3 CursorEnter Callback, when cursor enters the window
+// GLFW3: Cursor ennter callback, when cursor enters the window
 static void CursorEnterCallback(GLFWwindow *window, int enter)
 {
     if (enter) CORE.Input.Mouse.cursorOnScreen = true;
     else CORE.Input.Mouse.cursorOnScreen = false;
 }
 
-// GLFW3 Joystick Connected/Disconnected Callback
+// GLFW3: Joystick connected/disconnected callback
 static void JoystickCallback(int jid, int event)
 {
     if (event == GLFW_CONNECTED)
     {
-        strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid));
+        // WARNING: If glfwGetJoystickName() is longer than MAX_GAMEPAD_NAME_LENGTH,
+        // we can get a not-NULL terminated string, so, we clean destination and only copy up to -1
+        memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
+        strncpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid), MAX_GAMEPAD_NAME_LENGTH - 1);
     }
     else if (event == GLFW_DISCONNECTED)
     {
-        memset(CORE.Input.Gamepad.name[jid], 0, 64);
+        memset(CORE.Input.Gamepad.name[jid], 0, MAX_GAMEPAD_NAME_LENGTH);
     }
 }
 
+#ifdef _WIN32
+#   define WIN32_CLIPBOARD_IMPLEMENTATION
+#   include "../external/win32_clipboard.h"
+#endif
 // EOF
diff --git a/raylib/src/platforms/rcore_desktop_rgfw.c b/raylib/src/platforms/rcore_desktop_rgfw.c
--- a/raylib/src/platforms/rcore_desktop_rgfw.c
+++ b/raylib/src/platforms/rcore_desktop_rgfw.c
@@ -6,15 +6,14 @@
 *       - Windows (Win32, Win64)
 *       - Linux (X11/Wayland desktop mode)
 *       - MacOS (Cocoa)
+*       - HTML5 (Emscripten)
+*       - Others (untested)
 *
 *   LIMITATIONS:
 *       - TODO
 *
 *   POSSIBLE IMPROVEMENTS:
-*       - TODO
-*
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
+*       - TBD
 *
 *   CONFIGURATION:
 *       #define RCORE_PLATFORM_RGFW
@@ -27,7 +26,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5), Colleague Riley and contributors
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5), Colleague Riley and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -46,7 +45,11 @@
 *
 **********************************************************************************************/
 
-#if defined(GRAPHICS_API_OPENGL_ES2)
+#if defined(PLATFORM_WEB_RGFW)
+#define RGFW_NO_GL_HEADER
+#endif
+
+#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(PLATFORM_WEB_RGFW)
     #define RGFW_OPENGL_ES2
 #endif
 
@@ -63,27 +66,44 @@
 
 #define RGFW_IMPLEMENTATION
 
-#if defined(__WIN32) || defined(__WIN64)
+#if defined(_WIN32) || defined(_WIN64)
     #define WIN32_LEAN_AND_MEAN
     #define Rectangle rectangle_win32
     #define CloseWindow CloseWindow_win32
     #define ShowCursor __imp_ShowCursor
     #define _APISETSTRING_
+
+    #undef MAX_PATH
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
     __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
+#if defined(__cplusplus)
+}
 #endif
 
+#endif
+
 #if defined(__APPLE__)
     #define Point NSPOINT
     #define Size NSSIZE
 #endif
 
+#define RGFW_ALLOC RL_MALLOC
+#define RGFW_FREE RL_FREE
+#define RGFW_CALLOC RL_CALLOC
+
 #include "../external/RGFW.h"
 
-#if defined(__WIN32) || defined(__WIN64)
+#if defined(_WIN32) || defined(_WIN64)
     #undef DrawText
     #undef ShowCursor
     #undef CloseWindow
     #undef Rectangle
+
+    #undef MAX_PATH
+    #define MAX_PATH 1025
 #endif
 
 #if defined(__APPLE__)
@@ -99,6 +119,7 @@
 //----------------------------------------------------------------------------------
 typedef struct {
     RGFW_window *window;                // Native display device (physical screen connection)
+    RGFW_monitor mon;
 } PlatformData;
 
 //----------------------------------------------------------------------------------
@@ -106,18 +127,19 @@
 //----------------------------------------------------------------------------------
 extern CoreData CORE;                   // Global CORE state context
 
-static PlatformData platform = { NULL }; // Platform specific
+static PlatformData platform = { 0 };   // Platform specific
 
 static bool RGFW_disableCursor = false;
 
 static const unsigned short keyMappingRGFW[] = {
-    [RGFW_KEY_NULL] = KEY_NULL,
-    [RGFW_Quote] = KEY_APOSTROPHE,
-    [RGFW_Comma] = KEY_COMMA,
-    [RGFW_Minus] = KEY_MINUS,
-    [RGFW_Period] = KEY_PERIOD,
-    [RGFW_Slash] = KEY_SLASH,
-    [RGFW_Escape] = KEY_ESCAPE,
+    [RGFW_keyNULL] = KEY_NULL,
+    [RGFW_return] = KEY_ENTER,
+    [RGFW_apostrophe] = KEY_APOSTROPHE,
+    [RGFW_comma] = KEY_COMMA,
+    [RGFW_minus] = KEY_MINUS,
+    [RGFW_period] = KEY_PERIOD,
+    [RGFW_slash] = KEY_SLASH,
+    [RGFW_escape] = KEY_ESCAPE,
     [RGFW_F1] = KEY_F1,
     [RGFW_F2] = KEY_F2,
     [RGFW_F3] = KEY_F3,
@@ -130,7 +152,7 @@
     [RGFW_F10] = KEY_F10,
     [RGFW_F11] = KEY_F11,
     [RGFW_F12] = KEY_F12,
-    [RGFW_Backtick] = KEY_GRAVE,
+    [RGFW_backtick] = KEY_GRAVE,
     [RGFW_0] = KEY_ZERO,
     [RGFW_1] = KEY_ONE,
     [RGFW_2] = KEY_TWO,
@@ -141,20 +163,21 @@
     [RGFW_7] = KEY_SEVEN,
     [RGFW_8] = KEY_EIGHT,
     [RGFW_9] = KEY_NINE,
-    [RGFW_Equals] = KEY_EQUAL,
-    [RGFW_BackSpace] = KEY_BACKSPACE,
-    [RGFW_Tab] = KEY_TAB,
-    [RGFW_CapsLock] = KEY_CAPS_LOCK,
-    [RGFW_ShiftL] = KEY_LEFT_SHIFT,
-    [RGFW_ControlL] = KEY_LEFT_CONTROL,
-    [RGFW_AltL] = KEY_LEFT_ALT,
-    [RGFW_SuperL] = KEY_LEFT_SUPER,
+    [RGFW_equals] = KEY_EQUAL,
+    [RGFW_backSpace] = KEY_BACKSPACE,
+    [RGFW_tab] = KEY_TAB,
+    [RGFW_capsLock] = KEY_CAPS_LOCK,
+    [RGFW_shiftL] = KEY_LEFT_SHIFT,
+    [RGFW_controlL] = KEY_LEFT_CONTROL,
+    [RGFW_altL] = KEY_LEFT_ALT,
+    [RGFW_superL] = KEY_LEFT_SUPER,
     #ifndef RGFW_MACOS
-    [RGFW_ShiftR] = KEY_RIGHT_SHIFT,
-
-    [RGFW_AltR] = KEY_RIGHT_ALT,
+    [RGFW_shiftR] = KEY_RIGHT_SHIFT,
+    [RGFW_controlR] = KEY_RIGHT_CONTROL,
+    [RGFW_altR] = KEY_RIGHT_ALT,
+    [RGFW_superR] = KEY_RIGHT_SUPER,
     #endif
-    [RGFW_Space] = KEY_SPACE,
+    [RGFW_space] = KEY_SPACE,
 
     [RGFW_a] = KEY_A,
     [RGFW_b] = KEY_B,
@@ -182,23 +205,23 @@
     [RGFW_x] = KEY_X,
     [RGFW_y] = KEY_Y,
     [RGFW_z] = KEY_Z,
-    [RGFW_Bracket] = KEY_LEFT_BRACKET,
-    [RGFW_BackSlash] = KEY_BACKSLASH,
-    [RGFW_CloseBracket] = KEY_RIGHT_BRACKET,
-    [RGFW_Semicolon] = KEY_SEMICOLON,
-    [RGFW_Insert] = KEY_INSERT,
-    [RGFW_Home] = KEY_HOME,
-    [RGFW_PageUp] = KEY_PAGE_UP,
-    [RGFW_Delete] = KEY_DELETE,
-    [RGFW_End] = KEY_END,
-    [RGFW_PageDown] = KEY_PAGE_DOWN,
-    [RGFW_Right] = KEY_RIGHT,
-    [RGFW_Left] = KEY_LEFT,
-    [RGFW_Down] = KEY_DOWN,
-    [RGFW_Up] = KEY_UP,
-    [RGFW_Numlock] = KEY_NUM_LOCK,
+    [RGFW_bracket] = KEY_LEFT_BRACKET,
+    [RGFW_backSlash] = KEY_BACKSLASH,
+    [RGFW_closeBracket] = KEY_RIGHT_BRACKET,
+    [RGFW_semicolon] = KEY_SEMICOLON,
+    [RGFW_insert] = KEY_INSERT,
+    [RGFW_home] = KEY_HOME,
+    [RGFW_pageUp] = KEY_PAGE_UP,
+    [RGFW_delete] = KEY_DELETE,
+    [RGFW_end] = KEY_END,
+    [RGFW_pageDown] = KEY_PAGE_DOWN,
+    [RGFW_right] = KEY_RIGHT,
+    [RGFW_left] = KEY_LEFT,
+    [RGFW_down] = KEY_DOWN,
+    [RGFW_up] = KEY_UP,
+    [RGFW_numLock] = KEY_NUM_LOCK,
     [RGFW_KP_Slash] = KEY_KP_DIVIDE,
-    [RGFW_Multiply] = KEY_KP_MULTIPLY,
+    [RGFW_multiply] = KEY_KP_MULTIPLY,
     [RGFW_KP_Minus] = KEY_KP_SUBTRACT,
     [RGFW_KP_Return] = KEY_KP_ENTER,
     [RGFW_KP_1] = KEY_KP_1,
@@ -211,15 +234,38 @@
     [RGFW_KP_8] = KEY_KP_8,
     [RGFW_KP_9] = KEY_KP_9,
     [RGFW_KP_0] = KEY_KP_0,
-    [RGFW_KP_Period] = KEY_KP_DECIMAL
+    [RGFW_KP_Period] = KEY_KP_DECIMAL,
+    [RGFW_scrollLock] = KEY_SCROLL_LOCK,
 };
 
+static int RGFW_gpConvTable[18] = {
+    [RGFW_gamepadY] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
+    [RGFW_gamepadB] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
+    [RGFW_gamepadA] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
+    [RGFW_gamepadX] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
+    [RGFW_gamepadL1] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
+    [RGFW_gamepadR1] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
+    [RGFW_gamepadL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
+    [RGFW_gamepadR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
+    [RGFW_gamepadSelect] = GAMEPAD_BUTTON_MIDDLE_LEFT,
+    [RGFW_gamepadHome] = GAMEPAD_BUTTON_MIDDLE,
+    [RGFW_gamepadStart] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
+    [RGFW_gamepadUp] = GAMEPAD_BUTTON_LEFT_FACE_UP,
+    [RGFW_gamepadRight] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
+    [RGFW_gamepadDown] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
+    [RGFW_gamepadLeft] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
+    [RGFW_gamepadL3] = GAMEPAD_BUTTON_LEFT_THUMB,
+    [RGFW_gamepadR3] = GAMEPAD_BUTTON_RIGHT_THUMB,
+};
+
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
 bool InitGraphicsDevice(void);   // Initialize graphics device
 
+static KeyboardKey ConvertScancodeToKey(u32 keycode);
+
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
@@ -240,18 +286,65 @@
 
 // Toggle fullscreen mode
 void ToggleFullscreen(void)
-{   
-    RGFW_window_maximize(platform.window);
-    ToggleBorderlessWindowed();
+{
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+    {
+        // Store previous window position (in case we exit fullscreen)
+        CORE.Window.previousPosition = CORE.Window.position;
+        CORE.Window.previousScreen = CORE.Window.screen;
+
+        platform.mon = RGFW_window_getMonitor(platform.window);
+        FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+
+        RGFW_monitor_scaleToWindow(platform.mon, platform.window);
+        RGFW_window_setFullscreen(platform.window, 1);
+    }
+    else
+    {
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+
+        if (platform.mon.mode.area.w)
+        {
+            RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
+            RGFW_monitor_requestMode(monitor, platform.mon.mode, RGFW_monitorScale);
+
+            platform.mon.mode.area.w = 0;
+        }
+
+        // we update the window position right away
+        CORE.Window.position = CORE.Window.previousPosition;
+        RGFW_window_setFullscreen(platform.window, 0);
+        RGFW_window_move(platform.window, RGFW_POINT(CORE.Window.position.x, CORE.Window.position.y));
+        RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
+    }
+
+    // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS)
+    // NOTE: V-Sync can be enabled by graphic driver configuration
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
 }
 
 // Toggle borderless windowed mode
 void ToggleBorderlessWindowed(void)
 {
-    if (platform.window != NULL)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) ToggleFullscreen();
+
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
-        RGFW_window_setBorder(platform.window, CORE.Window.flags & FLAG_WINDOW_UNDECORATED);
+        CORE.Window.previousPosition = CORE.Window.position;
+        CORE.Window.previousScreen = CORE.Window.screen;
+
+        RGFW_window_setBorder(platform.window, 0);
+
+        RGFW_monitor mon = RGFW_window_getMonitor(platform.window);
+        RGFW_window_resize(platform.window, mon.mode.area);
     }
+    else
+    {
+        RGFW_window_setBorder(platform.window, 1);
+
+        CORE.Window.position = CORE.Window.previousPosition;
+        RGFW_window_resize(platform.window, RGFW_AREA(CORE.Window.previousScreen.width, CORE.Window.previousScreen.height));
+    }
 }
 
 // Set window state: maximized, if resizable
@@ -266,220 +359,207 @@
     RGFW_window_minimize(platform.window);
 }
 
-// Set window state: not minimized/maximized
+// Restore window from being minimized/maximized
 void RestoreWindow(void)
 {
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+
     RGFW_window_restore(platform.window);
 }
 
 // Set window configuration state using flags
 void SetWindowState(unsigned int flags)
 {
-    CORE.Window.flags |= flags;
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
 
-    if (flags & FLAG_VSYNC_HINT)
+    FLAG_SET(CORE.Window.flags, flags);
+
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
     {
         RGFW_window_swapInterval(platform.window, 1);
     }
-    if (flags & FLAG_FULLSCREEN_MODE)
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
     {
-        RGFW_window_maximize(platform.window);
-        ToggleBorderlessWindowed();
+        ToggleFullscreen();
     }
-    if (flags & FLAG_WINDOW_RESIZABLE)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
     {
-        RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
-        RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
+        RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
+        RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
     }
-    if (flags & FLAG_WINDOW_UNDECORATED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
     {
-        ToggleBorderlessWindowed();
+        RGFW_window_setBorder(platform.window, 0);
     }
-    if (flags & FLAG_WINDOW_HIDDEN)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
     {
         RGFW_window_hide(platform.window);
     }
-    if (flags & FLAG_WINDOW_MINIMIZED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
     {
         RGFW_window_minimize(platform.window);
     }
-    if (flags & FLAG_WINDOW_MAXIMIZED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
     {
         RGFW_window_maximize(platform.window);
     }
-    if (flags & FLAG_WINDOW_UNFOCUSED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW");
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
+        FLAG_CLEAR(platform.window->_flags, RGFW_windowFocusOnShow);
+        RGFW_window_setFlags(platform.window, platform.window->_flags);
     }
-    if (flags & FLAG_WINDOW_TOPMOST)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW");
+        RGFW_window_setFloating(platform.window, RGFW_TRUE);
     }
-    if (flags & FLAG_WINDOW_ALWAYS_RUN)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW");
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
     }
-    if (flags & FLAG_WINDOW_TRANSPARENT)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT post window creation post window creation is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
     }
-    if (flags & FLAG_WINDOW_HIGHDPI)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
     }
-    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
     {
-        RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH);
+        RGFW_window_setMousePassthrough(platform.window, 1);
     }
-    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
         ToggleBorderlessWindowed();
     }
-    if (flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
     {
-        RGFW_setGLSamples(4);
+        RGFW_setGLHint(RGFW_glSamples, 4);
     }
-    if (flags & FLAG_INTERLACED_HINT)
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
     {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
     }
 }
 
 // Clear window configuration state flags
 void ClearWindowState(unsigned int flags)
 {
-    CORE.Window.flags &= ~flags;
+    FLAG_CLEAR(CORE.Window.flags, flags);
 
-    if (flags & FLAG_VSYNC_HINT)
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
     {
         RGFW_window_swapInterval(platform.window, 0);
     }
-    if (flags & FLAG_FULLSCREEN_MODE)
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
     {
-        ToggleBorderlessWindowed();
-        RGFW_window_restore(platform.window);
-        CORE.Window.fullscreen = false;
+        ToggleFullscreen();
     }
-    if (flags & FLAG_WINDOW_RESIZABLE)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
     {
-        RGFW_window_setMaxSize(platform.window, RGFW_AREA(0, 0));
-        RGFW_window_setMinSize(platform.window, RGFW_AREA(0, 0));
+        RGFW_window_setMaxSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
+        RGFW_window_setMinSize(platform.window, RGFW_AREA(platform.window->r.w, platform.window->r.h));
     }
-    if (flags & FLAG_WINDOW_UNDECORATED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
     {
-        ToggleBorderlessWindowed();
+        RGFW_window_setBorder(platform.window, 1);
     }
-    if (flags & FLAG_WINDOW_HIDDEN)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
     {
+        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+
         RGFW_window_show(platform.window);
     }
-    if (flags & FLAG_WINDOW_MINIMIZED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
     {
+        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+
         RGFW_window_restore(platform.window);
     }
-    if (flags & FLAG_WINDOW_MAXIMIZED)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
     {
+        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) RGFW_window_focus(platform.window);
+
         RGFW_window_restore(platform.window);
     }
-    if (flags & FLAG_WINDOW_UNFOCUSED)
-    {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_RGFW");
-    }
-    if (flags & FLAG_WINDOW_TOPMOST)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
     {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TOPMOST is not supported on PLATFORM_DESKTOP_RGFW");
+        RGFW_window_setFlags(platform.window, platform.window->_flags | RGFW_windowFocusOnShow);
     }
-    if (flags & FLAG_WINDOW_ALWAYS_RUN)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
     {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_RGFW");
+        RGFW_window_setFloating(platform.window, RGFW_FALSE);
     }
-    if (flags & FLAG_WINDOW_TRANSPARENT)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
     {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization");
     }
-    if (flags & FLAG_WINDOW_HIGHDPI)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
     {
-        // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization");
     }
-    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
     {
-        RGFW_window_setMousePassthrough(platform.window, flags & FLAG_WINDOW_MOUSE_PASSTHROUGH);
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_RGFW");
+        RGFW_window_setMousePassthrough(platform.window, 0);
     }
-    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
-        ToggleFullscreen();
+        ToggleBorderlessWindowed();
     }
-    if (flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
     {
-        RGFW_setGLSamples(0);
+        RGFW_setGLHint(RGFW_glSamples, 0);
     }
-    if (flags & FLAG_INTERLACED_HINT)
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
     {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_RGFW");
+        TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization");
     }
 }
 
 // Set icon for window
 void SetWindowIcon(Image image)
 {
-    i32 channels = 4;
-
-    switch (image.format)
+    if (image.format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
     {
-        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
-        case PIXELFORMAT_UNCOMPRESSED_R16:           // 16 bpp (1 channel - half float)
-        case PIXELFORMAT_UNCOMPRESSED_R32:           // 32 bpp (1 channel - float)
-        {
-            channels = 1;
-        } break;
-        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:    // 8*2 bpp (2 channels)
-        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:        // 16 bpp
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8:        // 24 bpp
-        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:      // 16 bpp (1 bit alpha)
-        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:      // 16 bpp (4 bit alpha)
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:      // 32 bpp
-        {
-            channels = 2;
-        } break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32:     // 32*3 bpp (3 channels - float)
-        case PIXELFORMAT_UNCOMPRESSED_R16G16B16:     // 16*3 bpp (3 channels - half float)
-        case PIXELFORMAT_COMPRESSED_DXT1_RGB:        // 4 bpp (no alpha)
-        case PIXELFORMAT_COMPRESSED_ETC1_RGB:        // 4 bpp
-        case PIXELFORMAT_COMPRESSED_ETC2_RGB:        // 4 bpp
-        case PIXELFORMAT_COMPRESSED_PVRT_RGB:        // 4 bpp
-        {
-            channels = 3;
-        } break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:  // 32*4 bpp (4 channels - float)
-        case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:  // 16*4 bpp (4 channels - half float)
-        case PIXELFORMAT_COMPRESSED_DXT1_RGBA:       // 4 bpp (1 bit alpha)
-        case PIXELFORMAT_COMPRESSED_DXT3_RGBA:       // 8 bpp
-        case PIXELFORMAT_COMPRESSED_DXT5_RGBA:       // 8 bpp
-        case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:   // 8 bpp
-        case PIXELFORMAT_COMPRESSED_PVRT_RGBA:       // 4 bpp
-        case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA:   // 8 bpp
-        case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA:   // 2 bpp
-        {
-            channels = 4;
-        } break;
-        default: break;
+        TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format");
+        return;
     }
-
-    RGFW_window_setIcon(platform.window, image.data, RGFW_AREA(image.width, image.height), channels);
+    RGFW_window_setIcon(platform.window, (u8 *)image.data, RGFW_AREA(image.width, image.height), 4);
 }
 
 // Set icon for window
 void SetWindowIcons(Image *images, int count)
 {
-    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
+    if ((images == NULL) || (count <= 0))
+    {
+        RGFW_window_setIcon(platform.window, NULL, RGFW_AREA(0, 0), 0);
+    }
+    else
+    {
+        Image *bigIcon = NULL;
+        Image *smallIcon = NULL;
+
+        for (int i = 0; i < count; i++)
+        {
+            if (images[i].format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)
+            {
+                TRACELOG(LOG_WARNING, "RGFW: Window icon image must be in R8G8B8A8 pixel format");
+                continue;
+            }
+            if ((bigIcon == NULL) || ((images[i].width > bigIcon->width) && (images[i].height > bigIcon->height))) bigIcon = &images[i];
+            if ((smallIcon == NULL) || ((images[i].width < smallIcon->width) && (images[i].height > smallIcon->height))) smallIcon = &images[i];
+        }
+
+        if (smallIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)smallIcon->data, RGFW_AREA(smallIcon->width, smallIcon->height), 4, RGFW_iconWindow);
+        if (bigIcon != NULL) RGFW_window_setIconEx(platform.window, (u8 *)bigIcon->data, RGFW_AREA(bigIcon->width, bigIcon->height), 4, RGFW_iconTaskbar);
+    }
 }
 
 // Set title for window
 void SetWindowTitle(const char *title)
 {
-    RGFW_window_setName(platform.window, (char*)title);
+    RGFW_window_setName(platform.window, (char *)title);
     CORE.Window.title = title;
 }
 
@@ -492,7 +572,7 @@
 // Set monitor for the current window
 void SetWindowMonitor(int monitor)
 {
-    RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors()[monitor]);
+    RGFW_window_moveToMonitor(platform.window, RGFW_getMonitors(NULL)[monitor]);
 }
 
 // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
@@ -514,28 +594,32 @@
 // Set window dimensions
 void SetWindowSize(int width, int height)
 {
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+
     RGFW_window_resize(platform.window, RGFW_AREA(width, height));
 }
 
 // Set window opacity, value opacity is between 0.0 and 1.0
 void SetWindowOpacity(float opacity)
 {
-    TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
+    RGFW_window_setOpacity(platform.window, opacity);
 }
 
 // Set window focused
 void SetWindowFocused(void)
 {
-    RGFW_window_show(platform.window);
+    RGFW_window_focus(platform.window);
 }
 
 // Get native window handle
 void *GetWindowHandle(void)
 {
-#ifdef RGFW_WEBASM
-    return (void*)platform.window->src.ctx;
+    if (platform.window == NULL) return NULL;
+#ifdef RGFW_WASM
+    return (void *)platform.window->src.ctx;
 #else
-    return (void*)platform.window->src.window;
+    return (void *)platform.window->src.window;
 #endif
 }
 
@@ -545,11 +629,11 @@
     #define MAX_MONITORS_SUPPORTED 6
 
     int count = MAX_MONITORS_SUPPORTED;
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
     for (int i = 0; i < 6; i++)
     {
-        if (!mons[i].rect.x && !mons[i].rect.y && !mons[i].rect.w && mons[i].rect.h)
+        if (!mons[i].x && !mons[i].y && !mons[i].mode.area.w && mons[i].mode.area.h)
         {
             count = i;
             break;
@@ -559,15 +643,18 @@
     return count;
 }
 
-// Get number of monitors
+// Get current monitor where window is placed
 int GetCurrentMonitor(void)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
-    RGFW_monitor mon = RGFW_window_getMonitor(platform.window);
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
+    RGFW_monitor mon = { 0 };
 
+    if (platform.window) mon = RGFW_window_getMonitor(platform.window);
+    else mon = RGFW_getPrimaryMonitor();
+
     for (int i = 0; i < 6; i++)
     {
-        if ((mons[i].rect.x ==  mon.rect.x) && (mons[i].rect.y ==  mon.rect.y)) return i;
+        if ((mons[i].x ==  mon.x) && (mons[i].y ==  mon.y)) return i;
     }
 
     return 0;
@@ -576,31 +663,31 @@
 // Get selected monitor position
 Vector2 GetMonitorPosition(int monitor)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
-    return (Vector2){mons[monitor].rect.x, mons[monitor].rect.y};
+    return (Vector2){ (float)mons[monitor].x, (float)mons[monitor].y };
 }
 
 // Get selected monitor width (currently used by monitor)
 int GetMonitorWidth(int monitor)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
-    return mons[monitor].rect.w;
+    return mons[monitor].mode.area.w;
 }
 
 // Get selected monitor height (currently used by monitor)
 int GetMonitorHeight(int monitor)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
-    return mons[monitor].rect.h;
+    return mons[monitor].mode.area.h;
 }
 
 // Get selected monitor physical width in millimetres
 int GetMonitorPhysicalWidth(int monitor)
 {
-    RGFW_monitor* mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
     return mons[monitor].physW;
 }
@@ -608,22 +695,23 @@
 // Get selected monitor physical height in millimetres
 int GetMonitorPhysicalHeight(int monitor)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
-    return mons[monitor].physH;
+    return (int)mons[monitor].physH;
 }
 
 // Get selected monitor refresh rate
 int GetMonitorRefreshRate(int monitor)
 {
-    TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
-    return 0;
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
+
+    return (int)mons[monitor].mode.refreshRate;
 }
 
 // Get the human-readable, UTF-8 encoded name of the selected monitor
 const char *GetMonitorName(int monitor)
 {
-    RGFW_monitor *mons = RGFW_getMonitors();
+    RGFW_monitor *mons = RGFW_getMonitors(NULL);
 
     return mons[monitor].name;
 }
@@ -631,15 +719,19 @@
 // Get window position XY on monitor
 Vector2 GetWindowPosition(void)
 {
-    return (Vector2){ platform.window->r.x, platform.window->r.y };
+    if (platform.window == NULL) return (Vector2){ 0.0f, 0.0f };
+    return (Vector2){ (float)platform.window->r.x, (float)platform.window->r.y };
 }
 
 // Get window scale DPI factor for current monitor
 Vector2 GetWindowScaleDPI(void)
 {
-    RGFW_monitor monitor = RGFW_window_getMonitor(platform.window);
+    RGFW_monitor monitor = { 0 };
 
-    return (Vector2){monitor.scaleX, monitor.scaleX};
+    if (platform.window) monitor = RGFW_window_getMonitor(platform.window);
+    else monitor = RGFW_getPrimaryMonitor();
+
+    return (Vector2){ monitor.scaleX, monitor.scaleX };
 }
 
 // Set clipboard text content
@@ -649,12 +741,45 @@
 }
 
 // Get clipboard text content
-// NOTE: returned string is allocated and freed by GLFW
+// NOTE: returned string is allocated and freed by RGFW
 const char *GetClipboardText(void)
 {
     return RGFW_readClipboard(NULL);
 }
 
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if defined(_WIN32)
+    #define WIN32_CLIPBOARD_IMPLEMENTATION
+    #define WINUSER_ALREADY_INCLUDED
+    #define WINBASE_ALREADY_INCLUDED
+    #define WINGDI_ALREADY_INCLUDED
+    #include "../external/win32_clipboard.h"
+#endif
+#endif
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+    unsigned long long int dataSize = 0;
+    void *fileData = NULL;
+
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+#if defined(_WIN32)
+    int width = 0;
+    int height = 0;
+    fileData  = (void *)Win32GetClipboardImageData(&width, &height, &dataSize);
+
+    if (fileData == NULL) TRACELOG(LOG_WARNING, "Clipboard image: Couldn't get clipboard data");
+    else image = LoadImageFromMemory(".bmp", (const unsigned char *)fileData, dataSize);
+#else
+    TRACELOG(LOG_WARNING, "Clipboard image: PLATFORM_DESKTOP_RGFW doesn't implement GetClipboardImage() for this OS");
+#endif
+#endif // SUPPORT_CLIPBOARD_IMAGE
+
+    return image;
+}
+
 // Show mouse cursor
 void ShowCursor(void)
 {
@@ -677,18 +802,19 @@
 
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
-    RGFW_window_showMouse(platform.window, true);
-    CORE.Input.Mouse.cursorHidden = false;
+    ShowCursor();
+
+    CORE.Input.Mouse.cursorLocked = true;
 }
 
 // Disables cursor (lock cursor)
 void DisableCursor(void)
 {
     RGFW_disableCursor = true;
-
     RGFW_window_mouseHold(platform.window, RGFW_AREA(0, 0));
-
     HideCursor();
+
+    CORE.Input.Mouse.cursorLocked = true;
 }
 
 // Swap back buffer with front buffer (screen drawing)
@@ -704,25 +830,31 @@
 // Get elapsed time measure in seconds since InitTimer()
 double GetTime(void)
 {
-    double time = 0.0;
-    unsigned long long int nanoSeconds = RGFW_getTimeNS();
-    time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
-
-    return time;
+    return RGFW_getTime();
 }
 
 // Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
 void OpenURL(const char *url)
 {
     // Security check to (partially) avoid malicious code on target platform
     if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
     else
     {
-        // TODO: Open URL implementation
+        char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+#if defined(_WIN32)
+        sprintf(cmd, "explorer \"%s\"", url);
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+        sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser
+#endif
+#if defined(__APPLE__)
+        sprintf(cmd, "open '%s'", url);
+#endif
+        int result = system(cmd);
+        if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+        RL_FREE(cmd);
     }
 }
 
@@ -733,10 +865,16 @@
 // Set internal gamepad mappings
 int SetGamepadMappings(const char *mappings)
 {
-    TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
+    TRACELOG(LOG_WARNING, "SetGamepadMappings() unsupported on target platform");
     return 0;
 }
 
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() unsupported on target platform");
+}
+
 // Set mouse position XY
 void SetMousePosition(int x, int y)
 {
@@ -751,65 +889,12 @@
     RGFW_window_setMouseStandard(platform.window, cursor);
 }
 
-// Get physical key name.
+// Get physical key name
 const char *GetKeyName(int key)
 {
-    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
-    return "";
-}
-
-static KeyboardKey ConvertScancodeToKey(u32 keycode);
-
-// TODO: Review function to avoid duplicate with RSGL
-char RSGL_keystrToChar(const char *str)
-{
-    if (str[1] == 0) return str[0];
-
-    static const char *map[] = {
-        "asciitilde", "`",
-        "grave", "~",
-        "exclam", "!",
-        "at", "@",
-        "numbersign", "#",
-        "dollar", "$",
-        "percent", "%%",
-        "asciicircum", "^",
-        "ampersand", "&",
-        "asterisk", "*",
-        "parenleft", "(",
-        "parenright", ")",
-        "underscore", "_",
-        "minus", "-",
-        "plus", "+",
-        "equal", "=",
-        "braceleft", "{",
-        "bracketleft", "[",
-        "bracketright", "]",
-        "braceright", "}",
-        "colon", ":",
-        "semicolon", ";",
-        "quotedbl", "\"",
-        "apostrophe", "'",
-        "bar", "|",
-        "backslash", "\'",
-        "less", "<",
-        "comma", ",",
-        "greater", ">",
-        "period", ".",
-        "question", "?",
-        "slash", "/",
-        "space", " ",
-        "Return", "\n",
-        "Enter", "\n",
-        "enter", "\n",
-    };
-
-    for (unsigned char i = 0; i < (sizeof(map)/sizeof(char *)); i += 2)
-    {
-        if (strcmp(map[i], str) == 0) return *map[i + 1];
-    }
+    TRACELOG(LOG_WARNING, "GetKeyName() unsupported on target platform");
 
-    return '\0';
+    return "";
 }
 
 // Register all input events
@@ -832,7 +917,6 @@
     // Register previous mouse position
 
     // Reset last gamepad button/axis registered state
-
     for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
     {
         // Check if gamepad is available
@@ -871,8 +955,7 @@
     CORE.Window.resizedLastFrame = false;
 
     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
-    #define RGFW_HOLD_MOUSE     (1L<<2)
-    if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
+    if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
     {
         CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
         CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
@@ -882,27 +965,26 @@
         CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
     }
 
-    while (RGFW_window_checkEvent(platform.window))
+    if ((CORE.Window.eventWaiting) || (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)))
     {
-        if ((platform.window->event.type >= RGFW_jsButtonPressed) && (platform.window->event.type <= RGFW_jsAxisMove))
-        {
-            if (!CORE.Input.Gamepad.ready[platform.window->event.joystick])
-            {
-                CORE.Input.Gamepad.ready[platform.window->event.joystick] = true;
-                CORE.Input.Gamepad.axisCount[platform.window->event.joystick] = platform.window->event.axisesCount;
-                CORE.Input.Gamepad.name[platform.window->event.joystick][0] = '\0';
-                CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
-                CORE.Input.Gamepad.axisState[platform.window->event.joystick][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
-            }
-        }
-
-        RGFW_Event *event = &platform.window->event;
+        RGFW_window_eventWait(platform.window, -1); // Wait for input events: keyboard/mouse/window events (callbacks) -> Update keys state
+        CORE.Time.previous = GetTime();
+    }
 
+    while (RGFW_window_checkEvent(platform.window))
+    {
+        RGFW_event *event = &platform.window->event;
         // All input events can be processed after polling
+
         switch (event->type)
         {
-            case RGFW_quit: CORE.Window.shouldClose = true; break;
-            case RGFW_dnd:      // Dropped file
+            case RGFW_mouseEnter: CORE.Input.Mouse.cursorOnScreen = true; break;
+            case RGFW_mouseLeave: CORE.Input.Mouse.cursorOnScreen = false; break;
+            case RGFW_quit:
+                event->type = 0;
+                CORE.Window.shouldClose = true;
+                return;
+            case RGFW_DND:      // Dropped file
             {
                 for (int i = 0; i < event->droppedFilesCount; i++)
                 {
@@ -933,12 +1015,38 @@
             case RGFW_windowResized:
             {
                 SetupViewport(platform.window->r.w, platform.window->r.h);
-                CORE.Window.screen.width = platform.window->r.w;
-                CORE.Window.screen.height =  platform.window->r.h;
+
+                // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
+                if (IsWindowState(FLAG_WINDOW_HIGHDPI))
+                {
+                    CORE.Window.screen.width = (int)(platform.window->r.w/GetWindowScaleDPI().x);
+                    CORE.Window.screen.height = (int)(platform.window->r.h/GetWindowScaleDPI().y);
+                }
+                else
+                {
+                    CORE.Window.screen.width = platform.window->r.w;
+                    CORE.Window.screen.height = platform.window->r.h;
+                }
+
                 CORE.Window.currentFbo.width = platform.window->r.w;
                 CORE.Window.currentFbo.height = platform.window->r.h;
                 CORE.Window.resizedLastFrame = true;
             } break;
+            case RGFW_windowMaximized:
+            {
+                FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);  // The window was maximized
+            } break;
+            case RGFW_windowMinimized:
+            {
+                FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);  // The window was iconified
+            } break;
+            case RGFW_windowRestored:
+            {
+                if (RGFW_window_isMaximized(platform.window))
+                    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);           // The window was restored
+                if (RGFW_window_isMinimized(platform.window))
+                    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);           // The window was restored
+            } break;
             case RGFW_windowMoved:
             {
                 CORE.Window.position.x = platform.window->r.x;
@@ -948,8 +1056,7 @@
             // Keyboard events
             case RGFW_keyPressed:
             {
-                KeyboardKey key = ConvertScancodeToKey(event->keyCode);
-
+                KeyboardKey key = ConvertScancodeToKey(event->key);
                 if (key != KEY_NULL)
                 {
                     // If key was up, add it to the key pressed queue
@@ -962,24 +1069,20 @@
                     CORE.Input.Keyboard.currentKeyState[key] = 1;
                 }
 
-                // TODO: Put exitKey verification outside the switch?
-                if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
-                {
-                    CORE.Window.shouldClose = true;
-                }
+                if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
 
                 // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
                 // Check if there is space available in the queue
                 if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
                 {
                     // Add character (codepoint) to the queue
-                    CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = RSGL_keystrToChar(event->keyName);
+                    CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = event->keyChar;
                     CORE.Input.Keyboard.charPressedQueueCount++;
                 }
             } break;
             case RGFW_keyReleased:
             {
-                KeyboardKey key = ConvertScancodeToKey(event->keyCode);
+                KeyboardKey key = ConvertScancodeToKey(event->key);
                 if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
             } break;
 
@@ -991,6 +1094,7 @@
                     CORE.Input.Mouse.currentWheelMove.y = event->scroll;
                     break;
                 }
+                else CORE.Input.Mouse.currentWheelMove.y = 0;
 
                 int btn = event->button;
                 if (btn == RGFW_mouseLeft) btn = 1;
@@ -1004,12 +1108,12 @@
             } break;
             case RGFW_mouseButtonReleased:
             {
-
                 if ((event->button == RGFW_mouseScrollUp) || (event->button == RGFW_mouseScrollDown))
                 {
                     CORE.Input.Mouse.currentWheelMove.y = event->scroll;
                     break;
                 }
+                else CORE.Input.Mouse.currentWheelMove.y = 0;
 
                 int btn = event->button;
                 if (btn == RGFW_mouseLeft) btn = 1;
@@ -1023,10 +1127,10 @@
             } break;
             case RGFW_mousePosChanged:
             {
-                if (platform.window->_winArgs & RGFW_HOLD_MOUSE)
+                if (FLAG_IS_SET(platform.window->_flags, RGFW_HOLD_MOUSE))
                 {
-                    CORE.Input.Mouse.currentPosition.x += (float)event->point.x;
-                    CORE.Input.Mouse.currentPosition.y += (float)event->point.y;
+                    CORE.Input.Mouse.currentPosition.x += (float)event->vector.x;
+                    CORE.Input.Mouse.currentPosition.y += (float)event->vector.y;
                 }
                 else
                 {
@@ -1038,123 +1142,66 @@
                 CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
                 touchAction = 2;
             } break;
-            case RGFW_jsButtonPressed:
+            case RGFW_gamepadConnected:
             {
-                int button = -1;
-
-                switch (event->button)
-                {
-                    case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
-                    case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
-                    case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
-                    case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
-
-                    case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
-                    case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
-
-                    case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
-                    case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
-
-                    case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
-                    case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break;
-                    case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
-
-                    case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
-                    case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
-                    case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
-                    case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
+                CORE.Input.Gamepad.ready[platform.window->event.gamepad] = true;
+                CORE.Input.Gamepad.axisCount[platform.window->event.gamepad] = platform.window->event.axisesCount;
+                CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+                CORE.Input.Gamepad.axisState[platform.window->event.gamepad][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
 
-                    default: break;
-                }
+                strcpy(CORE.Input.Gamepad.name[platform.window->event.gamepad], RGFW_getGamepadName(platform.window, platform.window->event.gamepad));
+            } break;
+            case RGFW_gamepadDisconnected:
+            {
+                CORE.Input.Gamepad.ready[platform.window->event.gamepad] = false;
+            } break;
+            case RGFW_gamepadButtonPressed:
+            {
+                int button = RGFW_gpConvTable[event->button];
 
                 if (button >= 0)
                 {
-                    CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 1;
+                    CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 1;
                     CORE.Input.Gamepad.lastButtonPressed = button;
                 }
             } break;
-            case RGFW_jsButtonReleased:
+            case RGFW_gamepadButtonReleased:
             {
-                int button = -1;
-                switch (event->button)
-                {
-                    case RGFW_JS_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
-                    case RGFW_JS_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
-                    case RGFW_JS_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
-                    case RGFW_JS_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
-
-                    case RGFW_JS_L1: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
-                    case RGFW_JS_R1: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
-
-                    case RGFW_JS_L2: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
-                    case RGFW_JS_R2: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
-
-                    case RGFW_JS_SELECT: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
-                    case RGFW_JS_HOME: button = GAMEPAD_BUTTON_MIDDLE; break;
-                    case RGFW_JS_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
-
-                    case RGFW_JS_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
-                    case RGFW_JS_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
-                    case RGFW_JS_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
-                    case RGFW_JS_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
-                    default: break;
-                }
+                int button = RGFW_gpConvTable[event->button];
 
-                if (button >= 0)
-                {
-                    CORE.Input.Gamepad.currentButtonState[event->joystick][button] = 0;
-                    if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
-                }
+                CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = 0;
+                if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
             } break;
-            case RGFW_jsAxisMove:
+            case RGFW_gamepadAxisMove:
             {
                 int axis = -1;
-                for (int i = 0; i < event->axisesCount; i++)
+                float value = 0;
+
+                switch(event->whichAxis)
                 {
-                    switch(i)
+                    case 0:
                     {
-                        case 0:
-                        {
-                            if (abs(event->axis[i].x) > abs(event->axis[i].y))
-                            {
-                                axis = GAMEPAD_AXIS_LEFT_X;
-                                break;
-                            }
-
-                            axis = GAMEPAD_AXIS_LEFT_Y;
-                        } break;
-                        case 1:
-                        {
-                            if (abs(event->axis[i].x) > abs(event->axis[i].y))
-                            {
-                                axis = GAMEPAD_AXIS_RIGHT_X;
-                                break;
-                            }
-
-                            axis = GAMEPAD_AXIS_RIGHT_Y;
-                        } break;
-                        case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break;
-                        case 3: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break;
-                        default: break;
-                    }
-
-                    #ifdef __linux__
-                    float value = (event->axis[i].x + event->axis[i].y)/(float)32767;
-                    #else
-                    float value = (event->axis[i].x + -event->axis[i].y)/(float)32767;
-                    #endif
-                    CORE.Input.Gamepad.axisState[event->joystick][axis] = value;
-
-                    // Register button state for triggers in addition to their axes
-                    if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))
+                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_X] = event->axis[0].x/100.0f;
+                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_LEFT_Y] = event->axis[0].y/100.0f;
+                    } break;
+                    case 1:
                     {
+                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_X] = event->axis[1].x/100.0f;
+                        CORE.Input.Gamepad.axisState[event->gamepad][GAMEPAD_AXIS_RIGHT_Y] = event->axis[1].y/100.0f;
+                    } break;
+                    case 2: axis = GAMEPAD_AXIS_LEFT_TRIGGER;
+                    case 3:
+                    {
+                        if (axis == -1) axis = GAMEPAD_AXIS_RIGHT_TRIGGER;
+
                         int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
                         int pressed = (value > 0.1f);
-                        CORE.Input.Gamepad.currentButtonState[event->joystick][button] = pressed;
-                        
+                        CORE.Input.Gamepad.currentButtonState[event->gamepad][button] = pressed;
+
                         if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
                         else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
                     }
+                    default: break;
                 }
             } break;
             default: break;
@@ -1201,56 +1248,67 @@
 int InitPlatform(void)
 {
     // Initialize RGFW internal global state, only required systems
-    unsigned int flags = RGFW_CENTER | RGFW_ALLOW_DND;
+    unsigned int flags = RGFW_windowCenter | RGFW_windowAllowDND;
 
     // Check window creation flags
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
     {
-        CORE.Window.fullscreen = true;
-        flags |= RGFW_FULLSCREEN;
+        FLAG_SET(flags, RGFW_windowFullscreen);
     }
 
-    if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= RGFW_NO_BORDER;
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) flags |= RGFW_NO_RESIZE;
-
-    if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= RGFW_TRANSPARENT_WINDOW;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        FLAG_SET(flags, RGFW_windowedFullscreen);
+    }
 
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) flags |= RGFW_FULLSCREEN;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, RGFW_windowNoBorder);
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, RGFW_windowNoResize);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, RGFW_windowTransparent);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, RGFW_windowFullscreen);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, RGFW_windowHide);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, RGFW_windowMaximize);
 
     // NOTE: Some OpenGL context attributes must be set before window creation
-
     // Check selection OpenGL version
     if (rlGetVersion() == RL_OPENGL_21)
     {
-        RGFW_setGLVersion(RGFW_GL_CORE, 2, 1);
+        RGFW_setGLHint(RGFW_glMajor, 2);
+        RGFW_setGLHint(RGFW_glMinor, 1);
     }
     else if (rlGetVersion() == RL_OPENGL_33)
     {
-        RGFW_setGLVersion(RGFW_GL_CORE, 3, 3);
+        RGFW_setGLHint(RGFW_glMajor, 3);
+        RGFW_setGLHint(RGFW_glMinor, 3);
     }
     else if (rlGetVersion() == RL_OPENGL_43)
     {
-        RGFW_setGLVersion(RGFW_GL_CORE, 4, 1);
+        RGFW_setGLHint(RGFW_glMajor, 4);
+        RGFW_setGLHint(RGFW_glMinor, 3);
     }
 
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
-    {
-        RGFW_setGLSamples(4);
-    }
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) RGFW_setGLHint(RGFW_glSamples, 4);
 
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(flags, RGFW_windowFocusOnShow | RGFW_windowFocus);
+
     platform.window = RGFW_createWindow(CORE.Window.title, RGFW_RECT(0, 0, CORE.Window.screen.width, CORE.Window.screen.height), flags);
+    platform.mon.mode.area.w = 0;
 
+    if (platform.window != NULL)
+    {
+        // NOTE: RGFW's exit key is distinct from raylib's exit key (which can
+        // be set with SetExitKey()) and defaults to Escape
+        platform.window->exitKey = RGFW_keyNULL;
+    }
+
+#ifndef PLATFORM_WEB_RGFW
     RGFW_area screenSize = RGFW_getScreenSize();
     CORE.Window.display.width = screenSize.w;
     CORE.Window.display.height = screenSize.h;
-    /* 
-        I think this is needed by Raylib now ? 
-        If so, rcore_destkop_sdl should be updated too
-    */
-    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
-
-    if (CORE.Window.flags & FLAG_VSYNC_HINT) RGFW_window_swapInterval(platform.window, 1);
-
+#else
+    CORE.Window.display.width = CORE.Window.screen.width;
+    CORE.Window.display.height = CORE.Window.screen.height;
+#endif
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT)) RGFW_window_swapInterval(platform.window, 1);
     RGFW_window_makeCurrent(platform.window);
 
     // Check surface and context activation
@@ -1262,12 +1320,6 @@
         CORE.Window.render.height = CORE.Window.screen.height;
         CORE.Window.currentFbo.width = CORE.Window.render.width;
         CORE.Window.currentFbo.height = CORE.Window.render.height;
-
-        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
-        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
-        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
-        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
-        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
     }
     else
     {
@@ -1293,15 +1345,7 @@
     // Load OpenGL extensions
     // NOTE: GL procedures address loader is required to load extensions
     //----------------------------------------------------------------------------
-    rlLoadExtensions((void*)RGFW_getProcAddress);
-    //----------------------------------------------------------------------------
-
-    // TODO: Initialize input events system
-    // It could imply keyboard, mouse, gamepad, touch...
-    // Depending on the platform libraries/SDK it could use a callback mechanism
-    // For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
-    //----------------------------------------------------------------------------
-    // ...
+    rlLoadExtensions((void *)RGFW_getProcAddress);
     //----------------------------------------------------------------------------
 
     // Initialize timing system
@@ -1314,15 +1358,23 @@
     CORE.Storage.basePath = GetWorkingDirectory();
     //----------------------------------------------------------------------------
 
-#ifdef RGFW_X11
-    for (int i = 0; (i < 4) && (i < MAX_GAMEPADS); i++)
-    {
-        RGFW_registerJoystick(platform.window, i);
-    }
+#if defined(RGFW_WAYLAND)
+    if (RGFW_useWaylandBool) TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Wayland): Initialized successfully");
+    else TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (fallback)): Initialized successfully");
+#elif defined(RGFW_X11)
+    #if defined(__APPLE__)
+        TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11 (MacOS)): Initialized successfully");
+    #else
+        TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - X11): Initialized successfully");
+    #endif
+#elif defined (RGFW_WINDOWS)
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - Win32): Initialized successfully");
+#elif defined(RGFW_WASM)
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - WASMs): Initialized successfully");
+#elif defined(RGFW_MACOS)
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (RGFW - MacOS): Initialized successfully");
 #endif
 
-    TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully");
-
     return 0;
 }
 
@@ -1335,7 +1387,8 @@
 // Keycode mapping
 static KeyboardKey ConvertScancodeToKey(u32 keycode)
 {
-    if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return 0;
+    if (keycode > sizeof(keyMappingRGFW)/sizeof(unsigned short)) return KEY_NULL;
 
-    return keyMappingRGFW[keycode];
+    return (KeyboardKey)keyMappingRGFW[keycode];
 }
+
diff --git a/raylib/src/platforms/rcore_desktop_sdl.c b/raylib/src/platforms/rcore_desktop_sdl.c
--- a/raylib/src/platforms/rcore_desktop_sdl.c
+++ b/raylib/src/platforms/rcore_desktop_sdl.c
@@ -15,1624 +15,2191 @@
 *       - Improvement 01
 *       - Improvement 02
 *
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
-*
-*   CONFIGURATION:
-*       #define RCORE_PLATFORM_CUSTOM_FLAG
-*           Custom flag for rcore on target platform -not used-
-*
-*   DEPENDENCIES:
-*       - SDL 2 (main library): Windowing and inputs management
-*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#include "SDL.h"                // SDL base library (window/rendered, input, timing... functionality)
-
-#if defined(GRAPHICS_API_OPENGL_ES2)
-    // It seems it does not need to be included to work
-    //#include "SDL_opengles2.h"
-#else
-    #include "SDL_opengl.h"     // SDL OpenGL functionality (if required, instead of internal renderer)
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#ifndef MAX_CLIPBOARD_BUFFER_LENGTH
-    #define MAX_CLIPBOARD_BUFFER_LENGTH 1024 // Size of the clipboard buffer used on GetClipboardText()
-#endif
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-typedef struct {
-    SDL_Window *window;
-    SDL_GLContext glContext;
-
-    SDL_GameController *gamepad[MAX_GAMEPADS];
-    SDL_Cursor *cursor;
-    bool cursorRelative;
-} PlatformData;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-extern CoreData CORE;                   // Global CORE state context
-
-static PlatformData platform = { 0 };   // Platform specific data
-
-//----------------------------------------------------------------------------------
-// Local Variables Definition
-//----------------------------------------------------------------------------------
-#define SCANCODE_MAPPED_NUM 232
-static const KeyboardKey mapScancodeToKey[SCANCODE_MAPPED_NUM] = {
-    KEY_NULL,           // SDL_SCANCODE_UNKNOWN
-    0,
-    0,
-    0,
-    KEY_A,              // SDL_SCANCODE_A
-    KEY_B,              // SDL_SCANCODE_B
-    KEY_C,              // SDL_SCANCODE_C
-    KEY_D,              // SDL_SCANCODE_D
-    KEY_E,              // SDL_SCANCODE_E
-    KEY_F,              // SDL_SCANCODE_F
-    KEY_G,              // SDL_SCANCODE_G
-    KEY_H,              // SDL_SCANCODE_H
-    KEY_I,              // SDL_SCANCODE_I
-    KEY_J,              // SDL_SCANCODE_J
-    KEY_K,              // SDL_SCANCODE_K
-    KEY_L,              // SDL_SCANCODE_L
-    KEY_M,              // SDL_SCANCODE_M
-    KEY_N,              // SDL_SCANCODE_N
-    KEY_O,              // SDL_SCANCODE_O
-    KEY_P,              // SDL_SCANCODE_P
-    KEY_Q,              // SDL_SCANCODE_Q
-    KEY_R,              // SDL_SCANCODE_R
-    KEY_S,              // SDL_SCANCODE_S
-    KEY_T,              // SDL_SCANCODE_T
-    KEY_U,              // SDL_SCANCODE_U
-    KEY_V,              // SDL_SCANCODE_V
-    KEY_W,              // SDL_SCANCODE_W
-    KEY_X,              // SDL_SCANCODE_X
-    KEY_Y,              // SDL_SCANCODE_Y
-    KEY_Z,              // SDL_SCANCODE_Z
-    KEY_ONE,            // SDL_SCANCODE_1
-    KEY_TWO,            // SDL_SCANCODE_2
-    KEY_THREE,          // SDL_SCANCODE_3
-    KEY_FOUR,           // SDL_SCANCODE_4
-    KEY_FIVE,           // SDL_SCANCODE_5
-    KEY_SIX,            // SDL_SCANCODE_6
-    KEY_SEVEN,          // SDL_SCANCODE_7
-    KEY_EIGHT,          // SDL_SCANCODE_8
-    KEY_NINE,           // SDL_SCANCODE_9
-    KEY_ZERO,           // SDL_SCANCODE_0
-    KEY_ENTER,          // SDL_SCANCODE_RETURN
-    KEY_ESCAPE,         // SDL_SCANCODE_ESCAPE
-    KEY_BACKSPACE,      // SDL_SCANCODE_BACKSPACE
-    KEY_TAB,            // SDL_SCANCODE_TAB
-    KEY_SPACE,          // SDL_SCANCODE_SPACE
-    KEY_MINUS,          // SDL_SCANCODE_MINUS
-    KEY_EQUAL,          // SDL_SCANCODE_EQUALS
-    KEY_LEFT_BRACKET,   // SDL_SCANCODE_LEFTBRACKET
-    KEY_RIGHT_BRACKET,  // SDL_SCANCODE_RIGHTBRACKET
-    KEY_BACKSLASH,      // SDL_SCANCODE_BACKSLASH
-    0,                  // SDL_SCANCODE_NONUSHASH
-    KEY_SEMICOLON,      // SDL_SCANCODE_SEMICOLON
-    KEY_APOSTROPHE,     // SDL_SCANCODE_APOSTROPHE
-    KEY_GRAVE,          // SDL_SCANCODE_GRAVE
-    KEY_COMMA,          // SDL_SCANCODE_COMMA
-    KEY_PERIOD,         // SDL_SCANCODE_PERIOD
-    KEY_SLASH,          // SDL_SCANCODE_SLASH
-    KEY_CAPS_LOCK,      // SDL_SCANCODE_CAPSLOCK
-    KEY_F1,             // SDL_SCANCODE_F1
-    KEY_F2,             // SDL_SCANCODE_F2
-    KEY_F3,             // SDL_SCANCODE_F3
-    KEY_F4,             // SDL_SCANCODE_F4
-    KEY_F5,             // SDL_SCANCODE_F5
-    KEY_F6,             // SDL_SCANCODE_F6
-    KEY_F7,             // SDL_SCANCODE_F7
-    KEY_F8,             // SDL_SCANCODE_F8
-    KEY_F9,             // SDL_SCANCODE_F9
-    KEY_F10,            // SDL_SCANCODE_F10
-    KEY_F11,            // SDL_SCANCODE_F11
-    KEY_F12,            // SDL_SCANCODE_F12
-    KEY_PRINT_SCREEN,   // SDL_SCANCODE_PRINTSCREEN
-    KEY_SCROLL_LOCK,    // SDL_SCANCODE_SCROLLLOCK
-    KEY_PAUSE,          // SDL_SCANCODE_PAUSE
-    KEY_INSERT,         // SDL_SCANCODE_INSERT
-    KEY_HOME,           // SDL_SCANCODE_HOME
-    KEY_PAGE_UP,        // SDL_SCANCODE_PAGEUP
-    KEY_DELETE,         // SDL_SCANCODE_DELETE
-    KEY_END,            // SDL_SCANCODE_END
-    KEY_PAGE_DOWN,      // SDL_SCANCODE_PAGEDOWN
-    KEY_RIGHT,          // SDL_SCANCODE_RIGHT
-    KEY_LEFT,           // SDL_SCANCODE_LEFT
-    KEY_DOWN,           // SDL_SCANCODE_DOWN
-    KEY_UP,             // SDL_SCANCODE_UP
-    KEY_NUM_LOCK,       // SDL_SCANCODE_NUMLOCKCLEAR
-    KEY_KP_DIVIDE,      // SDL_SCANCODE_KP_DIVIDE
-    KEY_KP_MULTIPLY,    // SDL_SCANCODE_KP_MULTIPLY
-    KEY_KP_SUBTRACT,    // SDL_SCANCODE_KP_MINUS
-    KEY_KP_ADD,         // SDL_SCANCODE_KP_PLUS
-    KEY_KP_ENTER,       // SDL_SCANCODE_KP_ENTER
-    KEY_KP_1,           // SDL_SCANCODE_KP_1
-    KEY_KP_2,           // SDL_SCANCODE_KP_2
-    KEY_KP_3,           // SDL_SCANCODE_KP_3
-    KEY_KP_4,           // SDL_SCANCODE_KP_4
-    KEY_KP_5,           // SDL_SCANCODE_KP_5
-    KEY_KP_6,           // SDL_SCANCODE_KP_6
-    KEY_KP_7,           // SDL_SCANCODE_KP_7
-    KEY_KP_8,           // SDL_SCANCODE_KP_8
-    KEY_KP_9,           // SDL_SCANCODE_KP_9
-    KEY_KP_0,           // SDL_SCANCODE_KP_0
-    KEY_KP_DECIMAL,     // SDL_SCANCODE_KP_PERIOD
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0,
-    KEY_LEFT_CONTROL,   //SDL_SCANCODE_LCTRL
-    KEY_LEFT_SHIFT,     //SDL_SCANCODE_LSHIFT
-    KEY_LEFT_ALT,       //SDL_SCANCODE_LALT
-    KEY_LEFT_SUPER,     //SDL_SCANCODE_LGUI
-    KEY_RIGHT_CONTROL,  //SDL_SCANCODE_RCTRL
-    KEY_RIGHT_SHIFT,    //SDL_SCANCODE_RSHIFT
-    KEY_RIGHT_ALT,      //SDL_SCANCODE_RALT
-    KEY_RIGHT_SUPER     //SDL_SCANCODE_RGUI
-};
-
-static const int CursorsLUT[] = {
-    SDL_SYSTEM_CURSOR_ARROW,       // 0  MOUSE_CURSOR_DEFAULT
-    SDL_SYSTEM_CURSOR_ARROW,       // 1  MOUSE_CURSOR_ARROW
-    SDL_SYSTEM_CURSOR_IBEAM,       // 2  MOUSE_CURSOR_IBEAM
-    SDL_SYSTEM_CURSOR_CROSSHAIR,   // 3  MOUSE_CURSOR_CROSSHAIR
-    SDL_SYSTEM_CURSOR_HAND,        // 4  MOUSE_CURSOR_POINTING_HAND
-    SDL_SYSTEM_CURSOR_SIZEWE,      // 5  MOUSE_CURSOR_RESIZE_EW
-    SDL_SYSTEM_CURSOR_SIZENS,      // 6  MOUSE_CURSOR_RESIZE_NS
-    SDL_SYSTEM_CURSOR_SIZENWSE,    // 7  MOUSE_CURSOR_RESIZE_NWSE
-    SDL_SYSTEM_CURSOR_SIZENESW,    // 8  MOUSE_CURSOR_RESIZE_NESW
-    SDL_SYSTEM_CURSOR_SIZEALL,     // 9  MOUSE_CURSOR_RESIZE_ALL
-    SDL_SYSTEM_CURSOR_NO           // 10 MOUSE_CURSOR_NOT_ALLOWED
-    //SDL_SYSTEM_CURSOR_WAIT,      // No equivalent implemented on MouseCursor enum on raylib.h
-    //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h
-};
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Declaration
-//----------------------------------------------------------------------------------
-int InitPlatform(void);                                      // Initialize platform (graphics, inputs and more)
-void ClosePlatform(void);                                    // Close platform
-
-static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode);  // Help convert SDL scancodes to raylib key
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-// NOTE: Functions declaration is provided by raylib.h
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Window and Graphics Device
-//----------------------------------------------------------------------------------
-
-// Check if application should close
-bool WindowShouldClose(void)
-{
-    if (CORE.Window.ready) return CORE.Window.shouldClose;
-    else return true;
-}
-
-// Toggle fullscreen mode
-void ToggleFullscreen(void)
-{
-    const int monitor = SDL_GetWindowDisplayIndex(platform.window);
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
-        {
-            SDL_SetWindowFullscreen(platform.window, 0);
-            CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
-            CORE.Window.fullscreen = false;
-        }
-        else
-        {
-            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
-            CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
-            CORE.Window.fullscreen = true;
-        }
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-}
-
-// Toggle borderless windowed mode
-void ToggleBorderlessWindowed(void)
-{
-    const int monitor = SDL_GetWindowDisplayIndex(platform.window);
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
-        {
-            SDL_SetWindowFullscreen(platform.window, 0);
-            CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
-        }
-        else
-        {
-            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
-            CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
-        }
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-}
-
-// Set window state: maximized, if resizable
-void MaximizeWindow(void)
-{
-    SDL_MaximizeWindow(platform.window);
-    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
-}
-
-// Set window state: minimized
-void MinimizeWindow(void)
-{
-    SDL_MinimizeWindow(platform.window);
-    CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;
-}
-
-// Set window state: not minimized/maximized
-void RestoreWindow(void)
-{
-    SDL_ShowWindow(platform.window);
-}
-
-// Set window configuration state using flags
-void SetWindowState(unsigned int flags)
-{
-    CORE.Window.flags |= flags;
-
-    if (flags & FLAG_VSYNC_HINT)
-    {
-        SDL_GL_SetSwapInterval(1);
-    }
-    if (flags & FLAG_FULLSCREEN_MODE)
-    {
-        const int monitor = SDL_GetWindowDisplayIndex(platform.window);
-        const int monitorCount = SDL_GetNumVideoDisplays();
-        if ((monitor >= 0) && (monitor < monitorCount))
-        {
-            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
-            CORE.Window.fullscreen = true;
-        }
-        else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-    }
-    if (flags & FLAG_WINDOW_RESIZABLE)
-    {
-        SDL_SetWindowResizable(platform.window, SDL_TRUE);
-    }
-    if (flags & FLAG_WINDOW_UNDECORATED)
-    {
-        SDL_SetWindowBordered(platform.window, SDL_FALSE);
-    }
-    if (flags & FLAG_WINDOW_HIDDEN)
-    {
-        SDL_HideWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_MINIMIZED)
-    {
-        SDL_MinimizeWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_MAXIMIZED)
-    {
-        SDL_MaximizeWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_UNFOCUSED)
-    {
-        // NOTE: To be able to implement this part it seems that we should
-        // do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h`
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_TOPMOST)
-    {
-        SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
-    }
-    if (flags & FLAG_WINDOW_ALWAYS_RUN)
-    {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_TRANSPARENT)
-    {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_HIGHDPI)
-    {
-        // NOTE: Such a function does not seem to exist
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
-    {
-        //SDL_SetWindowGrab(platform.window, SDL_FALSE);
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
-    {
-        const int monitor = SDL_GetWindowDisplayIndex(platform.window);
-        const int monitorCount = SDL_GetNumVideoDisplays();
-        if ((monitor >= 0) && (monitor < monitorCount))
-        {
-            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
-        }
-        else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-    }
-    if (flags & FLAG_MSAA_4X_HINT)
-    {
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling
-    }
-    if (flags & FLAG_INTERLACED_HINT)
-    {
-        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
-    }
-}
-
-// Clear window configuration state flags
-void ClearWindowState(unsigned int flags)
-{
-    CORE.Window.flags &= ~flags;
-
-    if (flags & FLAG_VSYNC_HINT)
-    {
-        SDL_GL_SetSwapInterval(0);
-    }
-    if (flags & FLAG_FULLSCREEN_MODE)
-    {
-        SDL_SetWindowFullscreen(platform.window, 0);
-        CORE.Window.fullscreen = false;
-    }
-    if (flags & FLAG_WINDOW_RESIZABLE)
-    {
-        SDL_SetWindowResizable(platform.window, SDL_FALSE);
-    }
-    if (flags & FLAG_WINDOW_UNDECORATED)
-    {
-        SDL_SetWindowBordered(platform.window, SDL_TRUE);
-    }
-    if (flags & FLAG_WINDOW_HIDDEN)
-    {
-        SDL_ShowWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_MINIMIZED)
-    {
-        SDL_RestoreWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_MAXIMIZED)
-    {
-        SDL_RestoreWindow(platform.window);
-    }
-    if (flags & FLAG_WINDOW_UNFOCUSED)
-    {
-        //SDL_RaiseWindow(platform.window);
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_TOPMOST)
-    {
-        SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
-    }
-    if (flags & FLAG_WINDOW_ALWAYS_RUN)
-    {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_TRANSPARENT)
-    {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_HIGHDPI)
-    {
-        // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)
-    {
-        //SDL_SetWindowGrab(platform.window, SDL_TRUE);
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
-    }
-    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
-    {
-        SDL_SetWindowFullscreen(platform.window, 0);
-    }
-    if (flags & FLAG_MSAA_4X_HINT)
-    {
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling
-    }
-    if (flags & FLAG_INTERLACED_HINT)
-    {
-        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
-    }
-}
-
-// Set icon for window
-void SetWindowIcon(Image image)
-{
-    SDL_Surface *iconSurface = NULL;
-
-    unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0;
-    int depth = 0;  // Depth in bits
-    int pitch = 0;  // Pixel spacing (pitch) in bytes
-
-    switch (image.format)
-    {
-        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
-            rmask = 0xFF, gmask = 0;
-            bmask = 0, amask = 0;
-            depth = 8, pitch = image.width;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
-            rmask = 0xFF, gmask = 0xFF00;
-            bmask = 0, amask = 0;
-            depth = 16, pitch = image.width*2;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
-            rmask = 0xF800, gmask = 0x07E0;
-            bmask = 0x001F, amask = 0;
-            depth = 16, pitch = image.width*2;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8: // Uses BGR for 24-bit
-            rmask = 0x0000FF, gmask = 0x00FF00;
-            bmask = 0xFF0000, amask = 0;
-            depth = 24, pitch = image.width*3;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
-            rmask = 0xF800, gmask = 0x07C0;
-            bmask = 0x003E, amask = 0x0001;
-            depth = 16, pitch = image.width*2;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
-            rmask = 0xF000, gmask = 0x0F00;
-            bmask = 0x00F0, amask = 0x000F;
-            depth = 16, pitch = image.width*2;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
-            rmask = 0xFF000000, gmask = 0x00FF0000;
-            bmask = 0x0000FF00, amask = 0x000000FF;
-            depth = 32, pitch = image.width*4;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R32:
-            rmask = 0xFFFFFFFF, gmask = 0;
-            bmask = 0, amask = 0;
-            depth = 32, pitch = image.width*4;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
-            rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF;
-            bmask = 0xFFFFFFFF, amask = 0;
-            depth = 96, pitch = image.width*12;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
-            rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF;
-            bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF;
-            depth = 128, pitch = image.width*16;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R16:
-            rmask = 0xFFFF, gmask = 0;
-            bmask = 0, amask = 0;
-            depth = 16, pitch = image.width*2;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
-            rmask = 0xFFFF, gmask = 0xFFFF;
-            bmask = 0xFFFF, amask = 0;
-            depth = 48, pitch = image.width*6;
-            break;
-        case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
-            rmask = 0xFFFF, gmask = 0xFFFF;
-            bmask = 0xFFFF, amask = 0xFFFF;
-            depth = 64, pitch = image.width*8;
-            break;
-        default: return; // Compressed formats are not supported
-    }
-
-    iconSurface = SDL_CreateRGBSurfaceFrom( image.data, image.width, image.height, depth, pitch, rmask, gmask, bmask, amask );
-
-    if (iconSurface)
-    {
-        SDL_SetWindowIcon(platform.window, iconSurface);
-        SDL_FreeSurface(iconSurface);
-    }
-}
-
-// Set icon for window
-void SetWindowIcons(Image *images, int count)
-{
-    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
-}
-
-// Set title for window
-void SetWindowTitle(const char *title)
-{
-    SDL_SetWindowTitle(platform.window, title);
-
-    CORE.Window.title = title;
-}
-
-// Set window position on screen (windowed mode)
-void SetWindowPosition(int x, int y)
-{
-    SDL_SetWindowPosition(platform.window, x, y);
-
-    CORE.Window.position.x = x;
-    CORE.Window.position.y = y;
-}
-
-// Set monitor for the current window
-void SetWindowMonitor(int monitor)
-{
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        // NOTE:
-        // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3,
-        //    see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba
-        // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again.
-        const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)? true : false;
-
-        const int screenWidth = CORE.Window.screen.width;
-        const int screenHeight = CORE.Window.screen.height;
-        SDL_Rect usableBounds;
-        if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0)
-        {
-            if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen.
-
-            // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it
-            if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h))
-            {
-                // NOTE:
-                // 1. There's a known issue where if the window larger than the target display bounds,
-                //    when moving the windows to that display, the window could be clipped back
-                //    ending up positioned partly outside the target display.
-                // 2. The workaround for that is, previously to moving the window,
-                //    setting the window size to the target display size, so they match.
-                // 3. It wasn't done here because we can't assume changing the window size automatically
-                //    is acceptable behavior by the user.
-                SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y);
-                CORE.Window.position.x = usableBounds.x;
-                CORE.Window.position.y = usableBounds.y;
-            }
-            else
-            {
-                const int x = usableBounds.x + (usableBounds.w/2) - (screenWidth/2);
-                const int y = usableBounds.y + (usableBounds.h/2) - (screenHeight/2);
-                SDL_SetWindowPosition(platform.window, x, y);
-                CORE.Window.position.x = x;
-                CORE.Window.position.y = y;
-            }
-
-            if (wasFullscreen == 1) ToggleFullscreen(); // Re-enter fullscreen
-        }
-        else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds");
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-}
-
-// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
-void SetWindowMinSize(int width, int height)
-{
-    SDL_SetWindowMinimumSize(platform.window, width, height);
-
-    CORE.Window.screenMin.width = width;
-    CORE.Window.screenMin.height = height;
-}
-
-// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
-void SetWindowMaxSize(int width, int height)
-{
-    SDL_SetWindowMaximumSize(platform.window, width, height);
-
-    CORE.Window.screenMax.width = width;
-    CORE.Window.screenMax.height = height;
-}
-
-// Set window dimensions
-void SetWindowSize(int width, int height)
-{
-    SDL_SetWindowSize(platform.window, width, height);
-
-    CORE.Window.screen.width = width;
-    CORE.Window.screen.height = height;
-}
-
-// Set window opacity, value opacity is between 0.0 and 1.0
-void SetWindowOpacity(float opacity)
-{
-    if (opacity >= 1.0f) opacity = 1.0f;
-    else if (opacity <= 0.0f) opacity = 0.0f;
-
-    SDL_SetWindowOpacity(platform.window, opacity);
-}
-
-// Set window focused
-void SetWindowFocused(void)
-{
-    SDL_RaiseWindow(platform.window);
-}
-
-// Get native window handle
-void *GetWindowHandle(void)
-{
-    return (void *)platform.window;
-}
-
-// Get number of monitors
-int GetMonitorCount(void)
-{
-    int monitorCount = 0;
-
-    monitorCount = SDL_GetNumVideoDisplays();
-
-    return monitorCount;
-}
-
-// Get number of monitors
-int GetCurrentMonitor(void)
-{
-    int currentMonitor = 0;
-
-    currentMonitor = SDL_GetWindowDisplayIndex(platform.window);
-
-    return currentMonitor;
-}
-
-// Get selected monitor position
-Vector2 GetMonitorPosition(int monitor)
-{
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        SDL_Rect displayBounds;
-        if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0)
-        {
-            return (Vector2){ (float)displayBounds.x, (float)displayBounds.y };
-        }
-        else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds");
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-    return (Vector2){ 0.0f, 0.0f };
-}
-
-// Get selected monitor width (currently used by monitor)
-int GetMonitorWidth(int monitor)
-{
-    int width = 0;
-
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        SDL_DisplayMode mode;
-        SDL_GetCurrentDisplayMode(monitor, &mode);
-        width = mode.w;
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return width;
-}
-
-// Get selected monitor height (currently used by monitor)
-int GetMonitorHeight(int monitor)
-{
-    int height = 0;
-
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        SDL_DisplayMode mode;
-        SDL_GetCurrentDisplayMode(monitor, &mode);
-        height = mode.h;
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return height;
-}
-
-// Get selected monitor physical width in millimetres
-int GetMonitorPhysicalWidth(int monitor)
-{
-    int width = 0;
-
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        float ddpi = 0.0f;
-        SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL);
-        SDL_DisplayMode mode;
-        SDL_GetCurrentDisplayMode(monitor, &mode);
-        // Calculate size on inches, then convert to millimeter
-        if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f;
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return width;
-}
-
-// Get selected monitor physical height in millimetres
-int GetMonitorPhysicalHeight(int monitor)
-{
-    int height = 0;
-
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        float ddpi = 0.0f;
-        SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL);
-        SDL_DisplayMode mode;
-        SDL_GetCurrentDisplayMode(monitor, &mode);
-        // Calculate size on inches, then convert to millimeter
-        if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f;
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return height;
-}
-
-// Get selected monitor refresh rate
-int GetMonitorRefreshRate(int monitor)
-{
-    int refresh = 0;
-
-    const int monitorCount = SDL_GetNumVideoDisplays();
-    if ((monitor >= 0) && (monitor < monitorCount))
-    {
-        SDL_DisplayMode mode;
-        SDL_GetCurrentDisplayMode(monitor, &mode);
-        refresh = mode.refresh_rate;
-    }
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return refresh;
-}
-
-// Get the human-readable, UTF-8 encoded name of the selected monitor
-const char *GetMonitorName(int monitor)
-{
-    const int monitorCount = SDL_GetNumVideoDisplays();
-
-    if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor);
-    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
-
-    return "";
-}
-
-// Get window position XY on monitor
-Vector2 GetWindowPosition(void)
-{
-    int x = 0;
-    int y = 0;
-
-    SDL_GetWindowPosition(platform.window, &x, &y);
-
-    return (Vector2){ (float)x, (float)y };
-}
-
-// Get window scale DPI factor for current monitor
-Vector2 GetWindowScaleDPI(void)
-{
-    Vector2 scale = { 1.0f, 1.0f };
-
-    // NOTE: SDL_GetWindowDisplayScale was only added on SDL3
-    //       see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
-    // TODO: Implement the window scale factor calculation manually.
-    TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
-
-    return scale;
-}
-
-// Set clipboard text content
-void SetClipboardText(const char *text)
-{
-    SDL_SetClipboardText(text);
-}
-
-// Get clipboard text content
-const char *GetClipboardText(void)
-{
-    static char buffer[MAX_CLIPBOARD_BUFFER_LENGTH] = { 0 };
-
-    char *clipboard = SDL_GetClipboardText();
-
-    int clipboardSize = snprintf(buffer, sizeof(buffer), "%s", clipboard);
-    if (clipboardSize >= MAX_CLIPBOARD_BUFFER_LENGTH)
-    {
-        char *truncate = buffer + MAX_CLIPBOARD_BUFFER_LENGTH - 4;
-        sprintf(truncate, "...");
-    }
-
-    SDL_free(clipboard);
-
-    return buffer;
-}
-
-// Show mouse cursor
-void ShowCursor(void)
-{
-    SDL_ShowCursor(SDL_ENABLE);
-
-    CORE.Input.Mouse.cursorHidden = false;
-}
-
-// Hides mouse cursor
-void HideCursor(void)
-{
-    SDL_ShowCursor(SDL_DISABLE);
-
-    CORE.Input.Mouse.cursorHidden = true;
-}
-
-// Enables cursor (unlock cursor)
-void EnableCursor(void)
-{
-    SDL_SetRelativeMouseMode(SDL_FALSE);
-    SDL_ShowCursor(SDL_ENABLE);
-
-    platform.cursorRelative = false;
-    CORE.Input.Mouse.cursorHidden = false;
-}
-
-// Disables cursor (lock cursor)
-void DisableCursor(void)
-{
-    SDL_SetRelativeMouseMode(SDL_TRUE);
-
-    platform.cursorRelative = true;
-    CORE.Input.Mouse.cursorHidden = true;
-}
-
-// Swap back buffer with front buffer (screen drawing)
-void SwapScreenBuffer(void)
-{
-    SDL_GL_SwapWindow(platform.window);
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Misc
-//----------------------------------------------------------------------------------
-
-// Get elapsed time measure in seconds
-double GetTime(void)
-{
-    unsigned int ms = SDL_GetTicks();    // Elapsed time in milliseconds since SDL_Init()
-    double time = (double)ms/1000;
-    return time;
-}
-
-// Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
-void OpenURL(const char *url)
-{
-    // Security check to (partially) avoid malicious code
-    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
-    else SDL_OpenURL(url);
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Inputs
-//----------------------------------------------------------------------------------
-
-// Set internal gamepad mappings
-int SetGamepadMappings(const char *mappings)
-{
-    return SDL_GameControllerAddMapping(mappings);
-}
-
-// Set gamepad vibration
-void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
-{
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f))
-    {
-        if (leftMotor < 0.0f) leftMotor = 0.0f;
-        if (leftMotor > 1.0f) leftMotor = 1.0f;
-        if (rightMotor < 0.0f) rightMotor = 0.0f;
-        if (rightMotor > 1.0f) rightMotor = 1.0f;
-        if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME;
-
-        SDL_GameControllerRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(duration*1000.0f));
-    }
-}
-
-// Set mouse position XY
-void SetMousePosition(int x, int y)
-{
-    SDL_WarpMouseInWindow(platform.window, x, y);
-
-    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
-    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
-}
-
-// Set mouse cursor
-void SetMouseCursor(int cursor)
-{
-    platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]);
-    SDL_SetCursor(platform.cursor);
-
-    CORE.Input.Mouse.cursor = cursor;
-}
-
-// Get physical key name.
-const char *GetKeyName(int key)
-{
-    return SDL_GetKeyName(key);
-}
-
-static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
-{
-    CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId);
-
-    for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
-    {
-        SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i);
-        CORE.Input.Touch.pointId[i] = finger->id;
-        CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
-        CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
-        CORE.Input.Touch.currentTouchState[i] = 1;
-    }
-
-    for (int i = CORE.Input.Touch.pointCount; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.currentTouchState[i] = 0;
-}
-
-// Register all input events
-void PollInputEvents(void)
-{
-#if defined(SUPPORT_GESTURES_SYSTEM)
-    // NOTE: Gestures update must be called every frame to reset gestures correctly
-    // because ProcessGestureEvent() is just called on an event, not every frame
-    UpdateGestures();
-#endif
-
-    // Reset keys/chars pressed registered
-    CORE.Input.Keyboard.keyPressedQueueCount = 0;
-    CORE.Input.Keyboard.charPressedQueueCount = 0;
-
-    // Reset mouse wheel
-    CORE.Input.Mouse.currentWheelMove.x = 0;
-    CORE.Input.Mouse.currentWheelMove.y = 0;
-
-    // Register previous mouse position
-    if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
-    else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
-
-    // Reset last gamepad button/axis registered state
-    for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++)
-    {
-        // Check if gamepad is available
-        if (CORE.Input.Gamepad.ready[i])
-        {
-            // Register previous gamepad button states
-            for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
-            {
-                CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
-            }
-        }
-    }
-
-    // Register previous touch states
-    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
-
-    // Map touch position to mouse position for convenience
-    CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
-
-    int touchAction = -1;       // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
-    bool realTouch = false;     // Flag to differentiate real touch gestures from mouse ones
-
-    // Register previous keys states
-    // NOTE: Android supports up to 260 keys
-    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
-    {
-        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
-        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
-    }
-
-    // Register previous mouse states
-    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
-
-    // Poll input events for current platform
-    //-----------------------------------------------------------------------------
-    /*
-    // WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values
-    const Uint8 *keys = SDL_GetKeyboardState(NULL);
-    for (int i = 0; i < 256; ++i)
-    {
-        CORE.Input.Keyboard.currentKeyState[i] = keys[i];
-        //if (keys[i]) TRACELOG(LOG_WARNING, "Pressed key: %i", i);
-    }
-    */
-
-    CORE.Window.resizedLastFrame = false;
-
-    SDL_Event event = { 0 };
-    while (SDL_PollEvent(&event) != 0)
-    {
-        // All input events can be processed after polling
-        switch (event.type)
-        {
-            case SDL_QUIT: CORE.Window.shouldClose = true; break;
-
-            case SDL_DROPFILE:      // Dropped file
-            {
-                if (CORE.Window.dropFileCount == 0)
-                {
-                    // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files
-                    // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed
-                    // TODO: Pointers should probably be reallocated for any new file added...
-                    CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *));
-
-                    CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
-                    strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file);
-                    SDL_free(event.drop.file);
-
-                    CORE.Window.dropFileCount++;
-                }
-                else if (CORE.Window.dropFileCount < 1024)
-                {
-                    CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
-                    strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file);
-                    SDL_free(event.drop.file);
-
-                    CORE.Window.dropFileCount++;
-                }
-                else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!");
-
-            } break;
-
-            // Window events are also polled (Minimized, maximized, close...)
-            case SDL_WINDOWEVENT:
-            {
-                switch (event.window.event)
-                {
-                    case SDL_WINDOWEVENT_RESIZED:
-                    case SDL_WINDOWEVENT_SIZE_CHANGED:
-                    {
-                        const int width = event.window.data1;
-                        const int height = event.window.data2;
-                        SetupViewport(width, height);
-                        CORE.Window.screen.width = width;
-                        CORE.Window.screen.height = height;
-                        CORE.Window.currentFbo.width = width;
-                        CORE.Window.currentFbo.height = height;
-                        CORE.Window.resizedLastFrame = true;
-                    } break;
-                    case SDL_WINDOWEVENT_ENTER:
-                    {
-                        CORE.Input.Mouse.cursorOnScreen = true;
-                    } break;
-                    case SDL_WINDOWEVENT_LEAVE:
-                    {
-                        CORE.Input.Mouse.cursorOnScreen = false;
-                    } break;
-                    case SDL_WINDOWEVENT_HIDDEN:
-                    case SDL_WINDOWEVENT_MINIMIZED:
-                    case SDL_WINDOWEVENT_FOCUS_LOST:
-                    case SDL_WINDOWEVENT_SHOWN:
-                    case SDL_WINDOWEVENT_FOCUS_GAINED:
-                    case SDL_WINDOWEVENT_MAXIMIZED:
-                    case SDL_WINDOWEVENT_RESTORED:
-                    default: break;
-                }
-            } break;
-
-            // Keyboard events
-            case SDL_KEYDOWN:
-            {
-                KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
-
-                if (key != KEY_NULL)
-                {
-                    // If key was up, add it to the key pressed queue
-                    if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
-                    {
-                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
-                        CORE.Input.Keyboard.keyPressedQueueCount++;
-                    }
-
-                    CORE.Input.Keyboard.currentKeyState[key] = 1;
-                }
-
-                if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
-
-                // TODO: Put exitKey verification outside the switch?
-                if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey])
-                {
-                    CORE.Window.shouldClose = true;
-                }
-            } break;
-
-            case SDL_KEYUP:
-            {
-                KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
-                if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
-            } break;
-
-            case SDL_TEXTINPUT:
-            {
-                // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
-
-                int codepointSize = 0;
-
-                // Check if there is space available in the queue
-                if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
-                {
-                    // Add character (codepoint) to the queue
-                    CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = GetCodepointNext(event.text.text, &codepointSize);
-                    CORE.Input.Keyboard.charPressedQueueCount++;
-                }
-            } break;
-
-            // Check mouse events
-            case SDL_MOUSEBUTTONDOWN:
-            {
-                // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW
-                //       The following conditions align SDL with raylib.h MouseButton enum order
-                int btn = event.button.button - 1;
-                if (btn == 2) btn = 1;
-                else if (btn == 1) btn = 2;
-
-                CORE.Input.Mouse.currentButtonState[btn] = 1;
-                CORE.Input.Touch.currentTouchState[btn] = 1;
-
-                touchAction = 1;
-            } break;
-            case SDL_MOUSEBUTTONUP:
-            {
-                // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW
-                //       The following conditions align SDL with raylib.h MouseButton enum order
-                int btn = event.button.button - 1;
-                if (btn == 2) btn = 1;
-                else if (btn == 1) btn = 2;
-
-                CORE.Input.Mouse.currentButtonState[btn] = 0;
-                CORE.Input.Touch.currentTouchState[btn] = 0;
-
-                touchAction = 0;
-            } break;
-            case SDL_MOUSEWHEEL:
-            {
-                CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x;
-                CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y;
-            } break;
-            case SDL_MOUSEMOTION:
-            {
-                if (platform.cursorRelative)
-                {
-                    CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel;
-                    CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel;
-                    CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
-                }
-                else
-                {
-                    CORE.Input.Mouse.currentPosition.x = (float)event.motion.x;
-                    CORE.Input.Mouse.currentPosition.y = (float)event.motion.y;
-                }
-
-                CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
-                touchAction = 2;
-            } break;
-
-            case SDL_FINGERDOWN:
-            {
-                UpdateTouchPointsSDL(event.tfinger);
-                touchAction = 1;
-                realTouch = true;
-            } break;
-            case SDL_FINGERUP:
-            {
-                UpdateTouchPointsSDL(event.tfinger);
-                touchAction = 0;
-                realTouch = true;
-            } break;
-            case SDL_FINGERMOTION:
-            {
-                UpdateTouchPointsSDL(event.tfinger);
-                touchAction = 2;
-                realTouch = true;
-            } break;
-
-            // Check gamepad events
-            case SDL_JOYDEVICEADDED:
-            {
-                int jid = event.jdevice.which;
-
-                if (!CORE.Input.Gamepad.ready[jid] && (jid < MAX_GAMEPADS))
-                {
-                    platform.gamepad[jid] = SDL_GameControllerOpen(jid);
-
-                    if (platform.gamepad[jid])
-                    {
-                        CORE.Input.Gamepad.ready[jid] = true;
-                        CORE.Input.Gamepad.axisCount[jid] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[jid]));
-                        CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
-                        CORE.Input.Gamepad.axisState[jid][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
-                        strncpy(CORE.Input.Gamepad.name[jid], SDL_GameControllerNameForIndex(jid), 63);
-                        CORE.Input.Gamepad.name[jid][63] = '\0';
-                    }
-                    else
-                    {
-                        TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
-                    }
-                }
-            } break;
-            case SDL_JOYDEVICEREMOVED:
-            {
-                int jid = event.jdevice.which;
-
-                if (jid == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[jid])))
-                {
-                    SDL_GameControllerClose(platform.gamepad[jid]);
-                    platform.gamepad[jid] = SDL_GameControllerOpen(0);
-                    CORE.Input.Gamepad.ready[jid] = false;
-                    memset(CORE.Input.Gamepad.name[jid], 0, 64);
-                }
-            } break;
-            case SDL_CONTROLLERBUTTONDOWN:
-            {
-                int button = -1;
-
-                switch (event.jbutton.button)
-                {
-                    case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
-                    case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
-                    case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
-                    case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
-
-                    case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
-                    case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
-
-                    case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
-                    case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
-                    case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
-
-                    case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
-
-                    case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
-                    case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
-                    default: break;
-                }
-
-                if (button >= 0)
-                {
-                    CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 1;
-                    CORE.Input.Gamepad.lastButtonPressed = button;
-                }
-            } break;
-            case SDL_CONTROLLERBUTTONUP:
-            {
-                int button = -1;
-
-                switch (event.jbutton.button)
-                {
-                    case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
-                    case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
-                    case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
-                    case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
-
-                    case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
-                    case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
-
-                    case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
-                    case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
-                    case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
-
-                    case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
-                    case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
-
-                    case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
-                    case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
-                    default: break;
-                }
-
-                if (button >= 0)
-                {
-                    CORE.Input.Gamepad.currentButtonState[event.jbutton.which][button] = 0;
-                    if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
-                }
-            } break;
-            case SDL_CONTROLLERAXISMOTION:
-            {
-                int axis = -1;
-
-                switch (event.jaxis.axis)
-                {
-                    case SDL_CONTROLLER_AXIS_LEFTX: axis = GAMEPAD_AXIS_LEFT_X; break;
-                    case SDL_CONTROLLER_AXIS_LEFTY: axis = GAMEPAD_AXIS_LEFT_Y; break;
-                    case SDL_CONTROLLER_AXIS_RIGHTX: axis = GAMEPAD_AXIS_RIGHT_X; break;
-                    case SDL_CONTROLLER_AXIS_RIGHTY: axis = GAMEPAD_AXIS_RIGHT_Y; break;
-                    case SDL_CONTROLLER_AXIS_TRIGGERLEFT: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break;
-                    case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break;
-                    default: break;
-                }
-
-                if (axis >= 0)
-                {
-                    // SDL axis value range is -32768 to 32767, we normalize it to RayLib's -1.0 to 1.0f range
-                    float value = event.jaxis.value/(float)32767;
-                    CORE.Input.Gamepad.axisState[event.jaxis.which][axis] = value;
-
-                    // Register button state for triggers in addition to their axes
-                    if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))
-                    {
-                        int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
-                        int pressed = (value > 0.1f);
-                        CORE.Input.Gamepad.currentButtonState[event.jaxis.which][button] = pressed;
-                        if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
-                        else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
-                    }
-                }
-            } break;
-            default: break;
-        }
-
-#if defined(SUPPORT_GESTURES_SYSTEM)
-        if (touchAction > -1)
-        {
-            // Process mouse events as touches to be able to use mouse-gestures
-            GestureEvent gestureEvent = { 0 };
-
-            // Register touch actions
-            gestureEvent.touchAction = touchAction;
-
-            // Assign a pointer ID
-            gestureEvent.pointId[0] = 0;
-
-            // Register touch points count
-            gestureEvent.pointCount = 1;
-
-            // Register touch points position, only one point registered
-            if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0];
-            else gestureEvent.position[0] = GetMousePosition();
-
-            // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
-            gestureEvent.position[0].x /= (float)GetScreenWidth();
-            gestureEvent.position[0].y /= (float)GetScreenHeight();
-
-            // Gesture data is sent to gestures-system for processing
-            ProcessGestureEvent(gestureEvent);
-
-            touchAction = -1;
-        }
-#endif
-    }
-    //-----------------------------------------------------------------------------
-}
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Definition
-//----------------------------------------------------------------------------------
-
-// Initialize platform: graphics, inputs and more
-int InitPlatform(void)
-{
-    // Initialize SDL internal global state, only required systems
-    // NOTE: Not all systems need to be initialized, SDL_INIT_AUDIO is not required, managed by miniaudio
-    int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER);
-    if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; }
-
-    // Initialize graphic device: display/window and graphic context
-    //----------------------------------------------------------------------------
-    unsigned int flags = 0;
-    flags |= SDL_WINDOW_SHOWN;
-    flags |= SDL_WINDOW_OPENGL;
-    flags |= SDL_WINDOW_INPUT_FOCUS;
-    flags |= SDL_WINDOW_MOUSE_FOCUS;
-    flags |= SDL_WINDOW_MOUSE_CAPTURE;  // Window has mouse captured
-
-    // Check window creation flags
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0)
-    {
-        CORE.Window.fullscreen = true;
-        flags |= SDL_WINDOW_FULLSCREEN;
-    }
-
-    //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN;
-    if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS;
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE;
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED;
-    if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED;
-
-    if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0)
-    {
-        flags &= ~SDL_WINDOW_INPUT_FOCUS;
-        flags &= ~SDL_WINDOW_MOUSE_FOCUS;
-    }
-
-    if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP;
-    if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
-
-    if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI;
-
-    //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT;     // Alternative: SDL_GL_ALPHA_SIZE = 8
-
-    //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
-
-    // NOTE: Some OpenGL context attributes must be set before window creation
-
-    // Check selection OpenGL version
-    if (rlGetVersion() == RL_OPENGL_21)
-    {
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
-    }
-    else if (rlGetVersion() == RL_OPENGL_33)
-    {
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
-    }
-    else if (rlGetVersion() == RL_OPENGL_43)
-    {
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
-#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);   // Enable OpenGL Debug Context
-#endif
-    }
-    else if (rlGetVersion() == RL_OPENGL_ES_20)                 // Request OpenGL ES 2.0 context
-    {
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
-    }
-    else if (rlGetVersion() == RL_OPENGL_ES_30)                 // Request OpenGL ES 3.0 context
-    {
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
-        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
-    }
-
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
-    {
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
-        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
-    }
-
-    // Init window
-    platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags);
-
-    // Init OpenGL context
-    platform.glContext = SDL_GL_CreateContext(platform.window);
-
-    // Check window and glContext have been initialized successfully
-    if ((platform.window != NULL) && (platform.glContext != NULL))
-    {
-        CORE.Window.ready = true;
-
-        SDL_DisplayMode displayMode = { 0 };
-        SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode);
-
-        CORE.Window.display.width = displayMode.w;
-        CORE.Window.display.height = displayMode.h;
-
-        CORE.Window.render.width = CORE.Window.screen.width;
-        CORE.Window.render.height = CORE.Window.screen.height;
-        CORE.Window.currentFbo.width = CORE.Window.render.width;
-        CORE.Window.currentFbo.height = CORE.Window.render.height;
-
-        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
-        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
-        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
-        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
-        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
-
-        if (CORE.Window.flags & FLAG_VSYNC_HINT) SDL_GL_SetSwapInterval(1);
-        else SDL_GL_SetSwapInterval(0);
-    }
-    else
-    {
-        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
-        return -1;
-    }
-
-    // Load OpenGL extensions
-    // NOTE: GL procedures address loader is required to load extensions
-    rlLoadExtensions(SDL_GL_GetProcAddress);
-    //----------------------------------------------------------------------------
-
-    // Initialize input events system
-    //----------------------------------------------------------------------------
-    // Initialize gamepads
-    for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++)
-    {
-        platform.gamepad[i] = SDL_GameControllerOpen(i);
-
-        if (platform.gamepad[i])
-        {
-            CORE.Input.Gamepad.ready[i] = true;
-            CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
-            CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
-            CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
-            strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), 63);
-            CORE.Input.Gamepad.name[i][63] = '\0';
-        }
-        else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
-    }
-
-    // Disable mouse events being interpreted as touch events
-    // NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data
-    //       Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
-    SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
-
-    SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
-    //----------------------------------------------------------------------------
-
-    // Initialize timing system
-    //----------------------------------------------------------------------------
-    // NOTE: No need to call InitTimer(), let SDL manage it internally
-    CORE.Time.previous = GetTime();     // Get time as double
-
-    #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
-    SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod()
-    #endif
-    //----------------------------------------------------------------------------
-
-    // Initialize storage system
-    //----------------------------------------------------------------------------
-    // Define base path for storage
-    CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory();
-    //----------------------------------------------------------------------------
-
-    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully");
-
-    return 0;
-}
-
-// Close platform
-void ClosePlatform(void)
-{
-    SDL_FreeCursor(platform.cursor); // Free cursor
-    SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context
-    SDL_DestroyWindow(platform.window);
-    SDL_Quit(); // Deinitialize SDL internal global state
-}
-
-// Scancode to keycode mapping
-static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode)
-{
-    if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM)
-    {
-        return mapScancodeToKey[sdlScancode];
-    }
-
-    return KEY_NULL; // No equivalent key in Raylib
-}
-// EOF
+*   CONFIGURATION:
+*       #define RCORE_PLATFORM_CUSTOM_FLAG
+*           Custom flag for rcore on target platform -not used-
+*
+*   DEPENDENCIES:
+*       - SDL 2 or SDL 3 (main library): Windowing and inputs management
+*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifdef USING_SDL3_PACKAGE
+    #define USING_SDL3_PROJECT
+#endif
+#ifndef SDL_ENABLE_OLD_NAMES
+    #define SDL_ENABLE_OLD_NAMES    // Just in case we're on SDL3, we need some in-between compatibily
+#endif
+// SDL base library (window/rendered, input, timing... functionality)
+#ifdef USING_SDL3_PROJECT
+    #include "SDL3/SDL.h"
+#elif defined(USING_SDL2_PROJECT)
+    #include "SDL2/SDL.h"
+#else
+    #include "SDL.h"
+#endif
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+  #if defined(GRAPHICS_API_OPENGL_ES2)
+      // It seems it does not need to be included to work
+      //#include "SDL_opengles2.h"
+  #else
+      // SDL OpenGL functionality (if required, instead of internal renderer)
+      #ifdef USING_SDL3_PROJECT
+          #include "SDL3/SDL_opengl.h"
+      #elif defined(USING_SDL2_PROJECT)
+          #include "SDL2/SDL_opengl.h"
+      #else
+          #include "SDL_opengl.h"
+      #endif
+  #endif
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#ifndef MAX_CLIPBOARD_BUFFER_LENGTH
+    #define MAX_CLIPBOARD_BUFFER_LENGTH 1024 // Size of the clipboard buffer used on GetClipboardText()
+#endif
+
+#if ((defined(SDL_MAJOR_VERSION) && (SDL_MAJOR_VERSION == 3)) && (defined(SDL_MINOR_VERSION) && (SDL_MINOR_VERSION >= 1)))
+    #ifndef USING_VERSION_SDL3
+        #define USING_VERSION_SDL3
+    #endif
+#endif
+
+#define SCANCODE_MAPPED_NUM     232
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+    SDL_Window *window;
+    SDL_GLContext glContext;
+
+    SDL_GameController *gamepad[MAX_GAMEPADS];
+    SDL_JoystickID gamepadId[MAX_GAMEPADS]; // Joystick instance ids, they do not start from 0
+    SDL_Cursor *cursor;
+} PlatformData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
+
+static const KeyboardKey mapScancodeToKey[SCANCODE_MAPPED_NUM] = {
+    KEY_NULL,           // SDL_SCANCODE_UNKNOWN
+    0,
+    0,
+    0,
+    KEY_A,              // SDL_SCANCODE_A
+    KEY_B,              // SDL_SCANCODE_B
+    KEY_C,              // SDL_SCANCODE_C
+    KEY_D,              // SDL_SCANCODE_D
+    KEY_E,              // SDL_SCANCODE_E
+    KEY_F,              // SDL_SCANCODE_F
+    KEY_G,              // SDL_SCANCODE_G
+    KEY_H,              // SDL_SCANCODE_H
+    KEY_I,              // SDL_SCANCODE_I
+    KEY_J,              // SDL_SCANCODE_J
+    KEY_K,              // SDL_SCANCODE_K
+    KEY_L,              // SDL_SCANCODE_L
+    KEY_M,              // SDL_SCANCODE_M
+    KEY_N,              // SDL_SCANCODE_N
+    KEY_O,              // SDL_SCANCODE_O
+    KEY_P,              // SDL_SCANCODE_P
+    KEY_Q,              // SDL_SCANCODE_Q
+    KEY_R,              // SDL_SCANCODE_R
+    KEY_S,              // SDL_SCANCODE_S
+    KEY_T,              // SDL_SCANCODE_T
+    KEY_U,              // SDL_SCANCODE_U
+    KEY_V,              // SDL_SCANCODE_V
+    KEY_W,              // SDL_SCANCODE_W
+    KEY_X,              // SDL_SCANCODE_X
+    KEY_Y,              // SDL_SCANCODE_Y
+    KEY_Z,              // SDL_SCANCODE_Z
+    KEY_ONE,            // SDL_SCANCODE_1
+    KEY_TWO,            // SDL_SCANCODE_2
+    KEY_THREE,          // SDL_SCANCODE_3
+    KEY_FOUR,           // SDL_SCANCODE_4
+    KEY_FIVE,           // SDL_SCANCODE_5
+    KEY_SIX,            // SDL_SCANCODE_6
+    KEY_SEVEN,          // SDL_SCANCODE_7
+    KEY_EIGHT,          // SDL_SCANCODE_8
+    KEY_NINE,           // SDL_SCANCODE_9
+    KEY_ZERO,           // SDL_SCANCODE_0
+    KEY_ENTER,          // SDL_SCANCODE_RETURN
+    KEY_ESCAPE,         // SDL_SCANCODE_ESCAPE
+    KEY_BACKSPACE,      // SDL_SCANCODE_BACKSPACE
+    KEY_TAB,            // SDL_SCANCODE_TAB
+    KEY_SPACE,          // SDL_SCANCODE_SPACE
+    KEY_MINUS,          // SDL_SCANCODE_MINUS
+    KEY_EQUAL,          // SDL_SCANCODE_EQUALS
+    KEY_LEFT_BRACKET,   // SDL_SCANCODE_LEFTBRACKET
+    KEY_RIGHT_BRACKET,  // SDL_SCANCODE_RIGHTBRACKET
+    KEY_BACKSLASH,      // SDL_SCANCODE_BACKSLASH
+    0,                  // SDL_SCANCODE_NONUSHASH
+    KEY_SEMICOLON,      // SDL_SCANCODE_SEMICOLON
+    KEY_APOSTROPHE,     // SDL_SCANCODE_APOSTROPHE
+    KEY_GRAVE,          // SDL_SCANCODE_GRAVE
+    KEY_COMMA,          // SDL_SCANCODE_COMMA
+    KEY_PERIOD,         // SDL_SCANCODE_PERIOD
+    KEY_SLASH,          // SDL_SCANCODE_SLASH
+    KEY_CAPS_LOCK,      // SDL_SCANCODE_CAPSLOCK
+    KEY_F1,             // SDL_SCANCODE_F1
+    KEY_F2,             // SDL_SCANCODE_F2
+    KEY_F3,             // SDL_SCANCODE_F3
+    KEY_F4,             // SDL_SCANCODE_F4
+    KEY_F5,             // SDL_SCANCODE_F5
+    KEY_F6,             // SDL_SCANCODE_F6
+    KEY_F7,             // SDL_SCANCODE_F7
+    KEY_F8,             // SDL_SCANCODE_F8
+    KEY_F9,             // SDL_SCANCODE_F9
+    KEY_F10,            // SDL_SCANCODE_F10
+    KEY_F11,            // SDL_SCANCODE_F11
+    KEY_F12,            // SDL_SCANCODE_F12
+    KEY_PRINT_SCREEN,   // SDL_SCANCODE_PRINTSCREEN
+    KEY_SCROLL_LOCK,    // SDL_SCANCODE_SCROLLLOCK
+    KEY_PAUSE,          // SDL_SCANCODE_PAUSE
+    KEY_INSERT,         // SDL_SCANCODE_INSERT
+    KEY_HOME,           // SDL_SCANCODE_HOME
+    KEY_PAGE_UP,        // SDL_SCANCODE_PAGEUP
+    KEY_DELETE,         // SDL_SCANCODE_DELETE
+    KEY_END,            // SDL_SCANCODE_END
+    KEY_PAGE_DOWN,      // SDL_SCANCODE_PAGEDOWN
+    KEY_RIGHT,          // SDL_SCANCODE_RIGHT
+    KEY_LEFT,           // SDL_SCANCODE_LEFT
+    KEY_DOWN,           // SDL_SCANCODE_DOWN
+    KEY_UP,             // SDL_SCANCODE_UP
+    KEY_NUM_LOCK,       // SDL_SCANCODE_NUMLOCKCLEAR
+    KEY_KP_DIVIDE,      // SDL_SCANCODE_KP_DIVIDE
+    KEY_KP_MULTIPLY,    // SDL_SCANCODE_KP_MULTIPLY
+    KEY_KP_SUBTRACT,    // SDL_SCANCODE_KP_MINUS
+    KEY_KP_ADD,         // SDL_SCANCODE_KP_PLUS
+    KEY_KP_ENTER,       // SDL_SCANCODE_KP_ENTER
+    KEY_KP_1,           // SDL_SCANCODE_KP_1
+    KEY_KP_2,           // SDL_SCANCODE_KP_2
+    KEY_KP_3,           // SDL_SCANCODE_KP_3
+    KEY_KP_4,           // SDL_SCANCODE_KP_4
+    KEY_KP_5,           // SDL_SCANCODE_KP_5
+    KEY_KP_6,           // SDL_SCANCODE_KP_6
+    KEY_KP_7,           // SDL_SCANCODE_KP_7
+    KEY_KP_8,           // SDL_SCANCODE_KP_8
+    KEY_KP_9,           // SDL_SCANCODE_KP_9
+    KEY_KP_0,           // SDL_SCANCODE_KP_0
+    KEY_KP_DECIMAL,     // SDL_SCANCODE_KP_PERIOD
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0,
+    KEY_LEFT_CONTROL,   //SDL_SCANCODE_LCTRL
+    KEY_LEFT_SHIFT,     //SDL_SCANCODE_LSHIFT
+    KEY_LEFT_ALT,       //SDL_SCANCODE_LALT
+    KEY_LEFT_SUPER,     //SDL_SCANCODE_LGUI
+    KEY_RIGHT_CONTROL,  //SDL_SCANCODE_RCTRL
+    KEY_RIGHT_SHIFT,    //SDL_SCANCODE_RSHIFT
+    KEY_RIGHT_ALT,      //SDL_SCANCODE_RALT
+    KEY_RIGHT_SUPER     //SDL_SCANCODE_RGUI
+};
+
+static const int CursorsLUT[] = {
+    SDL_SYSTEM_CURSOR_ARROW,       // 0  MOUSE_CURSOR_DEFAULT
+    SDL_SYSTEM_CURSOR_ARROW,       // 1  MOUSE_CURSOR_ARROW
+    SDL_SYSTEM_CURSOR_IBEAM,       // 2  MOUSE_CURSOR_IBEAM
+    SDL_SYSTEM_CURSOR_CROSSHAIR,   // 3  MOUSE_CURSOR_CROSSHAIR
+    SDL_SYSTEM_CURSOR_HAND,        // 4  MOUSE_CURSOR_POINTING_HAND
+    SDL_SYSTEM_CURSOR_SIZEWE,      // 5  MOUSE_CURSOR_RESIZE_EW
+    SDL_SYSTEM_CURSOR_SIZENS,      // 6  MOUSE_CURSOR_RESIZE_NS
+    SDL_SYSTEM_CURSOR_SIZENWSE,    // 7  MOUSE_CURSOR_RESIZE_NWSE
+    SDL_SYSTEM_CURSOR_SIZENESW,    // 8  MOUSE_CURSOR_RESIZE_NESW
+    SDL_SYSTEM_CURSOR_SIZEALL,     // 9  MOUSE_CURSOR_RESIZE_ALL
+    SDL_SYSTEM_CURSOR_NO           // 10 MOUSE_CURSOR_NOT_ALLOWED
+    //SDL_SYSTEM_CURSOR_WAIT,      // No equivalent implemented on MouseCursor enum on raylib.h
+    //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h
+};
+
+// SDL3 Migration Layer made to avoid 'ifdefs' inside functions when we can
+#if defined(USING_VERSION_SDL3)
+
+// SDL3 Migration:
+//     SDL_WINDOW_FULLSCREEN_DESKTOP has been removed,
+//     and you can call SDL_GetWindowFullscreenMode()
+//     to see whether an exclusive fullscreen mode will be used
+//     or the borderless fullscreen desktop mode will be used
+#define SDL_WINDOW_FULLSCREEN_DESKTOP SDL_WINDOW_FULLSCREEN
+
+#define SDL_IGNORE  false
+#define SDL_DISABLE false
+#define SDL_ENABLE  true
+
+// SDL3 Migration: SDL_INIT_TIMER - no longer needed before calling SDL_AddTimer()
+#define SDL_INIT_TIMER 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|)
+
+// SDL3 Migration: The SDL_WINDOW_SHOWN flag has been removed. Windows are shown by default and can be created hidden by using the SDL_WINDOW_HIDDEN flag
+#define SDL_WINDOW_SHOWN 0x0 // It's a flag, so no problem in setting it to zero if we use in a bitor (|)
+
+// SDL3 Migration: Renamed
+// IMPORTANT: Might need to call SDL_CleanupEvent somewhere see :https://github.com/libsdl-org/SDL/issues/3540#issuecomment-1793449852
+#define SDL_DROPFILE  SDL_EVENT_DROP_FILE
+
+// SDL2 implementation for SDL3 function
+const char *SDL_GameControllerNameForIndex(int joystickIndex)
+{
+    // NOTE: SDL3 uses the IDs itself (SDL_JoystickID) instead of SDL2 joystick_index
+    const char *name = NULL;
+    int numJoysticks = 0;
+    SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks);
+
+    if (joysticks)
+    {
+        if (joystickIndex < numJoysticks)
+        {
+            SDL_JoystickID instance_id = joysticks[joystickIndex];
+            name = SDL_GetGamepadNameForID(instance_id);
+        }
+
+        SDL_free(joysticks);
+    }
+
+    return name;
+}
+
+int SDL_GetNumVideoDisplays(void)
+{
+    int monitorCount = 0;
+    SDL_DisplayID *displays = SDL_GetDisplays(&monitorCount);
+
+    // Safe because If 'mem' is NULL, SDL_free does nothing
+    SDL_free(displays);
+
+    return monitorCount;
+}
+
+// SLD3 Migration: To emulate SDL2 this function should return 'SDL_DISABLE' or 'SDL_ENABLE'
+// representing the *processing state* of the event before this function makes any changes to it
+Uint8 SDL_EventState(Uint32 type, int state)
+{
+    Uint8 stateBefore = SDL_EventEnabled(type);
+
+    switch (state)
+    {
+        case SDL_DISABLE: SDL_SetEventEnabled(type, false); break;
+        case SDL_ENABLE: SDL_SetEventEnabled(type, true); break;
+        default: TRACELOG(LOG_WARNING, "SDL: Event state of unknow type");
+    }
+
+    return stateBefore;
+}
+
+void SDL_GetCurrentDisplayMode_Adapter(SDL_DisplayID displayID, SDL_DisplayMode* mode)
+{
+    const SDL_DisplayMode *currentMode = SDL_GetCurrentDisplayMode(displayID);
+
+    if (currentMode == NULL) TRACELOG(LOG_WARNING, "SDL: No possible to get current display mode");
+    else *mode = *currentMode;
+}
+
+// SDL3 Migration: Renamed
+#define SDL_GetCurrentDisplayMode SDL_GetCurrentDisplayMode_Adapter
+
+SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
+{
+    return SDL_CreateSurface(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask));
+}
+
+// SDL3 Migration:
+//     SDL_GetDisplayDPI() -
+//     not reliable across platforms, approximately replaced by multiplying
+//     SDL_GetWindowDisplayScale() times 160 on iPhone and Android, and 96 on other platforms
+// returns 0 on success or a negative error code on failure
+int SDL_GetDisplayDPI(int displayIndex, float *ddpi, float *hdpi, float *vdpi)
+{
+    float dpi = SDL_GetWindowDisplayScale(platform.window)*96.0;
+
+    if (ddpi != NULL) *ddpi = dpi;
+    if (hdpi != NULL) *hdpi = dpi;
+    if (vdpi != NULL) *vdpi = dpi;
+
+    return 0;
+}
+
+SDL_Surface *SDL_CreateRGBSurfaceWithFormat(Uint32 flags, int width, int height, int depth, Uint32 format)
+{
+    return SDL_CreateSurface(width, height, format);
+}
+
+SDL_Surface *SDL_CreateRGBSurfaceFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask)
+{
+    return SDL_CreateSurfaceFrom(width, height, SDL_GetPixelFormatForMasks(depth, Rmask, Gmask, Bmask, Amask), pixels, pitch);
+}
+
+SDL_Surface *SDL_CreateRGBSurfaceWithFormatFrom(void *pixels, int width, int height, int depth, int pitch, Uint32 format)
+{
+    return SDL_CreateSurfaceFrom(width, height, format, pixels, pitch);
+}
+
+int SDL_NumJoysticks(void)
+{
+    int numJoysticks;
+    SDL_JoystickID *joysticks = SDL_GetJoysticks(&numJoysticks);
+    SDL_free(joysticks);
+    return numJoysticks;
+}
+
+// SDL_SetRelativeMouseMode
+// returns 0 on success or a negative error code on failure
+// If relative mode is not supported, this returns -1
+int SDL_SetRelativeMouseMode_Adapter(SDL_bool enabled)
+{
+    // SDL_SetWindowRelativeMouseMode(SDL_Window *window, bool enabled)
+    // \returns true on success or false on failure; call SDL_GetError() for more
+    if (SDL_SetWindowRelativeMouseMode(platform.window, enabled))
+    {
+        return 0; // success
+    }
+    else
+    {
+        return -1; // failure
+    }
+}
+
+#define SDL_SetRelativeMouseMode SDL_SetRelativeMouseMode_Adapter
+
+bool SDL_GetRelativeMouseMode_Adapter(void)
+{
+    return SDL_GetWindowRelativeMouseMode(platform.window);
+}
+
+#define SDL_GetRelativeMouseMode SDL_GetRelativeMouseMode_Adapter
+
+int SDL_GetNumTouchFingers(SDL_TouchID touchID)
+{
+    // SDL_Finger **SDL_GetTouchFingers(SDL_TouchID touchID, int *count)
+    int count = 0;
+    SDL_Finger **fingers = SDL_GetTouchFingers(touchID, &count);
+    SDL_free(fingers);
+    return count;
+}
+
+#else // We're on SDL2
+
+// Since SDL2 doesn't have this function we leave a stub
+// SDL_GetClipboardData function is available since SDL 3.1.3. (e.g. SDL3)
+void *SDL_GetClipboardData(const char *mime_type, size_t *size)
+{
+    TRACELOG(LOG_WARNING, "SDL: Getting clipboard data that is not text not available in SDL2");
+
+    // We could possibly implement it ourselves in this case for some easier platforms
+    return NULL;
+}
+
+#endif // USING_VERSION_SDL3
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void);                                      // Initialize platform (graphics, inputs and more)
+void ClosePlatform(void);                                    // Close platform
+
+static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode);  // Help convert SDL scancodes to raylib key
+static int GetCodepointNextSDL(const char *text, int *codepointSize); // Get next codepoint in a byte sequence and bytes processed
+static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event); // Update CORE input touch point info from SDL touch data
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+bool WindowShouldClose(void)
+{
+    if (CORE.Window.ready) return CORE.Window.shouldClose;
+    else return true;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+    const int monitor = SDL_GetWindowDisplayIndex(platform.window);
+    const int monitorCount = SDL_GetNumVideoDisplays();
+
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+        {
+            SDL_SetWindowFullscreen(platform.window, 0);
+            FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        }
+        else
+        {
+            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
+            FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        }
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+    const int monitor = SDL_GetWindowDisplayIndex(platform.window);
+    const int monitorCount = SDL_GetNumVideoDisplays();
+
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE))
+        {
+            SDL_SetWindowFullscreen(platform.window, 0);
+            FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+        }
+        else
+        {
+            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+            FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+        }
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+    SDL_MaximizeWindow(platform.window);
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+}
+
+// Set window state: minimized
+void MinimizeWindow(void)
+{
+    SDL_MinimizeWindow(platform.window);
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+}
+
+// Restore window from being minimized/maximized
+void RestoreWindow(void)
+{
+    SDL_RestoreWindow(platform.window);
+    // CORE.Window.flags will be removed on PollInputEvents()
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
+
+    FLAG_SET(CORE.Window.flags, flags);
+
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
+    {
+        SDL_GL_SetSwapInterval(1);
+    }
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
+    {
+        const int monitor = SDL_GetWindowDisplayIndex(platform.window);
+        const int monitorCount = SDL_GetNumVideoDisplays();
+
+    #if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+        if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+    #else
+        if ((monitor >= 0) && (monitor < monitorCount))
+    #endif
+        {
+            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN);
+            FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        }
+        else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
+    {
+        SDL_SetWindowResizable(platform.window, SDL_TRUE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
+    {
+        SDL_SetWindowBordered(platform.window, SDL_FALSE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
+    {
+        SDL_HideWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
+    {
+        SDL_MinimizeWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        SDL_MaximizeWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
+    {
+        // NOTE: To be able to implement this part it seems that we should
+        // do it ourselves, via 'windows.h', 'X11/Xlib.h' or even 'Cocoa.h'
+        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
+    {
+        SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
+    {
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
+    {
+        // NOTE: Such a function does not seem to exist
+        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
+    {
+        //SDL_SetWindowGrab(platform.window, SDL_FALSE);
+        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        const int monitor = SDL_GetWindowDisplayIndex(platform.window);
+        const int monitorCount = SDL_GetNumVideoDisplays();
+
+    #if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+        if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+    #else
+        if ((monitor >= 0) && (monitor < monitorCount))
+    #endif
+        {
+            SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+        }
+        else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+    }
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
+    {
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling
+    }
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
+    }
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+    FLAG_CLEAR(CORE.Window.flags, flags);
+
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
+    {
+        SDL_GL_SetSwapInterval(0);
+    }
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
+    {
+        SDL_SetWindowFullscreen(platform.window, 0);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
+    {
+        SDL_SetWindowResizable(platform.window, SDL_FALSE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
+    {
+        SDL_SetWindowBordered(platform.window, SDL_TRUE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
+    {
+        SDL_ShowWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
+    {
+        SDL_RestoreWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        SDL_RestoreWindow(platform.window);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
+    {
+        //SDL_RaiseWindow(platform.window);
+        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
+    {
+        SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE);
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
+    {
+        // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled
+        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
+    {
+        //SDL_SetWindowGrab(platform.window, SDL_TRUE);
+        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL");
+    }
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        SDL_SetWindowFullscreen(platform.window, 0);
+    }
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
+    {
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers
+        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling
+    }
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL");
+    }
+}
+
+// Set icon for window
+void SetWindowIcon(Image image)
+{
+    SDL_Surface *iconSurface = NULL;
+
+    unsigned int rmask = 0, gmask = 0, bmask = 0, amask = 0;
+    int depth = 0;  // Depth in bits
+    int pitch = 0;  // Pixel spacing (pitch) in bytes
+
+    switch (image.format)
+    {
+        case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
+        {
+            rmask = 0xFF, gmask = 0;
+            bmask = 0, amask = 0;
+            depth = 8, pitch = image.width;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
+        {
+            rmask = 0xFF, gmask = 0xFF00;
+            bmask = 0, amask = 0;
+            depth = 16, pitch = image.width*2;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
+        {
+            rmask = 0xF800, gmask = 0x07E0;
+            bmask = 0x001F, amask = 0;
+            depth = 16, pitch = image.width*2;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
+        {
+            // WARNING: SDL2 could be using BGR but SDL3 RGB
+            rmask = 0xFF0000, gmask = 0x00FF00;
+            bmask = 0x0000FF, amask = 0;
+            depth = 24, pitch = image.width*3;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
+        {
+            rmask = 0xF800, gmask = 0x07C0;
+            bmask = 0x003E, amask = 0x0001;
+            depth = 16, pitch = image.width*2;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
+        {
+            rmask = 0xF000, gmask = 0x0F00;
+            bmask = 0x00F0, amask = 0x000F;
+            depth = 16, pitch = image.width*2;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
+        {
+            rmask = 0xFF000000, gmask = 0x00FF0000;
+            bmask = 0x0000FF00, amask = 0x000000FF;
+            depth = 32, pitch = image.width*4;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R32:
+        {
+            rmask = 0xFFFFFFFF, gmask = 0;
+            bmask = 0, amask = 0;
+            depth = 32, pitch = image.width*4;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
+        {
+            rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF;
+            bmask = 0xFFFFFFFF, amask = 0;
+            depth = 96, pitch = image.width*12;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
+        {
+            rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF;
+            bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF;
+            depth = 128, pitch = image.width*16;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R16:
+        {
+            rmask = 0xFFFF, gmask = 0;
+            bmask = 0, amask = 0;
+            depth = 16, pitch = image.width*2;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
+        {
+            rmask = 0xFFFF, gmask = 0xFFFF;
+            bmask = 0xFFFF, amask = 0;
+            depth = 48, pitch = image.width*6;
+        } break;
+        case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
+        {
+            rmask = 0xFFFF, gmask = 0xFFFF;
+            bmask = 0xFFFF, amask = 0xFFFF;
+            depth = 64, pitch = image.width*8;
+        } break;
+        default: return; // Compressed formats are not supported
+    }
+
+    iconSurface = SDL_CreateRGBSurfaceFrom( image.data, image.width, image.height, depth, pitch, rmask, gmask, bmask, amask );
+
+    if (iconSurface)
+    {
+        SDL_SetWindowIcon(platform.window, iconSurface);
+        SDL_FreeSurface(iconSurface);
+    }
+}
+
+// Set icon for window
+void SetWindowIcons(Image *images, int count)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
+}
+
+// Set title for window
+void SetWindowTitle(const char *title)
+{
+    SDL_SetWindowTitle(platform.window, title);
+
+    CORE.Window.title = title;
+}
+
+// Set window position on screen (windowed mode)
+void SetWindowPosition(int x, int y)
+{
+    SDL_SetWindowPosition(platform.window, x, y);
+
+    CORE.Window.position.x = x;
+    CORE.Window.position.y = y;
+}
+
+// Set monitor for the current window
+void SetWindowMonitor(int monitor)
+{
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        // NOTE:
+        // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3,
+        //    see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba
+        // 2. A workaround for SDL2 is leaving fullscreen, moving the window, then entering full screen again
+        const bool wasFullscreen = (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))? true : false;
+
+        const int screenWidth = CORE.Window.screen.width;
+        const int screenHeight = CORE.Window.screen.height;
+        SDL_Rect usableBounds;
+
+    #if defined(USING_VERSION_SDL3) // Different style for success checking
+        if (SDL_GetDisplayUsableBounds(monitor, &usableBounds))
+    #else
+        if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0)
+    #endif
+        {
+            if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen
+
+            // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it
+            if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h))
+            {
+                // NOTE:
+                // 1. There's a known issue where if the window larger than the target display bounds,
+                //    when moving the windows to that display, the window could be clipped back
+                //    ending up positioned partly outside the target display
+                // 2. The workaround for that is, previously to moving the window,
+                //    setting the window size to the target display size, so they match
+                // 3. It wasn't done here because we can't assume changing the window size automatically
+                //    is acceptable behavior by the user
+                SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y);
+                CORE.Window.position.x = usableBounds.x;
+                CORE.Window.position.y = usableBounds.y;
+            }
+            else
+            {
+                const int x = usableBounds.x + (usableBounds.w/2) - (screenWidth/2);
+                const int y = usableBounds.y + (usableBounds.h/2) - (screenHeight/2);
+                SDL_SetWindowPosition(platform.window, x, y);
+                CORE.Window.position.x = x;
+                CORE.Window.position.y = y;
+            }
+
+            if (wasFullscreen == 1) ToggleFullscreen(); // Re-enter fullscreen
+        }
+        else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds");
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+}
+
+// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMinSize(int width, int height)
+{
+    SDL_SetWindowMinimumSize(platform.window, width, height);
+
+    CORE.Window.screenMin.width = width;
+    CORE.Window.screenMin.height = height;
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+    SDL_SetWindowMaximumSize(platform.window, width, height);
+
+    CORE.Window.screenMax.width = width;
+    CORE.Window.screenMax.height = height;
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+    SDL_SetWindowSize(platform.window, width, height);
+
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+    if (opacity >= 1.0f) opacity = 1.0f;
+    else if (opacity <= 0.0f) opacity = 0.0f;
+
+    SDL_SetWindowOpacity(platform.window, opacity);
+}
+
+// Set window focused
+void SetWindowFocused(void)
+{
+    SDL_RaiseWindow(platform.window);
+}
+
+// Get native window handle
+void *GetWindowHandle(void)
+{
+    return (void *)platform.window;
+}
+
+// Get number of monitors
+int GetMonitorCount(void)
+{
+    int monitorCount = 0;
+
+    monitorCount = SDL_GetNumVideoDisplays();
+
+    return monitorCount;
+}
+
+// Get current monitor where window is placed
+int GetCurrentMonitor(void)
+{
+    int currentMonitor = 0;
+
+    // Be aware that this returns an ID in SDL3 and a Index in SDL2
+    currentMonitor = SDL_GetWindowDisplayIndex(platform.window);
+
+    return currentMonitor;
+}
+
+// Get selected monitor position
+Vector2 GetMonitorPosition(int monitor)
+{
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        SDL_Rect displayBounds;
+
+    #if defined(USING_VERSION_SDL3)
+        if (SDL_GetDisplayUsableBounds(monitor, &displayBounds))
+    #else
+        if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0)
+    #endif
+        {
+            return (Vector2){ (float)displayBounds.x, (float)displayBounds.y };
+        }
+        else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds");
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+    return (Vector2){ 0.0f, 0.0f };
+}
+
+// Get selected monitor width (currently used by monitor)
+int GetMonitorWidth(int monitor)
+{
+    int width = 0;
+
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        SDL_DisplayMode mode;
+        SDL_GetCurrentDisplayMode(monitor, &mode);
+        width = mode.w;
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return width;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+    int height = 0;
+
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        SDL_DisplayMode mode;
+        SDL_GetCurrentDisplayMode(monitor, &mode);
+        height = mode.h;
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return height;
+}
+
+// Get selected monitor physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+    int width = 0;
+
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        float ddpi = 0.0f;
+        SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL);
+        SDL_DisplayMode mode;
+        SDL_GetCurrentDisplayMode(monitor, &mode);
+        // Calculate size on inches, then convert to millimeter
+        if (ddpi > 0.0f) width = (int)((mode.w/ddpi)*25.4f);
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return width;
+}
+
+// Get selected monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+    int height = 0;
+
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        float ddpi = 0.0f;
+        SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL);
+        SDL_DisplayMode mode;
+        SDL_GetCurrentDisplayMode(monitor, &mode);
+        // Calculate size on inches, then convert to millimeter
+        if (ddpi > 0.0f) height = (int)((mode.h/ddpi)*25.4f);
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return height;
+}
+
+// Get selected monitor refresh rate
+int GetMonitorRefreshRate(int monitor)
+{
+    int refresh = 0;
+
+    const int monitorCount = SDL_GetNumVideoDisplays();
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        SDL_DisplayMode mode;
+        SDL_GetCurrentDisplayMode(monitor, &mode);
+        refresh = mode.refresh_rate;
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return refresh;
+}
+
+// Get the human-readable, UTF-8 encoded name of the selected monitor
+const char *GetMonitorName(int monitor)
+{
+    const int monitorCount = SDL_GetNumVideoDisplays();
+
+#if defined(USING_VERSION_SDL3) // SDL3 Migration: Monitor is an id instead of index now, returns 0 on failure
+    if (SDL_GetDisplayProperties(monitor) != 0) // Returns 0 on failure, so a value other than zero indicates that the monitor id is valid
+#else
+    if ((monitor >= 0) && (monitor < monitorCount))
+#endif
+    {
+        return SDL_GetDisplayName(monitor);
+    }
+    else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor");
+
+    return "";
+}
+
+// Get window position XY on monitor
+Vector2 GetWindowPosition(void)
+{
+    int x = 0;
+    int y = 0;
+
+    SDL_GetWindowPosition(platform.window, &x, &y);
+
+    return (Vector2){ (float)x, (float)y };
+}
+
+// Get window scale DPI factor for current monitor
+Vector2 GetWindowScaleDPI(void)
+{
+    Vector2 scale = { 1.0f, 1.0f };
+
+#if defined(USING_VERSION_SDL3)
+    // NOTE: SDL_GetWindowDisplayScale added on SDL3
+    // REF: https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale
+    scale.x = SDL_GetWindowDisplayScale(platform.window);
+    scale.y = scale.x;
+#else
+    // NOTE: SDL_GetWindowDisplayScale not available on SDL2
+    // TODO: Implement the window scale factor calculation manually
+    TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
+#endif
+
+    return scale;
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+    SDL_SetClipboardText(text);
+}
+
+// Get clipboard text content
+const char *GetClipboardText(void)
+{
+    static char buffer[MAX_CLIPBOARD_BUFFER_LENGTH] = { 0 };
+
+    char *clipboard = SDL_GetClipboardText();
+
+    int clipboardSize = snprintf(buffer, sizeof(buffer), "%s", clipboard);
+    if (clipboardSize >= MAX_CLIPBOARD_BUFFER_LENGTH)
+    {
+        char *truncate = buffer + MAX_CLIPBOARD_BUFFER_LENGTH - 4;
+        sprintf(truncate, "...");
+    }
+
+    SDL_free(clipboard);
+
+    return buffer;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+    // Let's hope compiler put these arrays in static memory
+    const char *imageFormats[] = {
+        "image/bmp",
+        "image/png",
+        "image/jpg",
+        "image/tiff",
+    };
+    const char *imageExtensions[] = {
+        ".bmp",
+        ".png",
+        ".jpg",
+        ".tiff",
+    };
+
+    size_t dataSize = 0;
+    void  *fileData = NULL;
+
+    for (int i = 0; i < SDL_arraysize(imageFormats); i++)
+    {
+        // NOTE: This pointer should be free with SDL_free() at some point
+        fileData = SDL_GetClipboardData(imageFormats[i], &dataSize);
+
+        if (fileData)
+        {
+            image = LoadImageFromMemory(imageExtensions[i], fileData, (int)dataSize);
+            if (IsImageValid(image))
+            {
+                TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]);
+                return image;
+            }
+        }
+    }
+
+    if (!IsImageValid(image)) TRACELOG(LOG_WARNING, "Clipboard: Couldn't get clipboard data. ERROR: %s", SDL_GetError());
+#endif
+
+    return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+#if defined(USING_VERSION_SDL3)
+    SDL_ShowCursor();
+#else
+    SDL_ShowCursor(SDL_ENABLE);
+#endif
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+#if defined(USING_VERSION_SDL3)
+    SDL_HideCursor();
+#else
+    SDL_ShowCursor(SDL_DISABLE);
+#endif
+    CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+    SDL_SetRelativeMouseMode(SDL_FALSE);
+
+    ShowCursor();
+    CORE.Input.Mouse.cursorLocked = false;
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+    SDL_SetRelativeMouseMode(SDL_TRUE);
+
+    HideCursor();
+    CORE.Input.Mouse.cursorLocked = true;
+}
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // NOTE: We use a preprocessor condition here because `rlCopyFramebuffer` is only declared for software rendering
+    SDL_Surface *surface = SDL_GetWindowSurface(platform.window);
+    rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, surface->pixels);
+    SDL_UpdateWindowSurface(platform.window);
+#else
+    SDL_GL_SwapWindow(platform.window);
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds
+double GetTime(void)
+{
+    unsigned int ms = SDL_GetTicks();    // Elapsed time in milliseconds since SDL_Init()
+    double time = (double)ms/1000;
+    return time;
+}
+
+// Open URL with default system browser (if available)
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
+// REF: https://github.com/raysan5/raylib/issues/686
+void OpenURL(const char *url)
+{
+    // Security check to (partially) avoid malicious code
+    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
+    else SDL_OpenURL(url);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+    return SDL_GameControllerAddMapping(mappings);
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f))
+    {
+        if (leftMotor < 0.0f) leftMotor = 0.0f;
+        if (leftMotor > 1.0f) leftMotor = 1.0f;
+        if (rightMotor < 0.0f) rightMotor = 0.0f;
+        if (rightMotor > 1.0f) rightMotor = 1.0f;
+        if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME;
+
+        SDL_GameControllerRumble(platform.gamepad[gamepad], (Uint16)(leftMotor*65535.0f), (Uint16)(rightMotor*65535.0f), (Uint32)(duration*1000.0f));
+    }
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+    SDL_WarpMouseInWindow(platform.window, x, y);
+
+    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+    platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]);
+    SDL_SetCursor(platform.cursor);
+
+    CORE.Input.Mouse.cursor = cursor;
+}
+
+// Get physical key name
+const char *GetKeyName(int key)
+{
+    return SDL_GetKeyName(key);
+}
+
+// Register all input events
+void PollInputEvents(void)
+{
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    // NOTE: Gestures update must be called every frame to reset gestures correctly
+    // because ProcessGestureEvent() is just called on an event, not every frame
+    UpdateGestures();
+#endif
+
+    // Reset keys/chars pressed registered
+    CORE.Input.Keyboard.keyPressedQueueCount = 0;
+    CORE.Input.Keyboard.charPressedQueueCount = 0;
+
+    // Reset mouse wheel
+    CORE.Input.Mouse.currentWheelMove.x = 0;
+    CORE.Input.Mouse.currentWheelMove.y = 0;
+
+    // Register previous mouse position
+    if (CORE.Input.Mouse.cursorLocked) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
+    else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+
+    // Reset last gamepad button/axis registered state
+    for (int i = 0; (i < SDL_NumJoysticks()) && (i < MAX_GAMEPADS); i++)
+    {
+        // Check if gamepad is available
+        if (CORE.Input.Gamepad.ready[i])
+        {
+            // Register previous gamepad button states
+            for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++)
+            {
+                CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
+            }
+        }
+    }
+
+    // Register previous touch states
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
+
+    // Map touch position to mouse position for convenience
+    if (CORE.Input.Touch.pointCount == 0) CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
+
+    int touchAction = -1;       // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
+    bool realTouch = false;     // Flag to differentiate real touch gestures from mouse ones
+
+    // Register previous keys states
+    // NOTE: Android supports up to 260 keys
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
+    {
+        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    // Register previous mouse states
+    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
+
+    // Poll input events for current platform
+    //-----------------------------------------------------------------------------
+    // WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values
+    //const Uint8 *keys = SDL_GetKeyboardState(NULL);
+    //for (int i = 0; i < 256; i++) CORE.Input.Keyboard.currentKeyState[i] = keys[i];
+
+    CORE.Window.resizedLastFrame = false;
+
+    if ((CORE.Window.eventWaiting) || (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_ALWAYS_RUN)))
+    {
+        SDL_WaitEvent(NULL);
+        CORE.Time.previous = GetTime();
+    }
+
+    SDL_Event event = { 0 };
+    while (SDL_PollEvent(&event) != 0)
+    {
+        // All input events can be processed after polling
+        switch (event.type)
+        {
+            case SDL_QUIT: CORE.Window.shouldClose = true; break;
+
+            case SDL_DROPFILE:      // Dropped file
+            {
+                if (CORE.Window.dropFileCount == 0)
+                {
+                    // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files
+                    // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed
+                    // TODO: Pointers should probably be reallocated for any new file added...
+                    CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *));
+
+                    CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
+
+                #if defined(USING_VERSION_SDL3)
+                    // const char *data;   // The text for SDL_EVENT_DROP_TEXT and the file name for SDL_EVENT_DROP_FILE, NULL for other events
+                    // Event memory is now managed by SDL, so you should not free the data in SDL_EVENT_DROP_FILE,
+                    // and if you want to hold onto the text in SDL_EVENT_TEXT_EDITING and SDL_EVENT_TEXT_INPUT events,
+                    // you should make a copy of it. SDL_TEXTINPUTEVENT_TEXT_SIZE is no longer necessary and has been removed
+                    strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
+                #else
+                    strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
+                    SDL_free(event.drop.file);
+                #endif
+
+                    CORE.Window.dropFileCount++;
+                }
+                else if (CORE.Window.dropFileCount < 1024)
+                {
+                    CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
+
+                #if defined(USING_VERSION_SDL3)
+                    strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.data, MAX_FILEPATH_LENGTH - 1);
+                #else
+                    strncpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file, MAX_FILEPATH_LENGTH - 1);
+                    SDL_free(event.drop.file);
+                #endif
+
+                    CORE.Window.dropFileCount++;
+                }
+                else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!");
+
+            } break;
+
+            // Window events are also polled (minimized, maximized, close...)
+
+            #ifndef USING_VERSION_SDL3
+            // SDL3 states:
+            // The SDL_WINDOWEVENT_* events have been moved to top level events, and SDL_WINDOWEVENT has been removed
+            // In general, handling this change just means checking for the individual events instead of first checking for SDL_WINDOWEVENT
+            // and then checking for window events. You can compare the event >= SDL_EVENT_WINDOW_FIRST and <= SDL_EVENT_WINDOW_LAST if you need to see whether it's a window event
+            case SDL_WINDOWEVENT:
+            {
+                switch (event.window.event)
+                {
+            #endif
+                    case SDL_WINDOWEVENT_RESIZED:
+                    case SDL_WINDOWEVENT_SIZE_CHANGED:
+                    {
+                        const int width = event.window.data1;
+                        const int height = event.window.data2;
+                        SetupViewport(width, height);
+                        // if we are doing automatic DPI scaling, then the "screen" size is divided by the window scale
+                        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+                        {
+                            CORE.Window.screen.width = (int)(width/GetWindowScaleDPI().x);
+                            CORE.Window.screen.height = (int)(height/GetWindowScaleDPI().y);
+                        }
+                        else
+                        {
+                            CORE.Window.screen.width = width;
+                            CORE.Window.screen.height = height;
+                        }
+                        CORE.Window.currentFbo.width = width;
+                        CORE.Window.currentFbo.height = height;
+                        CORE.Window.resizedLastFrame = true;
+
+                        #ifndef USING_VERSION_SDL3
+                        // Manually detect if the window was maximized (due to SDL2 restore being unreliable on some platforms)
+                        // to remove the FLAG_WINDOW_MAXIMIZED accordingly
+                        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED))
+                        {
+                            int borderTop = 0;
+                            int borderLeft = 0;
+                            int borderBottom = 0;
+                            int borderRight = 0;
+                            SDL_GetWindowBordersSize(platform.window, &borderTop, &borderLeft, &borderBottom, &borderRight);
+                            SDL_Rect usableBounds;
+                            SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(platform.window), &usableBounds);
+
+                            if ((width + borderLeft + borderRight != usableBounds.w) && (height + borderTop + borderBottom != usableBounds.h)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+                        }
+                        #endif
+                    } break;
+
+                    case SDL_WINDOWEVENT_ENTER: CORE.Input.Mouse.cursorOnScreen = true; break;
+                    case SDL_WINDOWEVENT_LEAVE: CORE.Input.Mouse.cursorOnScreen = false; break;
+
+                    case SDL_WINDOWEVENT_MINIMIZED:
+                    {
+                        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+                    } break;
+                    case SDL_WINDOWEVENT_MAXIMIZED:
+                    {
+                        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+                    } break;
+                    case SDL_WINDOWEVENT_RESTORED:
+                    {
+                        if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MINIMIZED))
+                        {
+                            if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+                        }
+
+                        #ifdef USING_VERSION_SDL3
+                        if (!FLAG_IS_SET(SDL_GetWindowFlags(platform.window), SDL_WINDOW_MAXIMIZED))
+                        {
+                            if (FLAG_IS_SET(CORE.Window.flags, SDL_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, SDL_WINDOW_MAXIMIZED);
+                        }
+                        #endif
+                    } break;
+
+                    case SDL_WINDOWEVENT_HIDDEN:
+                    {
+                        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
+                    } break;
+                    case SDL_WINDOWEVENT_SHOWN:
+                    {
+                        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
+                    } break;
+
+                    case SDL_WINDOWEVENT_FOCUS_GAINED:
+                    {
+                        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
+                    } break;
+                    case SDL_WINDOWEVENT_FOCUS_LOST:
+                    {
+                        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
+                    } break;
+
+            #ifndef USING_VERSION_SDL3
+                    default: break;
+                }
+            } break;
+            #endif
+
+            // Keyboard events
+            case SDL_KEYDOWN:
+            {
+            #if defined(USING_VERSION_SDL3)
+                // SDL3 Migration: The following structures have been removed: SDL_Keysym
+                KeyboardKey key = ConvertScancodeToKey(event.key.scancode);
+            #else
+                KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
+            #endif
+
+                if (key != KEY_NULL)
+                {
+                    // If key was up, add it to the key pressed queue
+                    if ((CORE.Input.Keyboard.currentKeyState[key] == 0) && (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE))
+                    {
+                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key;
+                        CORE.Input.Keyboard.keyPressedQueueCount++;
+                    }
+
+                    CORE.Input.Keyboard.currentKeyState[key] = 1;
+                }
+
+                if (event.key.repeat) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
+
+                // Check for registered exit key to request exit game loop on next iteration
+                if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) CORE.Window.shouldClose = true;
+
+            } break;
+
+            case SDL_KEYUP:
+            {
+
+            #if defined(USING_VERSION_SDL3)
+                KeyboardKey key = ConvertScancodeToKey(event.key.scancode);
+            #else
+                KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode);
+            #endif
+                if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0;
+            } break;
+
+            case SDL_TEXTINPUT:
+            {
+                // NOTE: event.text.text data comes an UTF-8 text sequence but we register codepoints (int)
+
+                // Check if there is space available in the queue
+                if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
+                {
+                    // Add character (codepoint) to the queue
+
+                #if defined(USING_VERSION_SDL3)
+                    size_t textLen = strlen(event.text.text);
+                    unsigned int codepoint = (unsigned int)SDL_StepUTF8(&event.text.text, &textLen);
+                #else
+                    int codepointSize = 0;
+                    int codepoint = GetCodepointNextSDL(event.text.text, &codepointSize);
+                #endif
+
+                    CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = codepoint;
+                    CORE.Input.Keyboard.charPressedQueueCount++;
+                }
+            } break;
+
+            // Check mouse events
+            case SDL_MOUSEBUTTONDOWN:
+            {
+                // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW
+                //       The following conditions align SDL with raylib.h MouseButton enum order
+                int btn = event.button.button - 1;
+                if (btn == 2) btn = 1;
+                else if (btn == 1) btn = 2;
+
+                CORE.Input.Mouse.currentButtonState[btn] = 1;
+                CORE.Input.Touch.currentTouchState[btn] = 1;
+
+                touchAction = 1;
+            } break;
+            case SDL_MOUSEBUTTONUP:
+            {
+                // NOTE: SDL2 mouse button order is LEFT, MIDDLE, RIGHT, but raylib uses LEFT, RIGHT, MIDDLE like GLFW
+                //       The following conditions align SDL with raylib.h MouseButton enum order
+                int btn = event.button.button - 1;
+                if (btn == 2) btn = 1;
+                else if (btn == 1) btn = 2;
+
+                CORE.Input.Mouse.currentButtonState[btn] = 0;
+                CORE.Input.Touch.currentTouchState[btn] = 0;
+
+                touchAction = 0;
+            } break;
+            case SDL_MOUSEWHEEL:
+            {
+                CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x;
+                CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y;
+            } break;
+            case SDL_MOUSEMOTION:
+            {
+                if (CORE.Input.Mouse.cursorLocked)
+                {
+                    CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel;
+                    CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel;
+                    CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f };
+                }
+                else
+                {
+                    CORE.Input.Mouse.currentPosition.x = (float)event.motion.x;
+                    CORE.Input.Mouse.currentPosition.y = (float)event.motion.y;
+                }
+
+                CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
+                touchAction = 2;
+            } break;
+
+            case SDL_FINGERDOWN:
+            {
+                UpdateTouchPointsSDL(event.tfinger);
+                touchAction = 1;
+                realTouch = true;
+            } break;
+            case SDL_FINGERUP:
+            {
+                UpdateTouchPointsSDL(event.tfinger);
+                touchAction = 0;
+                realTouch = true;
+            } break;
+            case SDL_FINGERMOTION:
+            {
+                UpdateTouchPointsSDL(event.tfinger);
+                touchAction = 2;
+                realTouch = true;
+            } break;
+
+            // Check gamepad events
+            case SDL_JOYDEVICEADDED:
+            {
+                int jid = event.jdevice.which; // Joystick device index
+
+                // check if already added at InitPlatform
+                for (int i = 0; i < MAX_GAMEPADS; i++)
+                {
+                    if (jid == platform.gamepadId[i])
+                    {
+                        return;
+                    }
+                }
+
+                int nextAvailableSlot = 0;
+                while (nextAvailableSlot < MAX_GAMEPADS && CORE.Input.Gamepad.ready[nextAvailableSlot])
+                {
+                    ++nextAvailableSlot;
+                }
+
+                if ((nextAvailableSlot < MAX_GAMEPADS) && !CORE.Input.Gamepad.ready[nextAvailableSlot])
+                {
+                    platform.gamepad[nextAvailableSlot] = SDL_GameControllerOpen(jid);
+                    platform.gamepadId[nextAvailableSlot] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[nextAvailableSlot]));
+
+                    if (platform.gamepad[nextAvailableSlot])
+                    {
+                        CORE.Input.Gamepad.ready[nextAvailableSlot] = true;
+                        CORE.Input.Gamepad.axisCount[nextAvailableSlot] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[nextAvailableSlot]));
+                        CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+                        CORE.Input.Gamepad.axisState[nextAvailableSlot][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+                        memset(CORE.Input.Gamepad.name[nextAvailableSlot], 0, MAX_GAMEPAD_NAME_LENGTH);
+                        const char *controllerName = SDL_GameControllerNameForIndex(nextAvailableSlot);
+                        if (controllerName != NULL) strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], controllerName, MAX_GAMEPAD_NAME_LENGTH - 1);
+                        else strncpy(CORE.Input.Gamepad.name[nextAvailableSlot], "noname", 6);
+                    }
+                    else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
+                }
+            } break;
+            case SDL_JOYDEVICEREMOVED:
+            {
+                int jid = event.jdevice.which; // Joystick instance id
+
+                for (int i = 0; i < MAX_GAMEPADS; i++)
+                {
+                    if (platform.gamepadId[i] == jid)
+                    {
+                        SDL_GameControllerClose(platform.gamepad[i]);
+                        CORE.Input.Gamepad.ready[i] = false;
+                        memset(CORE.Input.Gamepad.name[i], 0, MAX_GAMEPAD_NAME_LENGTH);
+                        platform.gamepadId[i] = -1;
+                        break;
+                    }
+                }
+            } break;
+            case SDL_CONTROLLERBUTTONDOWN:
+            {
+                int button = -1;
+
+            #if defined(USING_VERSION_SDL3)
+                switch (event.gbutton.button)
+            #else
+                switch (event.jbutton.button)
+            #endif
+                {
+                    case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
+                    case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
+                    case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
+                    case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
+
+                    case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
+                    case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
+
+                    case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
+                    case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
+                    case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
+
+                    case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
+
+                    case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
+                    case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
+                    default: break;
+                }
+
+                if (button >= 0)
+                {
+                    for (int i = 0; i < MAX_GAMEPADS; i++)
+                    {
+                    #if defined(USING_VERSION_SDL3)
+                        if (platform.gamepadId[i] == event.gbutton.which)
+                    #else
+                        if (platform.gamepadId[i] == event.jbutton.which)
+                    #endif
+                        {
+                            CORE.Input.Gamepad.currentButtonState[i][button] = 1;
+                            CORE.Input.Gamepad.lastButtonPressed = button;
+                            break;
+                        }
+                    }
+                }
+            } break;
+            case SDL_CONTROLLERBUTTONUP:
+            {
+                int button = -1;
+
+            #if defined(USING_VERSION_SDL3)
+                switch (event.gbutton.button)
+            #else
+                switch (event.jbutton.button)
+            #endif
+                {
+                    case SDL_CONTROLLER_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
+                    case SDL_CONTROLLER_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
+                    case SDL_CONTROLLER_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
+                    case SDL_CONTROLLER_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
+
+                    case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
+                    case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
+
+                    case SDL_CONTROLLER_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
+                    case SDL_CONTROLLER_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break;
+                    case SDL_CONTROLLER_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
+
+                    case SDL_CONTROLLER_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
+                    case SDL_CONTROLLER_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
+
+                    case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
+                    case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
+                    default: break;
+                }
+
+                if (button >= 0)
+                {
+                    for (int i = 0; i < MAX_GAMEPADS; i++)
+                    {
+                    #if defined(USING_VERSION_SDL3)
+                        if (platform.gamepadId[i] == event.gbutton.which)
+                    #else
+                        if (platform.gamepadId[i] == event.jbutton.which)
+                    #endif
+                        {
+                            CORE.Input.Gamepad.currentButtonState[i][button] = 0;
+                            if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
+                            break;
+                        }
+                    }
+                }
+            } break;
+            case SDL_CONTROLLERAXISMOTION:
+            {
+                int axis = -1;
+
+                switch (event.jaxis.axis)
+                {
+                    case SDL_CONTROLLER_AXIS_LEFTX: axis = GAMEPAD_AXIS_LEFT_X; break;
+                    case SDL_CONTROLLER_AXIS_LEFTY: axis = GAMEPAD_AXIS_LEFT_Y; break;
+                    case SDL_CONTROLLER_AXIS_RIGHTX: axis = GAMEPAD_AXIS_RIGHT_X; break;
+                    case SDL_CONTROLLER_AXIS_RIGHTY: axis = GAMEPAD_AXIS_RIGHT_Y; break;
+                    case SDL_CONTROLLER_AXIS_TRIGGERLEFT: axis = GAMEPAD_AXIS_LEFT_TRIGGER; break;
+                    case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: axis = GAMEPAD_AXIS_RIGHT_TRIGGER; break;
+                    default: break;
+                }
+
+                if (axis >= 0)
+                {
+                    for (int i = 0; i < MAX_GAMEPADS; i++)
+                    {
+                        if (platform.gamepadId[i] == event.jaxis.which)
+                        {
+                            // SDL axis value range is -32768 to 32767, we normalize it to raylib's -1.0 to 1.0f range
+                            float value = event.jaxis.value/(float)32767;
+                            CORE.Input.Gamepad.axisState[i][axis] = value;
+
+                            // Register button state for triggers in addition to their axes
+                            if ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))
+                            {
+                                int button = (axis == GAMEPAD_AXIS_LEFT_TRIGGER)? GAMEPAD_BUTTON_LEFT_TRIGGER_2 : GAMEPAD_BUTTON_RIGHT_TRIGGER_2;
+                                int pressed = (value > 0.1f);
+                                CORE.Input.Gamepad.currentButtonState[i][button] = pressed;
+                                if (pressed) CORE.Input.Gamepad.lastButtonPressed = button;
+                                else if (CORE.Input.Gamepad.lastButtonPressed == button) CORE.Input.Gamepad.lastButtonPressed = 0;
+                            }
+                            break;
+                        }
+                    }
+                }
+            } break;
+            default: break;
+        }
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+        if (touchAction > -1)
+        {
+            // Process mouse events as touches to be able to use mouse-gestures
+            GestureEvent gestureEvent = { 0 };
+
+            // Register touch actions
+            gestureEvent.touchAction = touchAction;
+
+            // Assign a pointer ID
+            gestureEvent.pointId[0] = 0;
+
+            // Register touch points count
+            gestureEvent.pointCount = 1;
+
+            // Register touch points position, only one point registered
+            if (touchAction == 2 || realTouch) gestureEvent.position[0] = CORE.Input.Touch.position[0];
+            else gestureEvent.position[0] = GetMousePosition();
+
+            // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
+            gestureEvent.position[0].x /= (float)GetScreenWidth();
+            gestureEvent.position[0].y /= (float)GetScreenHeight();
+
+            // Gesture data is sent to gestures-system for processing
+            ProcessGestureEvent(gestureEvent);
+
+            touchAction = -1;
+        }
+#endif
+    }
+    //-----------------------------------------------------------------------------
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+    // Initialize SDL internal global state, only required systems
+    // NOTE: Not all systems need to be initialized, SDL_INIT_AUDIO is not required, managed by miniaudio
+    int result = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER);
+    if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; }
+
+    // Initialize graphic device: display/window and graphic context
+    //----------------------------------------------------------------------------
+    unsigned int flags = 0;
+    FLAG_SET(flags, SDL_WINDOW_SHOWN);
+    FLAG_SET(flags, SDL_WINDOW_INPUT_FOCUS);
+    FLAG_SET(flags, SDL_WINDOW_MOUSE_FOCUS);
+    FLAG_SET(flags, SDL_WINDOW_MOUSE_CAPTURE);  // Window has mouse captured
+
+    // Check window creation flags
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN);
+
+    //if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) FLAG_SET(flags, SDL_WINDOW_HIDDEN);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) FLAG_SET(flags, SDL_WINDOW_BORDERLESS);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) FLAG_SET(flags, SDL_WINDOW_RESIZABLE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_SET(flags, SDL_WINDOW_MINIMIZED);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_SET(flags, SDL_WINDOW_MAXIMIZED);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED))
+    {
+        FLAG_CLEAR(flags, SDL_WINDOW_INPUT_FOCUS);
+        FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_FOCUS);
+    }
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) FLAG_SET(flags, SDL_WINDOW_ALWAYS_ON_TOP);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MOUSE_PASSTHROUGH)) FLAG_CLEAR(flags, SDL_WINDOW_MOUSE_CAPTURE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) FLAG_SET(flags, SDL_WINDOW_ALLOW_HIGHDPI);
+
+    //if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TRANSPARENT)) FLAG_SET(flags, SDL_WINDOW_TRANSPARENT);     // Alternative: SDL_GL_ALPHA_SIZE = 8
+    //if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_DESKTOP)) FLAG_SET(flags, SDL_WINDOW_FULLSCREEN_DESKTOP);
+
+    // NOTE: Some OpenGL context attributes must be set before window creation
+
+    if (rlGetVersion() != RL_OPENGL_11_SOFTWARE)
+    {
+        // Add the flag telling the window to use an OpenGL context
+        flags |= SDL_WINDOW_OPENGL;
+
+        // Check selection OpenGL version
+        if (rlGetVersion() == RL_OPENGL_21)
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
+        }
+        else if (rlGetVersion() == RL_OPENGL_33)
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+        }
+        else if (rlGetVersion() == RL_OPENGL_43)
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+    #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT)
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);   // Enable OpenGL Debug Context
+    #endif
+        }
+        else if (rlGetVersion() == RL_OPENGL_ES_20)                 // Request OpenGL ES 2.0 context
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+        }
+        else if (rlGetVersion() == RL_OPENGL_ES_30)                 // Request OpenGL ES 3.0 context
+        {
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+        }
+
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
+        {
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
+            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
+        }
+    }
+
+    // Init window
+#if defined(USING_VERSION_SDL3)
+    platform.window = SDL_CreateWindow(CORE.Window.title, CORE.Window.screen.width, CORE.Window.screen.height, flags);
+#else
+    platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags);
+#endif
+
+    // Init OpenGL context
+    if (rlGetVersion() != RL_OPENGL_11_SOFTWARE)
+    {
+        platform.glContext = SDL_GL_CreateContext(platform.window);
+    }
+
+    if ((platform.window != NULL) && ((rlGetVersion() == RL_OPENGL_11_SOFTWARE) || (platform.glContext != NULL)))
+    {
+        CORE.Window.ready = true;
+
+        SDL_DisplayMode displayMode = { 0 };
+        SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode);
+
+        CORE.Window.display.width = displayMode.w;
+        CORE.Window.display.height = displayMode.h;
+
+        CORE.Window.render.width = CORE.Window.screen.width;
+        CORE.Window.render.height = CORE.Window.screen.height;
+        CORE.Window.currentFbo.width = CORE.Window.render.width;
+        CORE.Window.currentFbo.height = CORE.Window.render.height;
+
+        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
+        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+
+        if (platform.glContext != NULL)
+        {
+            SDL_GL_SetSwapInterval((FLAG_IS_SET(CORE.Window.flags, FLAG_VSYNC_HINT))? 1: 0);
+
+            // Load OpenGL extensions
+            // NOTE: GL procedures address loader is required to load extensions
+            rlLoadExtensions(SDL_GL_GetProcAddress);
+        }
+    }
+    else
+    {
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+        return -1;
+    }
+
+    //----------------------------------------------------------------------------
+
+    // Initialize input events system
+    //----------------------------------------------------------------------------
+    // Initialize gamepads
+    for (int i = 0; i < MAX_GAMEPADS; i++)
+    {
+        platform.gamepadId[i] = -1; // Set all gamepad initial instance ids as invalid to not conflict with instance id zero
+    }
+
+    int numJoysticks = SDL_NumJoysticks();
+
+    for (int i = 0; (i < numJoysticks) && (i < MAX_GAMEPADS); i++)
+    {
+        platform.gamepad[i] = SDL_GameControllerOpen(i);
+        platform.gamepadId[i] = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(platform.gamepad[i]));
+
+        if (platform.gamepad[i])
+        {
+            CORE.Input.Gamepad.ready[i] = true;
+            CORE.Input.Gamepad.axisCount[i] = SDL_JoystickNumAxes(SDL_GameControllerGetJoystick(platform.gamepad[i]));
+            CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] = -1.0f;
+            CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] = -1.0f;
+            strncpy(CORE.Input.Gamepad.name[i], SDL_GameControllerNameForIndex(i), MAX_GAMEPAD_NAME_LENGTH - 1);
+            CORE.Input.Gamepad.name[i][MAX_GAMEPAD_NAME_LENGTH - 1] = '\0';
+        }
+        else TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError());
+    }
+
+    // Disable mouse events being interpreted as touch events
+    // NOTE: This is wanted because there are SDL_FINGER* events available which provide unique data
+    // Due to the way PollInputEvents() and rgestures.h are currently implemented, setting this won't break SUPPORT_MOUSE_GESTURES
+    SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
+
+    SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
+    //----------------------------------------------------------------------------
+
+    // Initialize timing system
+    //----------------------------------------------------------------------------
+    // NOTE: No need to call InitTimer(), let SDL manage it internally
+    CORE.Time.previous = GetTime();     // Get time as double
+
+    #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP)
+    SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "1"); // SDL equivalent of timeBeginPeriod() and timeEndPeriod()
+    #endif
+    //----------------------------------------------------------------------------
+
+    // Initialize storage system
+    //----------------------------------------------------------------------------
+    // Define base path for storage
+    CORE.Storage.basePath = SDL_GetBasePath(); // Alternative: GetWorkingDirectory();
+    //----------------------------------------------------------------------------
+
+#if defined(USING_VERSION_SDL3)
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL3): Initialized successfully");
+#else
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL2): Initialized successfully");
+#endif
+
+    return 0;
+}
+
+// Close platform
+void ClosePlatform(void)
+{
+    SDL_FreeCursor(platform.cursor); // Free cursor
+    if (platform.glContext != NULL) SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context
+    SDL_DestroyWindow(platform.window);
+    SDL_Quit(); // Deinitialize SDL internal global state
+}
+
+// Scancode to keycode mapping
+static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode)
+{
+    if ((sdlScancode >= 0) && (sdlScancode < SCANCODE_MAPPED_NUM))
+    {
+        return mapScancodeToKey[sdlScancode];
+    }
+
+    return KEY_NULL; // No equivalent key in Raylib
+}
+
+// Get next codepoint in a byte sequence and bytes processed
+static int GetCodepointNextSDL(const char *text, int *codepointSize)
+{
+    const char *ptr = text;
+    int codepoint = 0x3f;       // Codepoint (defaults to '?')
+    *codepointSize = 1;
+
+    // Get current codepoint and bytes processed
+    if (0xf0 == (0xf8 & ptr[0]))
+    {
+        // 4 byte UTF-8 codepoint
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks
+        codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
+        *codepointSize = 4;
+    }
+    else if (0xe0 == (0xf0 & ptr[0]))
+    {
+        // 3 byte UTF-8 codepoint */
+        if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks
+        codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
+        *codepointSize = 3;
+    }
+    else if (0xc0 == (0xe0 & ptr[0]))
+    {
+        // 2 byte UTF-8 codepoint
+        if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } // 10xxxxxx checks
+        codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
+        *codepointSize = 2;
+    }
+    else if (0x00 == (0x80 & ptr[0]))
+    {
+        // 1 byte UTF-8 codepoint
+        codepoint = ptr[0];
+        *codepointSize = 1;
+    }
+
+    return codepoint;
+}
+
+// Update CORE input touch point info from SDL touch data
+static void UpdateTouchPointsSDL(SDL_TouchFingerEvent event)
+{
+#if defined(USING_VERSION_SDL3) // SDL3
+    int count = 0;
+    SDL_Finger **fingers = SDL_GetTouchFingers(event.touchID, &count);
+    CORE.Input.Touch.pointCount = count;
+
+    for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
+    {
+        SDL_Finger *finger = fingers[i];
+        CORE.Input.Touch.pointId[i] = finger->id;
+        CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
+        CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
+        CORE.Input.Touch.currentTouchState[i] = 1;
+    }
+
+    SDL_free(fingers);
+
+#else // SDL2
+
+    CORE.Input.Touch.pointCount = SDL_GetNumTouchFingers(event.touchId);
+
+    for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
+    {
+        SDL_Finger *finger = SDL_GetTouchFinger(event.touchId, i);
+        CORE.Input.Touch.pointId[i] = (int)finger->id;
+        CORE.Input.Touch.position[i].x = finger->x*CORE.Window.screen.width;
+        CORE.Input.Touch.position[i].y = finger->y*CORE.Window.screen.height;
+        CORE.Input.Touch.currentTouchState[i] = 1;
+    }
+#endif
+
+    for (int i = CORE.Input.Touch.pointCount; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.currentTouchState[i] = 0;
+}
diff --git a/raylib/src/platforms/rcore_desktop_win32.c b/raylib/src/platforms/rcore_desktop_win32.c
new file mode 100644
--- /dev/null
+++ b/raylib/src/platforms/rcore_desktop_win32.c
@@ -0,0 +1,2201 @@
+/**********************************************************************************************
+*
+*   rcore_desktop_win32 - Functions to manage window, graphics device and inputs
+*
+*   PLATFORM: DESKTOP: WIN32
+*       - Windows (Win32, Win64)
+*
+*   LIMITATIONS:
+*       - Initial development stage, lot of functionality missing
+*       - No support for MOUSE_BUTTON_FORWARD/MOUSE_BUTTON_BACK
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Improvement 01
+*       - Improvement 02
+*
+*   CONFIGURATION:
+*       #define RCORE_PLATFORM_CUSTOM_FLAG
+*           Custom flag for rcore on target platform -not used-
+*
+*   DEPENDENCIES:
+*       - Win32 API (windows.h)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+// Move windows.h symbols to new names to avoid redefining the same names as raylib
+#define CloseWindow CloseWindowWin32
+#define Rectangle RectangleWin32
+#define ShowCursor ShowCursorWin32
+#define DrawTextA DrawTextAWin32
+#define DrawTextW DrawTextWin32
+#define DrawTextExA DrawTextExAWin32
+#define DrawTextExW DrawTextExWin32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#undef CloseWindow      // raylib symbol collision
+#undef Rectangle        // raylib symbol collision
+#undef ShowCursor       // raylib symbol collision
+#undef LoadImage        // raylib symbol collision
+#undef DrawText         // raylib symbol collision
+#undef DrawTextA
+#undef DrawTextW
+#undef DrawTextEx       // raylib symbol collision
+#undef DrawTextExA
+#undef DrawTextExW
+
+#include <windowsx.h>
+#include <shellscalingapi.h>
+#include <versionhelpers.h>
+
+#include <malloc.h>          // Required for alloca()
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    #include <GL/gl.h>
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+// NOTE: appScreenSize is the last screen size requested by the app,
+// the backend must keep the client area this size (after DPI scaling is applied)
+// when the window isn't fullscreen/maximized/minimized
+typedef struct {
+    HWND hwnd;              // Window handler
+    HDC hdc;                // Graphic context handler
+    HGLRC glContext;        // OpenGL context handler
+
+    // Software renderer variables
+    HDC hdcmem;             // Memory graphic context handler
+    HBITMAP hbitmap;        // GDI bitmap handler
+    unsigned int *pixels;   // Pointer to pixel data buffer (BGRA format)
+
+    unsigned int appScreenWidth;
+    unsigned int appScreenHeight;
+    unsigned int desiredFlags;
+
+    LARGE_INTEGER timerFrequency;
+} PlatformData;
+
+// Define WGL function pointer types (no wglext.h needed)
+typedef HGLRC (WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
+typedef BOOL (WINAPI *PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC, const int *, const FLOAT *, UINT, int *, UINT *);
+typedef BOOL (WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int);
+typedef const char *(WINAPI *PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC hdc);
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
+
+// Required WGL functions
+static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
+static PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = NULL;
+static PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
+static PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = NULL;
+
+// --------------------------------------------------------------------------------
+// This part of the file contains pure functions that never access global state
+// This distinction helps keep the backend maintainable as the inputs and outputs
+// of every function called in this section can be fully derived from the call-site alone
+// --------------------------------------------------------------------------------
+
+// Prevent any code in this part of the file from accessing the global CORE state
+#define CORE DONT_USE_CORE_HERE
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define A_TO_W_ALLOCA(outWstr, inAnsi)   do {                   \
+    size_t outLen = AToWLen(inAnsi);                            \
+    outWstr = (WCHAR *)alloca(sizeof(WCHAR)*(outLen + 1));      \
+    AToWCopy(inAnsi, outWstr, outLen);                          \
+    outWstr[outLen] = 0;                                        \
+} while (0)
+
+#define STYLE_MASK_ALL          0xffffffff
+#define STYLE_MASK_READONLY     (WS_MINIMIZE | WS_MAXIMIZE)
+#define STYLE_MASK_WRITABLE     (~STYLE_MASK_READONLY)
+
+#define STYLE_FLAGS_RESIZABLE   WS_THICKFRAME
+
+#define STYLE_FLAGS_UNDECORATED_OFF     (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX)
+#define STYLE_FLAGS_UNDECORATED_ON      WS_POPUP
+
+#define WINDOW_STYLE_EX         0
+
+#define CLASS_NAME              L"raylibWindow"
+
+#define FLAG_MASK_OPTIONAL      (FLAG_VSYNC_HINT)
+#define FLAG_MASK_REQUIRED      ~(FLAG_MASK_OPTIONAL)
+
+// Flags that have no operations to perform during an update
+#define FLAG_MASK_NO_UPDATE     (FLAG_WINDOW_HIGHDPI | FLAG_MSAA_4X_HINT)
+
+#define WM_APP_UPDATE_WINDOW_SIZE (WM_APP + 1)
+
+#define WGL_DRAW_TO_WINDOW_ARB              0x2001
+#define WGL_ACCELERATION_ARB                0x2003
+#define WGL_SUPPORT_OPENGL_ARB              0x2010
+#define WGL_DOUBLE_BUFFER_ARB               0x2011
+#define WGL_PIXEL_TYPE_ARB                  0x2013
+#define WGL_COLOR_BITS_ARB                  0x2014
+#define WGL_RED_BITS_ARB                    0x2015
+#define WGL_RED_SHIFT_ARB                   0x2016
+#define WGL_GREEN_BITS_ARB                  0x2017
+#define WGL_GREEN_SHIFT_ARB                 0x2018
+#define WGL_BLUE_BITS_ARB                   0x2019
+#define WGL_BLUE_SHIFT_ARB                  0x201a
+#define WGL_ALPHA_BITS_ARB                  0x201b
+#define WGL_ALPHA_SHIFT_ARB                 0x201c
+#define WGL_DEPTH_BITS_ARB                  0x2022
+#define WGL_STENCIL_BITS_ARB                0x2023
+#define WGL_TYPE_RGBA_ARB                   0x202b
+
+// Context acceleration types
+#define WGL_NO_ACCELERATION_ARB             0x2025      // OpenGL 1.1 GDI software rasterizer
+#define WGL_GENERIC_ACCELERATION_ARB        0x2026
+#define WGL_FULL_ACCELERATION_ARB           0x2027      // OpenGL hardware-accelerated, using GPU-drivers provided by vendor
+
+// WGL_ARB_multisample extension supported
+#define WGL_SAMPLE_BUFFERS_ARB              0x2041      // Multisampling: 1 if multisample buffers are supported
+#define WGL_SAMPLES_ARB                     0x2042      // Multisampling: Number of samples per pixel (4, 8, 16)
+
+// WGL_ARB_framebuffer_sRGB extension supported
+#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB    0x20a9      // GL_TRUE if the framebuffer can do sRGB conversion
+
+#define WGL_NUMBER_PIXEL_FORMATS_ARB        0x2000
+#define WGL_CONTEXT_MAJOR_VERSION_ARB       0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB       0x2092
+#define WGL_CONTEXT_PROFILE_MASK_ARB        0x9126
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB    0x00000001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_ES_PROFILE_BIT_EXT        0x00000004
+#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT        0x00000004
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Maximize-minimize request types
+typedef enum {
+    MIZED_NONE,
+    MIZED_MIN,
+    MIZED_MAX
+} Mized;
+
+// Flag operations
+// NOTE: Some ops need to be deferred
+typedef struct {
+    DWORD set;
+    DWORD clear;
+} FlagsOp;
+
+// Monitor info type
+typedef struct {
+    HMONITOR needle;
+    int index;
+    int matchIndex;
+    RECT rect;
+} MonitorInfo;
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+// Get ASCII to WCHAR length
+static size_t AToWLen(const char *ascii)
+{
+    int sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, ascii, -1, NULL, 0);
+
+    if (sizeNeeded < 0) TRACELOG(LOG_ERROR, "WIN32: Failed to calculate wide length [ERROR: %u]", GetLastError());
+
+    return sizeNeeded;
+}
+
+// Copy ASCII to WCHAR string
+static void AToWCopy(const char *ascii, wchar_t *outPtr, size_t outLen)
+{
+    int size = MultiByteToWideChar(CP_UTF8, 0, ascii, -1, outPtr, (int)outLen);
+    if (size != outLen) TRACELOG(LOG_WARNING, "WIN32: Failed to convert %i UTF-8 chars to WCHAR, converted %i chars", outLen, size);
+}
+
+static bool DecoratedFromStyle(DWORD style)
+{
+    if (style & STYLE_FLAGS_UNDECORATED_ON)
+    {
+        if (style & STYLE_FLAGS_UNDECORATED_OFF) TRACELOG(LOG_ERROR, "WIN32: FLAGS: Style 0x%x has both undecorated on/off flags", style);
+        return false; // Not decorated
+    }
+
+    DWORD masked = (style & STYLE_FLAGS_UNDECORATED_OFF);
+    if (STYLE_FLAGS_UNDECORATED_OFF != masked) TRACELOG(LOG_ERROR, "WIN32: FLAGS: Style 0x%x is missing flags 0x%x", masked, masked ^ STYLE_FLAGS_UNDECORATED_OFF);
+
+    return true; // Decorated
+}
+
+// Get window style from required flags
+static DWORD MakeWindowStyle(unsigned flags)
+{
+    // Flag is not needed because there are no child windows,
+    // but supposedly it improves efficiency, plus, windows adds this
+    // flag automatically anyway so it keeps flags in sync with the OS
+    DWORD style = WS_CLIPSIBLINGS;
+
+    style |= (flags & FLAG_WINDOW_HIDDEN)? 0 : WS_VISIBLE;
+    style |= (flags & FLAG_WINDOW_RESIZABLE)? STYLE_FLAGS_RESIZABLE : 0;
+    style |= (flags & FLAG_WINDOW_UNDECORATED)? STYLE_FLAGS_UNDECORATED_ON : STYLE_FLAGS_UNDECORATED_OFF;
+
+    // Minimized takes precedence over maximized
+    int mized = MIZED_NONE;
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED)) mized = MIZED_MIN;
+    if (flags & FLAG_WINDOW_MAXIMIZED) mized = MIZED_MAX;
+
+    switch (mized)
+    {
+        case MIZED_NONE: break;
+        case MIZED_MIN: style |= WS_MINIMIZE; break;
+        case MIZED_MAX: style |= WS_MAXIMIZE; break;
+        default: break;
+    }
+
+    return style;
+}
+
+// Check flags state, enforces that the actual window/platform state is in sync with raylib's flags
+static void CheckFlags(const char *context, HWND hwnd, DWORD flags, DWORD expectedStyle, DWORD styleCheckMask)
+{
+    DWORD styleFromFlags = MakeWindowStyle(flags);
+    if ((styleFromFlags & styleCheckMask) != (expectedStyle & styleCheckMask))
+    {
+        TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s: window flags (0x%x) produced style 0x%x which != expected 0x%x (diff=0x%x, mask=0x%x)",
+            context, flags, styleFromFlags & styleCheckMask, expectedStyle & styleCheckMask,
+            (styleFromFlags & styleCheckMask) ^ (expectedStyle & styleCheckMask), styleCheckMask);
+    }
+
+    SetLastError(0);
+    LONG actualStyle = (LONG)GetWindowLongPtrW(hwnd, GWL_STYLE);
+    if ((actualStyle & styleCheckMask) != (expectedStyle & styleCheckMask))
+    {
+        TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s: expected style 0x%x but got 0x%x (diff=0x%x, mask=0x%x, lasterror=%lu)",
+            context, expectedStyle & styleCheckMask, actualStyle & styleCheckMask,
+            (expectedStyle & styleCheckMask) ^ (actualStyle & styleCheckMask),
+            styleCheckMask, GetLastError());
+    }
+
+    if (styleCheckMask & WS_MINIMIZE)
+    {
+        bool isIconic = IsIconic(hwnd);
+        bool styleMinimized = !!(WS_MINIMIZE & actualStyle);
+        if (isIconic != styleMinimized) TRACELOG(LOG_ERROR, "WIN32: FLAGS: IsIconic(%d) != WS_MINIMIZED(%d)", isIconic, styleMinimized);
+    }
+
+    if (styleCheckMask & WS_MAXIMIZE)
+    {
+        WINDOWPLACEMENT placement;
+        placement.length = sizeof(placement);
+        if (!GetWindowPlacement(hwnd, &placement))
+        {
+            TRACELOG(LOG_ERROR, "WIN32: FLAGS: %s failed, error=%lu", "GetWindowPlacement", GetLastError());
+        }
+        bool placementMaximized = (placement.showCmd == SW_SHOWMAXIMIZED);
+        bool styleMaximized = WS_MAXIMIZE & actualStyle;
+        if (placementMaximized != styleMaximized)
+        {
+            TRACELOG(LOG_ERROR, "WIN32: FLAGS: Maximized state desync, placement maximized=%d (showCmd=%lu) style maximized=%d",
+                placementMaximized, placement.showCmd, styleMaximized);
+        }
+    }
+}
+
+// Calculate window size (with borders, title-bar...) from desired client size (framebuffer size)
+static SIZE CalcWindowSize(UINT dpi, SIZE clientSize, DWORD style)
+{
+    RECT rect = { 0, 0, clientSize.cx, clientSize.cy };
+
+    int result = AdjustWindowRectExForDpi(&rect, style, 0, WINDOW_STYLE_EX, dpi);
+    if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to adjust window rect [ERROR: %lu]", GetLastError());
+
+    return (SIZE){ rect.right - rect.left, rect.bottom - rect.top };
+}
+
+// Update window size if required
+// NOTE: Returns true if the window size was updated, false otherwise
+static bool UpdateWindowSize(int mode, HWND hwnd, int width, int height, unsigned flags)
+{
+    if (flags & FLAG_WINDOW_MINIMIZED) return false;
+
+    if (flags & FLAG_WINDOW_MAXIMIZED)
+    {
+        CheckFlags("UpdateWindowSize(maximized)", hwnd, flags, MakeWindowStyle(flags), STYLE_MASK_ALL);
+        return false;
+    }
+
+    if (flags & FLAG_BORDERLESS_WINDOWED_MODE)
+    {
+        MONITORINFO info = { 0 };
+        HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+        info.cbSize = sizeof(info);
+        if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor info [ERROR: %lu]", GetLastError());
+
+        RECT windowRect = { 0 };
+        if (!GetWindowRect(hwnd, &windowRect)) TRACELOG(LOG_ERROR, "WIN32: Failed to get window rect [ERROR: %lu]", GetLastError());
+
+        if ((windowRect.left == info.rcMonitor.left) &&
+            (windowRect.top == info.rcMonitor.top) &&
+            ((windowRect.right - windowRect.left) == (info.rcMonitor.right - info.rcMonitor.left)) &&
+            ((windowRect.bottom - windowRect.top) == (info.rcMonitor.bottom - info.rcMonitor.top))) return false;
+
+        if (!SetWindowPos(hwnd, HWND_TOP,
+            info.rcMonitor.left, info.rcMonitor.top,
+            info.rcMonitor.right - info.rcMonitor.left,
+            info.rcMonitor.bottom - info.rcMonitor.top,
+            SWP_NOOWNERZORDER))
+        {
+            TRACELOG(LOG_ERROR, "WIN32: Failed to set window position [ERROR: %lu]", GetLastError());
+        }
+
+        return true;
+    }
+
+    // Get size in pixels from points, considering high-dpi
+    UINT dpi = GetDpiForWindow(hwnd);
+    float dpiScale = ((float)dpi)/96.0f;
+    bool dpiScaling = flags & FLAG_WINDOW_HIGHDPI;
+    SIZE desiredSize = {
+        .cx = dpiScaling? (int)((float)width*dpiScale) : width,
+        .cy = dpiScaling? (int)((float)height*dpiScale) : height
+    };
+
+    // Get client size (framebuffer inside the window)
+    RECT rect = { 0 };
+    GetClientRect(hwnd, &rect);
+    SIZE clientSize = { rect.right, rect.bottom };
+
+    // If client size is alread desired size, no need to update
+    if ((clientSize.cx == desiredSize.cx) || (clientSize.cy == desiredSize.cy)) return false;
+
+    TRACELOG(LOG_INFO, "WIN32: Restoring client size from [%dx%d] to [%dx%d] (dpi:%lu dpiScaling:%d app:%ix%i)",
+        clientSize.cx, clientSize.cy, desiredSize.cx, desiredSize.cy, dpi, dpiScaling, width, height);
+
+    // Calculate window size from desired framebuffer size and window flags
+    SIZE windowSize = CalcWindowSize(dpi, desiredSize, MakeWindowStyle(flags));
+    POINT windowPos = { 0 };
+    UINT swpFlags = SWP_NOZORDER | SWP_FRAMECHANGED;
+
+    if (mode == 0) // UPDATE_WINDOW_FIRST
+    {
+        HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+        if (!monitor) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor from window [ERROR: %lu]", GetLastError());
+
+        MONITORINFO info = { 0 };
+        info.cbSize = sizeof(info);
+        if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: Failed to get monitor info [ERROR: %lu]", GetLastError());
+
+        #define MAX(a,b) (((a)>(b))? (a):(b))
+
+        LONG monitorWidth = info.rcMonitor.right - info.rcMonitor.left;
+        LONG monitorHeight = info.rcMonitor.bottom - info.rcMonitor.top;
+        windowPos = (POINT){
+            MAX(0, (monitorWidth - windowSize.cx)/2),
+            MAX(0, (monitorHeight - windowSize.cy)/2),
+        };
+    }
+    else swpFlags |= SWP_NOMOVE;
+
+    // WARNING: This code must be called after swInit() has been called, after InitPlatform() in [rcore]
+    //RECT rc = {0, 0, desired.cx, desired.cy};
+    //AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0);
+    //SetWindowPos(hwnd, NULL, windowPos.x, windowPos.y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER);
+
+    return true;
+}
+
+// Verify if we are running in Windows 10 version 1703 (Creators Update)
+static BOOL IsWindows10Version1703OrGreaterWin32(void)
+{
+    HMODULE ntdll = LoadLibraryW(L"ntdll.dll");
+
+    DWORD (*Verify)(RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG) =
+        (DWORD (*)(RTL_OSVERSIONINFOEXW*, ULONG, ULONGLONG))GetProcAddress(ntdll, "RtlVerifyVersionInfo");
+    if (!Verify)
+    {
+        TRACELOG(LOG_ERROR, "WIN32: Failed to verify Windows version [ERROR: %lu]", GetLastError());
+        return 0;
+    }
+
+    RTL_OSVERSIONINFOEXW osvi = { 0 };
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    osvi.dwMajorVersion = 10;
+    osvi.dwMinorVersion = 0;
+    osvi.dwBuildNumber = 15063;  // Build 15063 corresponds to Windows 10 version 1703 (Creators Update)
+
+    DWORDLONG cond = 0;
+    VER_SET_CONDITION(cond, VER_MAJORVERSION, VER_GREATER_EQUAL);
+    VER_SET_CONDITION(cond, VER_MINORVERSION, VER_GREATER_EQUAL);
+    VER_SET_CONDITION(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL);
+
+    return 0 == (*Verify)(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, cond);
+}
+
+// Get OpenGL function pointers
+static void *WglGetProcAddress(const char *procname)
+{
+    void *proc = (void *)wglGetProcAddress(procname);
+
+    if ((proc == NULL) ||
+        // NOTE: Some GPU drivers could return following
+        // invalid sentinel values instead of NULL
+        (proc == (void *)0x1) ||
+        (proc == (void *)0x2) ||
+        (proc == (void *)0x3) ||
+        (proc == (void *)-1))
+    {
+        // TODO: Keep gl module pointer as global platform data?
+        HMODULE glModule = LoadLibraryW(L"opengl32.dll");
+        proc = (void *)GetProcAddress(glModule, procname);
+
+        //if (proc == NULL) TRACELOG(LOG_ERROR, "GL: GetProcAddress() failed to get %s [%p], error=%u", procname, proc, GetLastError());
+        //else TRACELOG(LOG_INFO, "GL: Found entry point for %s [%p]", procname, proc);
+    }
+
+    return proc;
+}
+
+// Get key from wparam (mapping)
+static KeyboardKey GetKeyFromWparam(WPARAM wparam)
+{
+    switch (wparam)
+    {
+        /* case VK_LBUTTON: return KEY_; */
+        /* case VK_RBUTTON: return KEY_; */
+        /* case VK_CANCEL: return KEY_; */
+        /* case VK_MBUTTON: return KEY_; */
+        /* case VK_XBUTTON1: return KEY_; */
+        /* case VK_XBUTTON2: return KEY_; */
+        /* case VK_BACK: return KEY_; */
+        /* case VK_TAB: return KEY_; */
+        /* case VK_CLEAR: return KEY_; */
+        case VK_RETURN: return KEY_ENTER;
+        /* case VK_SHIFT: return KEY_; */
+        /* case VK_CONTROL: return KEY_; */
+        /* case VK_MENU: return KEY_; */
+        /* case VK_PAUSE: return KEY_; */
+        /* case VK_CAPITAL: return KEY_; */
+        /* case VK_KANA: return KEY_; */
+        /* case VK_HANGUL: return KEY_; */
+        /* case VK_IME_ON: return KEY_; */
+        /* case VK_JUNJA: return KEY_; */
+        /* case VK_FINAL: return KEY_; */
+        /* case VK_HANJA: return KEY_; */
+        /* case VK_KANJI: return KEY_; */
+        /* case VK_IME_OFF: return KEY_; */
+        case VK_ESCAPE: return KEY_ESCAPE;
+        /* case VK_CONVERT: return KEY_; */
+        /* case VK_NONCONVERT: return KEY_; */
+        /* case VK_ACCEPT: return KEY_; */
+        /* case VK_MODECHANGE: return KEY_; */
+        case VK_SPACE: return KEY_SPACE;
+        /* case VK_PRIOR: return KEY_; */
+        /* case VK_NEXT: return KEY_; */
+        /* case VK_END: return KEY_; */
+        /* case VK_HOME: return KEY_; */
+        case VK_LEFT: return KEY_LEFT;
+        case VK_UP: return KEY_UP;
+        case VK_RIGHT: return KEY_RIGHT;
+        case VK_DOWN: return KEY_DOWN;
+        /* case VK_SELECT: return KEY_; */
+        /* case VK_PRINT: return KEY_; */
+        /* case VK_EXECUTE: return KEY_; */
+        /* case VK_SNAPSHOT: return KEY_; */
+        /* case VK_INSERT: return KEY_; */
+        /* case VK_DELETE: return KEY_; */
+        /* case VK_HELP: return KEY_; */
+        case '0': return KEY_ZERO;
+        case '1': return KEY_ONE;
+        case '2': return KEY_TWO;
+        case '3': return KEY_THREE;
+        case '4': return KEY_FOUR;
+        case '5': return KEY_FIVE;
+        case '6': return KEY_SIX;
+        case '7': return KEY_SEVEN;
+        case '8': return KEY_EIGHT;
+        case '9': return KEY_NINE;
+        /* case 0x3A-40: return KEY_; */
+        case 'A': return KEY_A;
+        case 'B': return KEY_B;
+        case 'C': return KEY_C;
+        case 'D': return KEY_D;
+        case 'E': return KEY_E;
+        case 'F': return KEY_F;
+        case 'G': return KEY_G;
+        case 'H': return KEY_H;
+        case 'I': return KEY_I;
+        case 'J': return KEY_J;
+        case 'K': return KEY_K;
+        case 'L': return KEY_L;
+        case 'M': return KEY_M;
+        case 'N': return KEY_N;
+        case 'O': return KEY_O;
+        case 'P': return KEY_P;
+        case 'Q': return KEY_Q;
+        case 'R': return KEY_R;
+        case 'S': return KEY_S;
+        case 'T': return KEY_T;
+        case 'U': return KEY_U;
+        case 'V': return KEY_V;
+        case 'W': return KEY_W;
+        case 'X': return KEY_X;
+        case 'Y': return KEY_Y;
+        case 'Z': return KEY_Z;
+        /* case VK_LWIN: return KEY_; */
+        /* case VK_RWIN: return KEY_; */
+        /* case VK_APPS: return KEY_; */
+        /* case VK_SLEEP: return KEY_; */
+        /* case VK_NUMPAD0: return KEY_; */
+        /* case VK_NUMPAD1: return KEY_; */
+        /* case VK_NUMPAD2: return KEY_; */
+        /* case VK_NUMPAD3: return KEY_; */
+        /* case VK_NUMPAD4: return KEY_; */
+        /* case VK_NUMPAD5: return KEY_; */
+        /* case VK_NUMPAD6: return KEY_; */
+        /* case VK_NUMPAD7: return KEY_; */
+        /* case VK_NUMPAD8: return KEY_; */
+        /* case VK_NUMPAD9: return KEY_; */
+        /* case VK_MULTIPLY: return KEY_; */
+        /* case VK_ADD: return KEY_; */
+        /* case VK_SEPARATOR: return KEY_; */
+        /* case VK_SUBTRACT: return KEY_; */
+        /* case VK_DECIMAL: return KEY_; */
+        /* case VK_DIVIDE: return KEY_; */
+        /* case VK_F1: return KEY_; */
+        /* case VK_F2: return KEY_; */
+        /* case VK_F3: return KEY_; */
+        /* case VK_F4: return KEY_; */
+        /* case VK_F5: return KEY_; */
+        /* case VK_F6: return KEY_; */
+        /* case VK_F7: return KEY_; */
+        /* case VK_F8: return KEY_; */
+        /* case VK_F9: return KEY_; */
+        /* case VK_F10: return KEY_; */
+        /* case VK_F11: return KEY_; */
+        /* case VK_F12: return KEY_; */
+        /* case VK_F13: return KEY_; */
+        /* case VK_F14: return KEY_; */
+        /* case VK_F15: return KEY_; */
+        /* case VK_F16: return KEY_; */
+        /* case VK_F17: return KEY_; */
+        /* case VK_F18: return KEY_; */
+        /* case VK_F19: return KEY_; */
+        /* case VK_F20: return KEY_; */
+        /* case VK_F21: return KEY_; */
+        /* case VK_F22: return KEY_; */
+        /* case VK_F23: return KEY_; */
+        /* case VK_F24: return KEY_; */
+        /* case VK_NUMLOCK: return KEY_; */
+        /* case VK_SCROLL: return KEY_; */
+        /* case VK_LSHIFT: return KEY_; */
+        /* case VK_RSHIFT: return KEY_; */
+        /* case VK_LCONTROL: return KEY_; */
+        /* case VK_RCONTROL: return KEY_; */
+        /* case VK_LMENU: return KEY_; */
+        /* case VK_RMENU: return KEY_; */
+        /* case VK_BROWSER_BACK: return KEY_; */
+        /* case VK_BROWSER_FORWARD: return KEY_; */
+        /* case VK_BROWSER_REFRESH: return KEY_; */
+        /* case VK_BROWSER_STOP: return KEY_; */
+        /* case VK_BROWSER_SEARCH: return KEY_; */
+        /* case VK_BROWSER_FAVORITES: return KEY_; */
+        /* case VK_BROWSER_HOME: return KEY_; */
+        /* case VK_VOLUME_MUTE: return KEY_; */
+        /* case VK_VOLUME_DOWN: return KEY_; */
+        /* case VK_VOLUME_UP: return KEY_; */
+        /* case VK_MEDIA_NEXT_TRACK: return KEY_; */
+        /* case VK_MEDIA_PREV_TRACK: return KEY_; */
+        /* case VK_MEDIA_STOP: return KEY_; */
+        /* case VK_MEDIA_PLAY_PAUSE: return KEY_; */
+        /* case VK_LAUNCH_MAIL: return KEY_; */
+        /* case VK_LAUNCH_MEDIA_SELECT: return KEY_; */
+        /* case VK_LAUNCH_APP1: return KEY_; */
+        /* case VK_LAUNCH_APP2: return KEY_; */
+        /* case VK_OEM_1: return KEY_; */
+        /* case VK_OEM_PLUS: return KEY_; */
+        /* case VK_OEM_COMMA: return KEY_; */
+        /* case VK_OEM_MINUS: return KEY_; */
+        /* case VK_OEM_PERIOD: return KEY_; */
+        /* case VK_OEM_2: return KEY_; */
+        /* case VK_OEM_3: return KEY_; */
+        /* case VK_OEM_4: return KEY_; */
+        /* case VK_OEM_5: return KEY_; */
+        /* case VK_OEM_6: return KEY_; */
+        /* case VK_OEM_7: return KEY_; */
+        /* case VK_OEM_8: return KEY_; */
+        /* case VK_OEM_102: return KEY_; */
+        /* case VK_PROCESSKEY: return KEY_; */
+        /* case VK_PACKET: return KEY_; */
+        /* case VK_ATTN: return KEY_; */
+        /* case VK_CRSEL: return KEY_; */
+        /* case VK_EXSEL: return KEY_; */
+        /* case VK_EREOF: return KEY_; */
+        /* case VK_PLAY: return KEY_; */
+        /* case VK_ZOOM: return KEY_; */
+        /* case VK_NONAME: return KEY_; */
+        /* case VK_PA1: return KEY_; */
+        /* case VK_OEM_CLEAR: return KEY_; */
+        default: return KEY_NULL;
+    }
+}
+
+// Get cursor name
+static LPCWSTR GetCursorName(int cursor)
+{
+    LPCWSTR name = (LPCWSTR)IDC_ARROW;
+
+    switch (cursor)
+    {
+        case MOUSE_CURSOR_DEFAULT: name = (LPCWSTR)IDC_ARROW; break;
+        case MOUSE_CURSOR_ARROW: name = (LPCWSTR)IDC_ARROW; break;
+        case MOUSE_CURSOR_IBEAM: name = (LPCWSTR)IDC_IBEAM; break;
+        case MOUSE_CURSOR_CROSSHAIR: name = (LPCWSTR)IDC_CROSS; break;
+        case MOUSE_CURSOR_POINTING_HAND: name = (LPCWSTR)IDC_HAND; break;
+        case MOUSE_CURSOR_RESIZE_EW: name = (LPCWSTR)IDC_SIZEWE; break;
+        case MOUSE_CURSOR_RESIZE_NS: name = (LPCWSTR)IDC_SIZENS; break;
+        case MOUSE_CURSOR_RESIZE_NWSE: name = (LPCWSTR)IDC_SIZENWSE; break;
+        case MOUSE_CURSOR_RESIZE_NESW: name = (LPCWSTR)IDC_SIZENESW; break;
+        case MOUSE_CURSOR_RESIZE_ALL: name = (LPCWSTR)IDC_SIZEALL; break;
+        case MOUSE_CURSOR_NOT_ALLOWED: name = (LPCWSTR)IDC_NO; break;
+        default: break;
+    }
+
+    return name;
+}
+
+// Count monitors process
+// NOTE: Required by GetMonitorCount()
+static BOOL CALLBACK CountMonitorsProc(HMONITOR handle, HDC hdc, LPRECT rect, LPARAM lparam)
+{
+    int *count = (int *)lparam;
+    *count += 1;
+
+    // Always return TRUE to continue the loop, otherwise, the caller
+    // can't distinguish between stopping the loop and an error
+    return TRUE;
+}
+
+// Find monitor process
+// NOTE: Required by GetCurrentMonitor()
+static BOOL CALLBACK FindMonitorProc(HMONITOR handle, HDC hdc, LPRECT rect, LPARAM lparam)
+{
+    MonitorInfo *monitor = (MonitorInfo *)lparam;
+
+    if (handle == monitor->needle)
+    {
+        monitor->matchIndex = monitor->index;
+        monitor->rect = *rect;
+    }
+
+    monitor->index += 1;
+
+    // Always return TRUE to continue the loop, otherwise, the caller
+    // can't distinguish between stopping the loop and an error
+    return TRUE;
+}
+
+// Get style changed required operations flags
+// NOTE: Required for deferred operations
+static void GetStyleChangeFlagOps(DWORD coreWindowFlags, STYLESTRUCT *style, FlagsOp *deferredFlags)
+{
+    // Check window resizable flag change
+    bool resizable = (coreWindowFlags & FLAG_WINDOW_RESIZABLE);
+    bool resizableOld = ((style->styleOld & STYLE_FLAGS_RESIZABLE) != 0);
+    bool resizableNew = ((style->styleNew & STYLE_FLAGS_RESIZABLE) != 0);
+    if (resizable != resizableOld) TRACELOG(LOG_ERROR, "WIN32: Expected resizable %u but got %u", resizable, resizableOld);
+    if (resizableOld != resizableNew)
+    {
+        if (resizableNew) deferredFlags->set |= FLAG_WINDOW_RESIZABLE;
+        else deferredFlags->clear |= FLAG_WINDOW_RESIZABLE;
+    }
+
+    // Check window decorated flag change
+    bool decorated = (0 == (coreWindowFlags & FLAG_WINDOW_UNDECORATED));
+    bool decoratedOld = DecoratedFromStyle(style->styleOld);
+    bool decoratedNew = DecoratedFromStyle(style->styleNew);
+    if (decorated != decoratedOld) TRACELOG(LOG_ERROR, "WIN32: Expected decorated %u but got %u", decorated, decoratedOld);
+    if (decoratedOld != decoratedNew)
+    {
+        if (decoratedNew) deferredFlags->clear |= FLAG_WINDOW_UNDECORATED;
+        else deferredFlags->set |= FLAG_WINDOW_UNDECORATED;
+    }
+
+    // Check window hidden flag change
+    bool hidden = (coreWindowFlags & FLAG_WINDOW_HIDDEN);
+    bool hiddenOld = ((style->styleOld & WS_VISIBLE) == 0);
+    bool hiddenNew = ((style->styleNew & WS_VISIBLE) == 0);
+    if (hidden != hiddenOld) TRACELOG(LOG_ERROR, "WIN32: Expected hidden %u but got %u", hidden, hiddenOld);
+    if (hiddenOld != hiddenNew)
+    {
+        if (hiddenNew) deferredFlags->set |= FLAG_WINDOW_HIDDEN;
+        else deferredFlags->clear |= FLAG_WINDOW_HIDDEN;
+    }
+}
+
+// Adopt window resize
+// NOTE: Call when the window is rezised, returns true
+// if the new window size should update the desired app size
+static bool AdoptWindowResize(unsigned flags)
+{
+    if (flags & FLAG_WINDOW_MINIMIZED) return false;
+    if (flags & FLAG_WINDOW_MAXIMIZED) return false;
+    if (flags & FLAG_FULLSCREEN_MODE) return false;
+    if (flags & FLAG_BORDERLESS_WINDOWED_MODE) return false;
+    if (!(flags & FLAG_WINDOW_RESIZABLE)) return false;
+
+    return true;
+}
+
+// ---------------------------------------------------------------------------------------------
+// Here's the end of the "pure function section", the rest of the file can access global state
+// ---------------------------------------------------------------------------------------------
+
+// Unlock the ability to use CORE in the rest of the file
+#undef CORE
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void);             // Initialize platform (graphics, inputs and more)
+void ClosePlatform(void);           // Close platform
+
+// Win32 process messages management function
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+
+// Win32: Handle inputs functions
+static void HandleKey(WPARAM wparam, LPARAM lparam, char state);
+static void HandleMouseButton(int button, char state);
+static void HandleRawInput(LPARAM lparam);
+static void HandleWindowResize(HWND hwnd, int *width, int *height);
+
+static void UpdateWindowStyle(HWND hwnd, unsigned desiredFlags);
+static unsigned SanitizeFlags(int mode, unsigned flags);
+static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height); // Update window flags
+
+// Check if OpenGL extension is available
+static bool IsWglExtensionAvailable(HDC hdc, const char *extension);
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+bool WindowShouldClose(void)
+{
+    return CORE.Window.shouldClose;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+    TRACELOG(LOG_WARNING, "WIN32: Toggle full screen functionality not implemented");
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+    if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) ClearWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
+    else SetWindowState(FLAG_BORDERLESS_WINDOWED_MODE);
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+    SetWindowState(FLAG_WINDOW_MAXIMIZED);
+}
+
+// Set window state: minimized
+void MinimizeWindow(void)
+{
+    SetWindowState(FLAG_WINDOW_MINIMIZED);
+}
+
+// Restore window from being minimized/maximized
+void RestoreWindow(void)
+{
+    if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) &&
+        (CORE.Window.flags & FLAG_WINDOW_MINIMIZED)) ClearWindowState(FLAG_WINDOW_MINIMIZED);
+    else ClearWindowState(FLAG_WINDOW_MINIMIZED | FLAG_WINDOW_MAXIMIZED);
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+    platform.desiredFlags = SanitizeFlags(1 /*SANITIZE_FLAGS_NORMAL*/, CORE.Window.flags | flags);
+    UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+    platform.desiredFlags = SanitizeFlags(1 /*SANITIZE_FLAGS_NORMAL*/, CORE.Window.flags & ~flags);
+    UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
+}
+
+// Set icon for window
+void SetWindowIcon(Image image)
+{
+    if (!platform.hwnd || (image.data == NULL) || (image.width <= 0) || (image.height <= 0)) return;
+
+    HDC hdc = GetDC(platform.hwnd);
+
+    // Create 32-bit BGRA DIB for color
+    BITMAPV5HEADER bi = { 0 };
+    ZeroMemory(&bi, sizeof(bi));
+    bi.bV5Size = sizeof(bi);
+    bi.bV5Width = image.width;
+    bi.bV5Height = -image.height; // Negative = top-down bitmap
+    bi.bV5Planes = 1;
+    bi.bV5BitCount = 32;
+    bi.bV5Compression = BI_BITFIELDS;
+    bi.bV5RedMask = 0x00FF0000;
+    bi.bV5GreenMask = 0x0000FF00;
+    bi.bV5BlueMask = 0x000000FF;
+    bi.bV5AlphaMask = 0xFF000000;
+
+    unsigned char *targetBits = NULL;
+    HBITMAP hColorBitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&targetBits, NULL, 0);
+    if (!hColorBitmap)
+    {
+        ReleaseDC(platform.hwnd, hdc);
+        return;
+    }
+
+    // Copy RGBA > BGRA (Win32 expects BGRA)
+    for (int y = 0; y < image.height; y++)
+    {
+        for (int x = 0; x < image.width; x++)
+        {
+            int i = (y*image.width + x)*4;
+            targetBits[i + 0] = ((unsigned char *)image.data)[i + 2]; // B
+            targetBits[i + 1] = ((unsigned char *)image.data)[i + 1]; // G
+            targetBits[i + 2] = ((unsigned char *)image.data)[i + 0]; // R
+            targetBits[i + 3] = ((unsigned char *)image.data)[i + 3]; // A
+        }
+    }
+
+    // Create mask bitmap (1-bit, all opaque)
+    HBITMAP hMaskBitmap = CreateBitmap(image.width, image.height, 1, 1, NULL);
+
+    // Build icon info
+    ICONINFO ii = { 0 };
+    ZeroMemory(&ii, sizeof(ii));
+    ii.fIcon = TRUE;
+    ii.hbmMask = hMaskBitmap;
+    ii.hbmColor = hColorBitmap;
+
+    HICON hIcon = CreateIconIndirect(&ii);
+
+    // Clean up GDI bitmaps (icon keeps copies internally)
+    DeleteObject(hColorBitmap);
+    DeleteObject(hMaskBitmap);
+    ReleaseDC(platform.hwnd, hdc);
+
+    if (hIcon)
+    {
+        // Set both large and small icons
+        SendMessage(platform.hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+        SendMessage(platform.hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
+    }
+}
+
+// Set icon for window
+void SetWindowIcons(Image *images, int count)
+{
+    // TODO: Implement SetWindowIcons()
+}
+
+void SetWindowTitle(const char *title)
+{
+    CORE.Window.title = title;
+
+    WCHAR *titleWide = NULL;
+    A_TO_W_ALLOCA(titleWide, CORE.Window.title);
+
+    int result = SetWindowTextW(platform.hwnd, titleWide);
+    if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to set window title [ERROR: %lu]", GetLastError());
+}
+
+// Set window position on screen (windowed mode)
+void SetWindowPosition(int x, int y)
+{
+    if (platform.hwnd != NULL)
+    {
+        RECT rect = { 0 };
+        if (GetWindowRect(platform.hwnd, &rect))
+        {
+            int width = rect.right - rect.left;
+            int height = rect.bottom - rect.top;
+
+            // Move the window to the new position (keeping size and z-order)
+            SetWindowPos(platform.hwnd, NULL, x, y, width, height, SWP_NOZORDER | SWP_NOACTIVATE);
+        }
+    }
+}
+
+// Set monitor for the current window
+void SetWindowMonitor(int monitor)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMonitor not implemented");
+}
+
+// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMinSize(int width, int height)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMinSize not implemented");
+
+    CORE.Window.screenMin.width = width;
+    CORE.Window.screenMin.height = height;
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMaxSize not implemented");
+
+    CORE.Window.screenMax.width = width;
+    CORE.Window.screenMax.height = height;
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+    TRACELOG(LOG_WARNING, "SetWindowSize not implemented");
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+    TRACELOG(LOG_WARNING, "SetWindowOpacity not implemented");
+}
+
+// Set window focused
+void SetWindowFocused(void)
+{
+    TRACELOG(LOG_WARNING, "SetWindowFocused not implemented");
+}
+
+// Get native window handle
+void *GetWindowHandle(void)
+{
+    return platform.hwnd;
+}
+
+int GetMonitorCount(void)
+{
+    int count = 0;
+
+    int result = EnumDisplayMonitors(NULL, NULL, CountMonitorsProc, (LPARAM)&count);
+    if (result == 0) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "EnumDisplayMonitors", GetLastError());
+
+    return count;
+}
+
+// Get current monitor where window is placed
+int GetCurrentMonitor(void)
+{
+    HMONITOR monitor = MonitorFromWindow(platform.hwnd, MONITOR_DEFAULTTOPRIMARY);
+    if (!monitor) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "MonitorFromWindow", GetLastError());
+
+    MonitorInfo info = { 0 };
+    info.needle = monitor;
+    info.index = 0;
+    info.matchIndex = -1;
+
+    int result = EnumDisplayMonitors(NULL, NULL, FindMonitorProc, (LPARAM)&info);
+    if (result == 0) TRACELOG(LOG_ERROR, "%s failed, error=%lu", "EnumDisplayMonitors", GetLastError());
+
+    return info.matchIndex;
+}
+
+// Get selected monitor position
+Vector2 GetMonitorPosition(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPosition not implemented");
+    return (Vector2){ 0, 0 };
+}
+
+// Get selected monitor width (currently used by monitor)
+int GetMonitorWidth(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorWidth not implemented");
+    return 0;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorHeight not implemented");
+    return 0;
+}
+
+// Get selected monitor physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth not implemented");
+    return 0;
+}
+
+// Get selected monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight not implemented");
+    return 0;
+}
+
+// Get selected monitor refresh rate
+int GetMonitorRefreshRate(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorRefreshRate not implemented");
+    return 0;
+}
+
+// Get the human-readable, UTF-8 encoded name of the selected monitor
+const char *GetMonitorName(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorName not implemented");
+    return 0;
+}
+
+// Get window position XY on monitor
+Vector2 GetWindowPosition(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowPosition not implemented");
+    return (Vector2){ 0, 0 };
+}
+
+// Get window scale DPI factor for current monitor
+Vector2 GetWindowScaleDPI(void)
+{
+    float scale = ((float)GetDpiForWindow(platform.hwnd))/96.0f;
+    return (Vector2){ scale, scale };
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+    TRACELOG(LOG_WARNING, "SetClipboardText not implemented");
+}
+
+// Get clipboard text content
+const char *GetClipboardText(void)
+{
+    TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
+    return NULL;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardText not implemented");
+
+    return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+    SetCursor(LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+    // NOTE: We use SetCursor() instead of ShowCursor() because
+    // it makes it easy to only hide the cursor while it's inside the client area
+    SetCursor(NULL);
+    CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+    if (CORE.Input.Mouse.cursorLocked)
+    {
+        if (!ClipCursor(NULL)) TRACELOG(LOG_WARNING, "WIN32: Failed to clip cursor [ERROR: %lu]", GetLastError());
+
+        RAWINPUTDEVICE rid = { 0 };
+        rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+        rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
+        rid.dwFlags = RIDEV_REMOVE; // Add to this window even in background
+        rid.hwndTarget = NULL;
+        int result = RegisterRawInputDevices(&rid, 1, sizeof(rid));
+        if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to register raw input devices [ERROR: %lu]", GetLastError());
+
+        ShowCursor();
+        CORE.Input.Mouse.cursorLocked = false;
+    }
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+    if (!CORE.Input.Mouse.cursorLocked)
+    {
+        RAWINPUTDEVICE rid = { 0 };
+        rid.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
+        rid.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
+        rid.dwFlags = RIDEV_INPUTSINK; // Add to this window even in background
+        rid.hwndTarget = platform.hwnd;
+        int result = RegisterRawInputDevices(&rid, 1, sizeof(rid));
+        if (result == 0) TRACELOG(LOG_WARNING, "WIN32: Failed to register raw input devices [ERROR: %lu]", GetLastError());
+
+        RECT clientRect = { 0 };
+        if (!GetClientRect(platform.hwnd, &clientRect)) TRACELOG(LOG_WARNING, "WIN32: Failed to get client rectangle [ERROR: %lu]", GetLastError());
+
+        POINT topleft = { clientRect.left, clientRect.top };
+        if (!ClientToScreen(platform.hwnd, &topleft)) TRACELOG(LOG_WARNING, "WIN32: Failed to get client to screen size [ERROR: %lu]", GetLastError());
+
+        LONG width = clientRect.right - clientRect.left;
+        LONG height = clientRect.bottom - clientRect.top;
+
+        TRACELOG(LOG_INFO, "WIN32: Clip cursor client rect: [%d,%d %d,%d], top-left: (%d,%d)",
+            clientRect.left, clientRect.top, clientRect.right, clientRect.bottom, topleft.x, topleft.y);
+
+        LONG centerX = topleft.x + width/2;
+        LONG centerY = topleft.y + height/2;
+        RECT clipRect = { centerX, centerY, centerX + 1, centerY + 1 };
+        if (!ClipCursor(&clipRect)) TRACELOG(LOG_WARNING, "WIN32: Failed to clip cursor [ERROR: %lu]", GetLastError());
+
+        CORE.Input.Mouse.previousPosition = (Vector2){ 0, 0 };
+        CORE.Input.Mouse.currentPosition = (Vector2){ 0, 0 };
+        HideCursor();
+
+        CORE.Input.Mouse.cursorLocked = true;
+    }
+}
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+    if (!platform.hdc) abort();
+
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Update framebuffer
+    rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
+
+    // Force redraw
+    InvalidateRect(platform.hwnd, NULL, FALSE);
+    UpdateWindow(platform.hwnd);
+#else
+    if (!SwapBuffers(platform.hdc)) TRACELOG(LOG_ERROR, "WIN32: Failed to swap buffers [ERROR: %lu]", GetLastError());
+    if (!ValidateRect(platform.hwnd, NULL)) TRACELOG(LOG_ERROR, "WIN32: Failed to validate screen rect [ERROR: %lu]", GetLastError());
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds
+double GetTime(void)
+{
+    LARGE_INTEGER now = 0;
+    QueryPerformanceCounter(&now);
+    return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
+}
+
+// Open URL with default system browser (if available)
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
+// REF: https://github.com/raysan5/raylib/issues/686
+void OpenURL(const char *url)
+{
+    // Security check to (partially) avoid malicious code on target platform
+    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
+    else
+    {
+        char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+        sprintf(cmd, "explorer \"%s\"", url);
+        int result = system(cmd);
+        if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+        RL_FREE(cmd);
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadMappings not implemented");
+
+    return -1;
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadVibration not implemented");
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+    if (!CORE.Input.Mouse.cursorLocked)
+    {
+        CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
+        CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+        TRACELOG(LOG_WARNING, "SetMousePosition not implemented");
+    }
+    else TRACELOG(LOG_WARNING, "INPUT: MOUSE: Cursor not enabled");
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+    LPCWSTR cursorName = GetCursorName(cursor);
+    HCURSOR hcursor = LoadCursorW(NULL, cursorName);
+    if (!hcursor) TRACELOG(LOG_ERROR, "WIN32: Failed to load requested cursor [ERROR: %lu]", GetLastError());
+
+    SetCursor(hcursor);
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Get physical key name
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName not implemented");
+    return NULL;
+}
+
+// Register all input events
+void PollInputEvents(void)
+{
+    // Reset keys/chars pressed registered
+    CORE.Input.Keyboard.keyPressedQueueCount = 0;
+    CORE.Input.Keyboard.charPressedQueueCount = 0;
+
+    // Reset key repeats
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+
+    // Reset last gamepad button/axis registered state
+    CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
+    //CORE.Input.Gamepad.axisCount = 0;
+
+    // Register previous touch states
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
+
+    // Reset touch positions
+    // TODO: It resets on target platform the mouse position and not filled again until a move-event,
+    // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
+    //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
+
+    memcpy(CORE.Input.Keyboard.previousKeyState, CORE.Input.Keyboard.currentKeyState, sizeof(CORE.Input.Keyboard.previousKeyState));
+    memset(CORE.Input.Keyboard.keyRepeatInFrame, 0, sizeof(CORE.Input.Keyboard.keyRepeatInFrame));
+
+    // Register previous mouse wheel state
+    CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
+    CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
+
+    // Register previous mouse position
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+
+    // Process windows messages
+    MSG msg = { 0 };
+    while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
+    {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize modern OpenGL context
+// NOTE: We need to create a dummy context first to query required extensions
+HGLRC InitOpenGL(HWND hwnd, HDC hdc)
+{
+    // First, create a dummy context to get WGL extensions
+    PIXELFORMATDESCRIPTOR pixelFormatDesc = {
+        .nSize = sizeof(PIXELFORMATDESCRIPTOR),
+        .nVersion = 1,
+        .dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+        .iPixelType = PFD_TYPE_RGBA,
+        .cColorBits = 32,
+        .cAlphaBits = 8,
+        .cDepthBits = 24,
+        .iLayerType = PFD_MAIN_PLANE
+    };
+
+    int pixelFormat = ChoosePixelFormat(hdc, &pixelFormatDesc);
+    SetPixelFormat(hdc, pixelFormat, &pixelFormatDesc);
+    //int pixelFormat = ChoosePixelFormat(platform.hdc, &pixelFormatDesc);
+    //if (!pixelFormat) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "ChoosePixelFormat", GetLastError()); return -1; }
+    //if (!SetPixelFormat(platform.hdc, pixelFormat, &pixelFormatDesc)) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "SetPixelFormat", GetLastError()); return -1; }
+
+    HGLRC tempContext = wglCreateContext(hdc);
+    //if (!tempContext) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglCreateContext", GetLastError()); return -1; }
+    BOOL result = wglMakeCurrent(hdc, tempContext);
+    //if (!result) { TRACELOG(LOG_ERROR, "%s failed, error=%lu", "wglMakeCurrent", GetLastError()); return -1; }
+
+    // Load WGL extension entry points
+    wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
+    wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
+    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
+    wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
+
+    // Setup modern pixel format if extension is available
+    if (wglChoosePixelFormatARB)
+    {
+        int pixelFormatAttribs[] = {
+            WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
+            WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
+            WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
+            WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
+            WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
+            WGL_COLOR_BITS_ARB, 32,
+            //WGL_RED_BITS_ARB, 8,
+            //WGL_GREEN_BITS_ARB, 8,
+            //WGL_BLUE_BITS_ARB, 8,
+            //WGL_ALPHA_BITS_ARB, 8,
+            WGL_DEPTH_BITS_ARB, 24,
+            WGL_STENCIL_BITS_ARB, 8,
+            0 // Terminator
+        };
+
+        int format = 0;
+        UINT numFormats = 0;
+        if (wglChoosePixelFormatARB(hdc, pixelFormatAttribs, NULL, 1, &format, &numFormats) && (numFormats > 0))
+        {
+            PIXELFORMATDESCRIPTOR newPixelFormatDescriptor = { 0 };
+            DescribePixelFormat(hdc, format, sizeof(newPixelFormatDescriptor), &newPixelFormatDescriptor);
+            SetPixelFormat(hdc, format, &newPixelFormatDescriptor);
+        }
+    }
+
+    // Create real modern OpenGL context (3.3 core)
+    HGLRC realContext = NULL;
+    if (wglCreateContextAttribsARB)
+    {
+        int glContextVersionMajor = 1;
+        int glContextVersionMinor = 1;
+        int glContextProfile = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
+
+        if (rlGetVersion() == RL_OPENGL_21)         // Request OpenGL 2.1 context
+        {
+            glContextVersionMajor = 2;
+            glContextVersionMinor = 1;
+        }
+        else if (rlGetVersion() == RL_OPENGL_33)    // Request OpenGL 3.3 context
+        {
+            glContextVersionMajor = 3;
+            glContextVersionMinor = 3;
+        }
+        else if (rlGetVersion() == RL_OPENGL_43)    // Request OpenGL 4.3 context
+        {
+            glContextVersionMajor = 4;
+            glContextVersionMinor = 3;
+        }
+        else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context
+        {
+            if (IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es_profile") ||
+                IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es2_profile"))
+            {
+                glContextVersionMajor = 2;
+                glContextVersionMinor = 0;
+                glContextProfile = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
+            }
+            else TRACELOG(LOG_WARNING, "GL: OpenGL ES context not supported by GPU");
+        }
+        else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
+        {
+            if (IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es_profile") ||
+                IsWglExtensionAvailable(platform.hdc, "WGL_EXT_create_context_es2_profile"))
+            {
+                glContextVersionMajor = 3;
+                glContextVersionMinor = 0;
+                glContextProfile = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
+            }
+            else TRACELOG(LOG_WARNING, "GL: OpenGL ES context not supported by GPU");
+        }
+
+        int contextAttribs[] = {
+            WGL_CONTEXT_MAJOR_VERSION_ARB, glContextVersionMajor,
+            WGL_CONTEXT_MINOR_VERSION_ARB, glContextVersionMinor,
+            WGL_CONTEXT_PROFILE_MASK_ARB, glContextProfile, // WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, WGL_CONTEXT_ES_PROFILE_BIT_EXT (if supported)
+            //WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB [glDebugMessageCallback()]
+            0 // Terminator
+        };
+
+        // NOTE: We are not sharing context resources so, second parameters is NULL
+        realContext = wglCreateContextAttribsARB(hdc, NULL, contextAttribs);
+
+        // Check for error context creation errors
+        // ERROR_INVALID_VERSION_ARB (0x2095)
+        // ERROR_INVALID_PROFILE_ARB (0x2096)
+        if (realContext == NULL) TRACELOG(LOG_ERROR, "GL: Error creating requested context: %lu", GetLastError());
+    }
+
+    // Cleanup dummy temp context
+    wglMakeCurrent(NULL, NULL);
+    wglDeleteContext(tempContext);
+
+    // Activate real context
+    if (realContext) wglMakeCurrent(hdc, realContext);
+
+    // Once we got a real modern OpenGL context,
+    // we can load required extensions (function pointers)
+    rlLoadExtensions(WglGetProcAddress);
+
+    return realContext;
+}
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+    int result = 0;
+
+    platform.appScreenWidth = CORE.Window.screen.width;
+    platform.appScreenHeight = CORE.Window.screen.height;
+    platform.desiredFlags = SanitizeFlags(0 /*SANITIZE_FLAGS_FIRST*/, CORE.Window.flags);
+
+    // NOTE: From this point CORE.Window.flags should always reflect the actual state of the window
+    CORE.Window.flags = FLAG_WINDOW_HIDDEN | (platform.desiredFlags & FLAG_MASK_NO_UPDATE);
+
+/*
+    // TODO: Review SetProcessDpiAwarenessContext()
+    // NOTE: SetProcessDpiAwarenessContext() requires Windows 10, version 1703 and shcore.lib linkage
+    if (IsWindows10Version1703OrGreaterWin32())
+    {
+        TRACELOG(LOG_INFO, "DpiAware: >=Win10Creators");
+        if (!SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2))
+            TRACELOG(LOG_ERROR, "%s failed, error %u", "SetProcessDpiAwarenessContext", GetLastError());
+    }
+    else
+    {
+        TRACELOG(LOG_INFO, "DpiAware: <Win10Creators");
+        HRESULT hr = SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
+        if (hr < 0) TRACELOG(LOG_ERROR, "%s failed, hresult=0x%lx", "SetProcessDpiAwareness", (DWORD)hr);
+    }
+*/
+
+    HINSTANCE hInstance = GetModuleHandleW(0);
+
+    // Define window class
+    WNDCLASSEXW windowClass = {
+        .cbSize = sizeof(WNDCLASSEXW),
+        .style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
+        .lpfnWndProc = WndProc,                         // Custom procedure assigned
+        .cbWndExtra = sizeof(LONG_PTR),                 // extra space for the Tuple object ptr
+        .hInstance = hInstance,
+        .hCursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW), // TODO: Audit if we want to set this since we're implementing WM_SETCURSOR
+        .lpszClassName = CLASS_NAME                     // Class name: L"raylibWindow"
+    };
+
+    // Load user-provided icon if available
+    // NOTE: raylib resource file defaults to GLFW_ICON id, so looking for same identifier
+    windowClass.hIcon = LoadImageW(hInstance, L"GLFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+    if (!windowClass.hIcon) windowClass.hIcon = LoadImageW(NULL, (LPCWSTR)IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
+
+    // Register window class
+    result = (int)RegisterClassExW(&windowClass);
+    if (result == 0) TRACELOG(LOG_ERROR, "WIN32: Failed to register window class [ERROR: %lu]", GetLastError());
+
+    // Get primary monitor info
+    POINT primaryTopLeft = { 0 };
+    HMONITOR monitor = MonitorFromPoint(primaryTopLeft, MONITOR_DEFAULTTOPRIMARY);
+    if (monitor != NULL)
+    {
+        MONITORINFO info = { 0 };
+        info.cbSize = sizeof(info);
+        result = (int)GetMonitorInfoW(monitor, &info);
+
+        if (result == 0) TRACELOG(LOG_WARNING, "WIN32: DISPLAY: Failed to get monitor info [ERROR: %u]", GetLastError());
+        else
+        {
+            CORE.Window.display.width = info.rcMonitor.right - info.rcMonitor.left;
+            CORE.Window.display.height = info.rcMonitor.bottom - info.rcMonitor.top;
+        }
+    }
+    else TRACELOG(LOG_WARNING, "WIN32: DISPLAY: Failed to get primary monitor from point [ERROR: %u]", GetLastError());
+
+    // Adjust the window rectangle so the *client area* matches desired size
+    // NOTE: Window width/height includes borders and title-bar
+    DWORD style = WS_OVERLAPPEDWINDOW;
+    RECT rect = { 0, 0, platform.appScreenWidth, platform.appScreenHeight };
+    AdjustWindowRect(&rect, style, FALSE);
+    //AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, FALSE, WINDOW_STYLE_EX);
+    //AdjustWindowRectExForDpi(&rect, style, FALSE, WINDOW_STYLE_EX, dpi);
+    int windowWidth  = rect.right - rect.left;
+    int windowHeight = rect.bottom - rect.top;
+
+    // Create window
+    // NOTE: Title string needs to be converted to WCHAR
+    WCHAR *titleWide = NULL;
+    A_TO_W_ALLOCA(titleWide, CORE.Window.title);
+
+    // Create window and get handle
+    platform.hwnd = CreateWindowExW(
+        WINDOW_STYLE_EX,
+        CLASS_NAME,
+        titleWide,
+        MakeWindowStyle(CORE.Window.flags),     // WS_OVERLAPPEDWINDOW | WS_VISIBLE
+        CW_USEDEFAULT, CW_USEDEFAULT,
+        windowWidth, windowHeight,  // TODO: Window size [width, height], needs to be updated?
+        NULL, NULL,
+        GetModuleHandleW(NULL), NULL);
+
+    if (!platform.hwnd)
+    {
+        TRACELOG(LOG_ERROR, "WIN32: WINDOW: Failed to create window [ERROR: %lu]", GetLastError());
+        return -1;
+    }
+
+    // Get handle to device drawing context
+    // NOTE: Windows GDI object that represents a drawing surface
+    platform.hdc = GetDC(platform.hwnd);
+
+    if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
+    {
+        //ShowWindow(platform.hwnd, SW_SHOWDEFAULT); //SW_SHOWNORMAL
+
+        // Initialize software framebuffer
+        BITMAPINFO bmi = { 0 };
+        ZeroMemory(&bmi, sizeof(bmi));
+        bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+        bmi.bmiHeader.biWidth = platform.appScreenWidth;
+        bmi.bmiHeader.biHeight = -(int)(platform.appScreenHeight); // Top-down bitmap
+        bmi.bmiHeader.biPlanes = 1;
+        bmi.bmiHeader.biBitCount  = 32;         // 32-bit BGRA
+        bmi.bmiHeader.biCompression = BI_RGB;
+
+        platform.hdcmem = CreateCompatibleDC(platform.hdc);
+
+        platform.hbitmap = CreateDIBSection(
+            platform.hdcmem, &bmi, DIB_RGB_COLORS,
+            (void **)&platform.pixels, NULL, 0);
+
+        SelectObject(platform.hdcmem, platform.hbitmap);
+
+        //ReleaseDC(platform.hwnd, platform.hdc); // Required?
+    }
+    else
+    {
+        // Init hardware-accelerated OpenGL modern context
+        platform.glContext = InitOpenGL(platform.hwnd, platform.hdc);
+    }
+
+    CORE.Window.ready = true;
+
+    // Update flags (in case of deferred state change required)
+    UpdateFlags(platform.hwnd, platform.desiredFlags, platform.appScreenWidth, platform.appScreenHeight);
+
+    CORE.Window.render.width = CORE.Window.screen.width;
+    CORE.Window.render.height = CORE.Window.screen.height;
+    CORE.Window.currentFbo.width = CORE.Window.render.width;
+    CORE.Window.currentFbo.height = CORE.Window.render.height;
+    TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
+    TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+    TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+    TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+    TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+
+    if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
+    {
+        TRACELOG(LOG_INFO, "GL: OpenGL device information:");
+        TRACELOG(LOG_INFO, "    > Vendor:   %s", "raylib");
+        TRACELOG(LOG_INFO, "    > Renderer: %s", "rlsw - OpenGL 1.1 Software Renderer");
+        TRACELOG(LOG_INFO, "    > Version:  %s", "1.0");
+        TRACELOG(LOG_INFO, "    > GLSL:     %s", "NOT SUPPORTED");
+    }
+
+    // Initialize timming system
+    //----------------------------------------------------------------------------
+    LARGE_INTEGER time = { 0 };
+    QueryPerformanceCounter(&time);
+    QueryPerformanceFrequency(&platform.timerFrequency);
+    CORE.Time.base = time.QuadPart;
+
+    InitTimer();
+    //----------------------------------------------------------------------------
+
+    // Initialize storage system
+    //----------------------------------------------------------------------------
+    CORE.Storage.basePath = GetWorkingDirectory();
+    //----------------------------------------------------------------------------
+
+    TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: WIN32: Initialized successfully");
+
+    return 0;
+}
+
+// Close platform
+void ClosePlatform(void)
+{
+    if (platform.hwnd)
+    {
+        int result = DestroyWindow(platform.hwnd);
+        if (result == 0) TRACELOG(LOG_WARNING, "WIN32: WINDOW: Failed on window destroy [ERROR: %u]", GetLastError());
+        platform.hwnd = NULL;
+    }
+}
+
+// Window procedure, message processing callback
+// NOTE: All window event messages are processed here
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    LRESULT result = 0;
+
+    // Sanity check
+    DWORD mask = STYLE_MASK_ALL;
+    if (platform.hwnd == hwnd)
+    {
+        if (msg == WM_WINDOWPOSCHANGING) mask &= ~(WS_MINIMIZE | WS_MAXIMIZE);
+        CheckFlags("WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask);
+    }
+
+    FlagsOp flagsOp = { 0 };
+    FlagsOp *deferredFlags = &flagsOp;
+
+    // Message processing
+    //------------------------------------------------------------------------------------
+    switch (msg)
+    {
+        case WM_CREATE:
+        {
+            // WARNING: Not recommended to do OpenGL intialization at this point
+
+        } break;
+        //case WM_ACTIVATE
+        case WM_DESTROY:
+        {
+            // Clean up for window destruction
+            if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
+            {
+                if (platform.hdcmem)
+                {
+                    DeleteDC(platform.hdcmem);
+                    platform.hdcmem = NULL;
+                }
+
+                if (platform.hbitmap)
+                {
+                    DeleteObject(platform.hbitmap); // Clears platform.pixels data
+                    platform.hbitmap = NULL;
+                    platform.pixels = NULL; // NOTE: Pointer invalid after DeleteObject()
+                }
+            }
+            else // OpenGL hardware renderer
+            {
+                wglMakeCurrent(platform.hdc, NULL);
+                if (platform.glContext)
+                {
+                    if (!wglDeleteContext(platform.glContext)) abort();
+                    platform.glContext = NULL;
+                }
+            }
+
+            if (platform.hdc)
+            {
+                if (!ReleaseDC(hwnd, platform.hdc)) abort();
+                platform.hdc = NULL;
+            }
+
+            PostQuitMessage(0);
+
+        } break;
+        case WM_CLOSE: CORE.Window.shouldClose = true; break; // Window close button [x], ALT+F4
+        //case WM_QUIT: // Application closing, not related to window
+        case WM_KILLFOCUS:
+        {
+            memset(CORE.Input.Keyboard.previousKeyState, 0, sizeof(CORE.Input.Keyboard.previousKeyState));
+            memset(CORE.Input.Keyboard.currentKeyState, 0, sizeof(CORE.Input.Keyboard.currentKeyState));
+        } break;
+        case WM_SIZING:
+        {
+            if (CORE.Window.flags & FLAG_WINDOW_RESIZABLE)
+            {
+                // TODO: Enforce min/max size
+            }
+            else TRACELOG(LOG_WARNING, "WIN32: WINDOW: Trying to resize a non-resizable window");
+
+            result = TRUE;
+        } break;
+        case WM_STYLECHANGING:
+        {
+            if (wparam == GWL_STYLE)
+            {
+                STYLESTRUCT *ss = (STYLESTRUCT *)lparam;
+                GetStyleChangeFlagOps(CORE.Window.flags, ss, deferredFlags);
+
+                UINT dpi = GetDpiForWindow(hwnd);
+                // Get client size (framebuffer inside the window)
+                RECT rect = { 0 };
+                GetClientRect(hwnd, &rect);
+                SIZE clientSize = { rect.right, rect.bottom };
+                SIZE oldSize = CalcWindowSize(dpi, clientSize, ss->styleOld);
+                SIZE newSize = CalcWindowSize(dpi, clientSize, ss->styleNew);
+
+                if (oldSize.cx != newSize.cx || oldSize.cy != newSize.cy)
+                {
+                    TRACELOG(LOG_INFO, "WIN32: WINDOW: Resize from style change [%dx%d] to [%dx%d]", oldSize.cx, oldSize.cy, newSize.cx, newSize.cy);
+
+                    if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED)
+                    {
+                        // looks like windows will automatically "unminimize" a window
+                        // if a style changes modifies it's size
+                        TRACELOG(LOG_INFO, "WIN32: WINDOW: Style change modifed window size, removing maximized flag");
+                        deferredFlags->clear |= FLAG_WINDOW_MAXIMIZED;
+                    }
+                }
+            }
+        } break;
+        case WM_WINDOWPOSCHANGING:
+        {
+            WINDOWPOS *pos = (WINDOWPOS *)lparam;
+            if (pos->flags & SWP_SHOWWINDOW) deferredFlags->clear |= FLAG_WINDOW_HIDDEN;
+            else if (pos->flags & SWP_HIDEWINDOW) deferredFlags->set |= FLAG_WINDOW_HIDDEN;
+
+            Mized mized = MIZED_NONE;
+            bool isIconic = IsIconic(hwnd);
+            bool styleMinimized = !!(WS_MINIMIZE & GetWindowLongPtrW(hwnd, GWL_STYLE));
+            if (isIconic != styleMinimized) TRACELOG(LOG_WARNING, "WIN32: IsIconic state different from WS_MINIMIZED state");
+
+            if (isIconic) mized = MIZED_MIN;
+            else
+            {
+                WINDOWPLACEMENT placement;
+                placement.length = sizeof(placement);
+                if (!GetWindowPlacement(hwnd, &placement)) TRACELOG(LOG_ERROR, "WIN32: WINDOW: FAiled to get monitor placement [ERROR: %lu]", GetLastError());
+
+                if (placement.showCmd == SW_SHOWMAXIMIZED) mized = MIZED_MAX;
+            }
+
+            switch (mized)
+            {
+                case MIZED_NONE:
+                {
+                    deferredFlags->clear |= (FLAG_WINDOW_MINIMIZED | FLAG_WINDOW_MAXIMIZED);
+                    HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY);
+                    MONITORINFO info;
+                    info.cbSize = sizeof(info);
+                    if (!GetMonitorInfoW(monitor, &info)) TRACELOG(LOG_ERROR, "WIN32: MONITOR: Failed to get monitor info [ERROR: %lu]", GetLastError());
+
+                    if ((pos->x == info.rcMonitor.left) &&
+                        (pos->y == info.rcMonitor.top) &&
+                        (pos->cx == (info.rcMonitor.right - info.rcMonitor.left)) &&
+                        (pos->cy == (info.rcMonitor.bottom - info.rcMonitor.top))) deferredFlags->set |= FLAG_BORDERLESS_WINDOWED_MODE;
+                    else deferredFlags->clear |= FLAG_BORDERLESS_WINDOWED_MODE;
+
+                } break;
+                case MIZED_MIN:
+                {
+                    // !!! NOTE !!! Do not update the maximized/borderless
+                    // flags because when hwnd is minimized it temporarily overrides
+                    // the maximized state/flag which gets restored on SW_RESTORE
+                    deferredFlags->set |= FLAG_WINDOW_MINIMIZED;
+                } break;
+                case MIZED_MAX:
+                {
+                    deferredFlags->clear |= FLAG_WINDOW_MINIMIZED;
+                    deferredFlags->set |= FLAG_WINDOW_MAXIMIZED;
+                } break;
+                default: break;
+            }
+        } break;
+        case WM_SIZE:
+        {
+            // WARNING: Don't trust the docs, they say you won't get this message if you don't call DefWindowProc
+            // in response to WM_WINDOWPOSCHANGED but looks like when a window is created you'll get this
+            // message without getting WM_WINDOWPOSCHANGED
+            HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight);
+        } break;
+        //case WM_MOVE
+        case WM_WINDOWPOSCHANGED:
+        {
+            WINDOWPOS *pos = (WINDOWPOS*)lparam;
+            if (!(pos->flags & SWP_NOSIZE)) HandleWindowResize(hwnd, &platform.appScreenWidth, &platform.appScreenHeight);
+        } break;
+        case WM_GETDPISCALEDSIZE:
+        {
+            SIZE *inoutSize = (SIZE *)lparam;
+            UINT newDpi = (UINT)wparam; // TODO: WARNING: Converting from WPARAM = UINT_PTR
+
+            // for any of these other cases, we might want to post a window
+            // resize event after the dpi changes?
+            if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return TRUE;
+            if (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) return TRUE;
+            if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) return TRUE;
+
+            float dpiScale = ((float)newDpi)/96.0f;
+            bool dpiScaling = CORE.Window.flags & FLAG_WINDOW_HIGHDPI;
+            // Get size in pixels from points
+            SIZE desired = {
+                .cx = dpiScaling? (int)((float)platform.appScreenWidth*dpiScale) : platform.appScreenWidth,
+                .cy = dpiScaling? (int)((float)platform.appScreenHeight*dpiScale) : platform.appScreenHeight
+            };
+            inoutSize->cx = desired.cx;
+            inoutSize->cy = desired.cy;
+
+            result = TRUE;
+        } break;
+        case WM_DPICHANGED:
+        {
+            // Get current dpi scale factor
+            float scalex = HIWORD(wParam)/96.0f;
+            float scaley = LOWORD(wParam)/96.0f;
+
+            RECT *suggestedRect = (RECT *)lparam;
+
+            // Never set the window size to anything other than the suggested rect here
+            // Doing so can cause a window to stutter between monitors when transitioning between them
+            int result = (int)SetWindowPos(hwnd, NULL,
+                suggestedRect->left, suggestedRect->top,
+                suggestedRect->right - suggestedRect->left,
+                suggestedRect->bottom - suggestedRect->top,
+                SWP_NOZORDER | SWP_NOACTIVATE);
+
+            if (result == 0) TRACELOG(LOG_ERROR, "Failed to set window position [ERROR: %lu]", GetLastError());
+
+            // TODO: Update screen data, render size, screen scaling, viewport...
+
+        } break;
+        case WM_SETCURSOR:
+        {
+            // Called when mouse moves, enters/leaves window...
+            if (LOWORD(lparam) == HTCLIENT)
+            {
+                SetCursor(CORE.Input.Mouse.cursorHidden? NULL : LoadCursorW(NULL, (LPCWSTR)IDC_ARROW));
+                return 0;
+            }
+
+            result = DefWindowProc(hwnd, msg, wparam, lparam);
+        } break;
+        case WM_PAINT:
+        {
+            if (rlGetVersion() == RL_OPENGL_11_SOFTWARE) // Using software renderer
+            {
+                PAINTSTRUCT ps = { 0 };
+                HDC hdc = BeginPaint(hwnd, &ps);
+
+                // Blit from memory DC to window DC
+                BitBlt(hdc, 0, 0, platform.appScreenWidth, platform.appScreenHeight, platform.hdcmem, 0, 0, SRCCOPY);
+
+                EndPaint(hwnd, &ps);
+            }
+        }
+        case WM_INPUT:
+        {
+            //HandleRawInput(lparam);
+        } break;
+        case WM_MOUSEMOVE:
+        {
+            if (!CORE.Input.Mouse.cursorLocked)
+            {
+                CORE.Input.Mouse.currentPosition.x = (float)GET_X_LPARAM(lparam);
+                CORE.Input.Mouse.currentPosition.y = (float)GET_Y_LPARAM(lparam);
+                CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
+            }
+        } break;
+        case WM_KEYDOWN: HandleKey(wparam, lparam, 1); break;
+        case WM_KEYUP: HandleKey(wparam, lparam, 0); break;
+        case WM_LBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_LEFT, 1); break;
+        case WM_LBUTTONUP  : HandleMouseButton(MOUSE_BUTTON_LEFT, 0); break;
+        case WM_RBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_RIGHT, 1); break;
+        case WM_RBUTTONUP  : HandleMouseButton(MOUSE_BUTTON_RIGHT, 0); break;
+        case WM_MBUTTONDOWN: HandleMouseButton(MOUSE_BUTTON_MIDDLE, 1); break;
+        case WM_MBUTTONUP  : HandleMouseButton(MOUSE_BUTTON_MIDDLE, 0); break;
+        case WM_XBUTTONDOWN:
+        {
+            switch (HIWORD(wparam))
+            {
+                case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 1); break;
+                case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 1); break;
+                default: TRACELOG(LOG_WARNING, "TODO: handle ex mouse button DOWN wparam=%u", HIWORD(wparam)); break;
+            }
+        } break;
+        case WM_XBUTTONUP:
+        {
+            switch (HIWORD(wparam))
+            {
+                case XBUTTON1: HandleMouseButton(MOUSE_BUTTON_SIDE, 0); break;
+                case XBUTTON2: HandleMouseButton(MOUSE_BUTTON_EXTRA, 0); break;
+                default: TRACELOG(LOG_WARNING, "TODO: handle ex mouse button UP   wparam=%u", HIWORD(wparam)); break;
+            }
+        } break;
+        case WM_MOUSEWHEEL: CORE.Input.Mouse.currentWheelMove.y = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
+        case WM_MOUSEHWHEEL: CORE.Input.Mouse.currentWheelMove.x = ((float)GET_WHEEL_DELTA_WPARAM(wparam))/WHEEL_DELTA; break;
+        case WM_APP_UPDATE_WINDOW_SIZE:
+        {
+            //UpdateWindowSize(UPDATE_WINDOW_NORMAL, hwnd, platform.appScreenWidth, platform.appScreenHeight, CORE.Window.flags);
+        } break;
+
+        default: result = DefWindowProcW(hwnd, msg, wparam, lparam); // Message passed directly for execution (default behaviour)
+    }
+    //------------------------------------------------------------------------------------
+
+    // Sanity check for flags
+    if (platform.hwnd == hwnd) CheckFlags("After WndProc", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), mask);
+
+    // Operations to execute after the above check
+    if (flagsOp.set & flagsOp.clear) TRACELOG(LOG_WARNING, "WIN32: FLAGS: Flags 0x%x were both set and cleared", flagsOp.set & flagsOp.clear);
+
+    DWORD save = CORE.Window.flags;
+    CORE.Window.flags |= flagsOp.set;
+    CORE.Window.flags &= ~flagsOp.clear;
+    if (save != CORE.Window.flags) TRACELOG(LOG_DEBUG, "WIN32: FLAGS: Current deferred flags: 0x%x > 0x%x (diff 0x%x)", save, CORE.Window.flags, save ^ CORE.Window.flags);
+
+    return result;
+}
+
+// Handle keyboard input event
+static void HandleKey(WPARAM wparam, LPARAM lparam, char state)
+{
+    KeyboardKey key = GetKeyFromWparam(wparam);
+
+    // TODO: Use scancode?
+    //BYTE scancode = lparam >> 16;
+    //TRACELOG(LOG_INFO, "KEY key=%d vk=%lu scan=%u = %u", key, wparam, scancode, state);
+
+    if (key != KEY_NULL)
+    {
+        CORE.Input.Keyboard.currentKeyState[key] = state;
+
+        if ((key == KEY_ESCAPE) && (state == 1)) CORE.Window.shouldClose = true;
+    }
+    else TRACELOG(LOG_WARNING, "INPUT: Unknown (or currently unhandled) virtual keycode %d (0x%x)", wparam, wparam);
+
+    // TODO: Add key to the queue as well?
+}
+
+// Handle mouse button input event
+static void HandleMouseButton(int button, char state)
+{
+    // Register current mouse button state
+    CORE.Input.Mouse.currentButtonState[button] = state;
+    CORE.Input.Touch.currentTouchState[button] = state;
+}
+
+// Handle raw input event
+static void HandleRawInput(LPARAM lparam)
+{
+    RAWINPUT input = { 0 };
+
+    UINT inputSize = sizeof(input);
+    UINT size = GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &input, &inputSize, sizeof(RAWINPUTHEADER));
+
+    if (size == (UINT)-1) TRACELOG(LOG_ERROR, "WIN32: Failed to get raw input data [ERROR: %lu]", GetLastError());
+
+    if (input.header.dwType != RIM_TYPEMOUSE) TRACELOG(LOG_ERROR, "WIN32: Unexpected WM_INPUT type %lu", input.header.dwType);
+
+    if (input.data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) TRACELOG(LOG_ERROR, "TODO: handle absolute mouse inputs!");
+
+    if (input.data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) TRACELOG(LOG_ERROR, "TODO: handle virtual desktop mouse inputs!");
+
+    // Trick to keep the mouse position at 0,0 and instead move
+    // the previous position so we can still get a proper mouse delta
+    //CORE.Input.Mouse.previousPosition.x -= input.data.mouse.lLastX;
+    //CORE.Input.Mouse.previousPosition.y -= input.data.mouse.lLastY;
+    //if (CORE.Input.Mouse.currentPosition.x != 0) abort();
+    //if (CORE.Input.Mouse.currentPosition.y != 0) abort();
+}
+
+// Handle window resizing event
+static void HandleWindowResize(HWND hwnd, int *width, int *height)
+{
+    if (CORE.Window.flags & FLAG_WINDOW_MINIMIZED) return;
+
+    // Get client size (framebuffer inside the window)
+    RECT rect = { 0 };
+    GetClientRect(hwnd, &rect);
+    SIZE clientSize = { rect.right, rect.bottom };
+
+    // TODO: Update framebuffer on resize
+    CORE.Window.currentFbo.width = (int)clientSize.cx;
+    CORE.Window.currentFbo.height = (int)clientSize.cy;
+    //SetupViewport(0, 0, clientSize.cx, clientSize.cy);
+
+    SetupViewport(clientSize.cx, clientSize.cy);
+    CORE.Window.resizedLastFrame = true;
+    float dpiScale = ((float)GetDpiForWindow(hwnd))/96.0f;
+    bool highdpi = !!(CORE.Window.flags & FLAG_WINDOW_HIGHDPI);
+    unsigned int screenWidth = highdpi? (unsigned int)(((float)clientSize.cx)/dpiScale) : clientSize.cx;
+    unsigned int screenHeight = highdpi? (unsigned int)(((float)clientSize.cy)/dpiScale) : clientSize.cy;
+    CORE.Window.screen.width = screenWidth;
+    CORE.Window.screen.height = screenHeight;
+
+    if (AdoptWindowResize(CORE.Window.flags))
+    {
+        TRACELOG(LOG_DEBUG, "WIN32: WINDOW: Updating app size to [%ix%i] from window resize", screenWidth, screenHeight);
+        *width = screenWidth;
+        *height = screenHeight;
+    }
+
+    CORE.Window.screenScale = MatrixScale( (float)CORE.Window.render.width/CORE.Window.screen.width,
+        (float)CORE.Window.render.height/CORE.Window.screen.height, 1.0f);
+}
+
+// Update window style
+static void UpdateWindowStyle(HWND hwnd, unsigned desiredFlags)
+{
+    DWORD current = STYLE_MASK_WRITABLE & MakeWindowStyle(CORE.Window.flags);
+    DWORD desired = STYLE_MASK_WRITABLE & MakeWindowStyle(desiredFlags);
+
+    if (current != desired)
+    {
+        SetLastError(0);
+        DWORD previous = STYLE_MASK_WRITABLE & SetWindowLongPtrW(hwnd, GWL_STYLE, desired);
+        if (previous != current)
+        {
+            TRACELOG(LOG_ERROR, "WIN32: WINDOW: SetWindowLongPtr() returned writable flags 0x%x but expected 0x%x (diff=0x%x, error=%lu)",
+                previous, current, previous ^ current, GetLastError());
+        }
+
+        CheckFlags("UpdateWindowStyle", hwnd, desiredFlags, desired, STYLE_MASK_WRITABLE);
+    }
+
+    // Minimized takes precedence over maximized
+    Mized currentMized = MIZED_NONE;
+    Mized desiredMized = MIZED_NONE;
+    if (CORE.Window.flags & WS_MINIMIZE) currentMized = MIZED_MIN;
+    else if (CORE.Window.flags & WS_MAXIMIZE) currentMized = MIZED_MAX;
+    if (desiredFlags & WS_MINIMIZE) currentMized = MIZED_MIN;
+    else if (desiredFlags & WS_MAXIMIZE) currentMized = MIZED_MAX;
+
+    if (currentMized != desiredMized)
+    {
+        switch (desiredMized)
+        {
+            case MIZED_NONE: ShowWindow(hwnd, SW_RESTORE); break;
+            case MIZED_MIN: ShowWindow(hwnd, SW_MINIMIZE); break;
+            case MIZED_MAX: ShowWindow(hwnd, SW_MAXIMIZE); break;
+        }
+    }
+}
+
+// Sanitize flags
+static unsigned SanitizeFlags(int mode, unsigned flags)
+{
+    if ((flags & FLAG_WINDOW_MAXIMIZED) && (flags & FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        TRACELOG(LOG_WARNING, "WIN32: WINDOW: Borderless windows mode overriding maximized window flag");
+        flags &= ~FLAG_WINDOW_MAXIMIZED;
+    }
+
+    if (mode == 1)
+    {
+        if ((flags & FLAG_MSAA_4X_HINT) && (!(CORE.Window.flags & FLAG_MSAA_4X_HINT)))
+        {
+            TRACELOG(LOG_WARNING, "WIN32: WINDOW: MSAA can only be configured before window initialization");
+            flags &= ~FLAG_MSAA_4X_HINT;
+        }
+    }
+
+    return flags;
+}
+
+// All window state changes from raylib flags go through this function. It performs
+// whatever operations are needed to update the window state to match the desired flags
+// In most cases this function should not update CORE.Window.flags directly, instead,
+// the window itself should update CORE.Window.flags in response to actual state changes
+// This means that CORE.Window.flags should always represent the actual state of the
+// window. This function will continue to perform these update operations so long as
+// the state continues to change
+//
+// This design takes care of many odd corner cases. For example, if you want to restore
+// a window that was previously maximized AND minimized and you want to remove both these
+// flags, you actually need to call ShowWindow with SW_RESTORE twice. Another example is
+// if you have a maximized window, if the undecorated flag is modified then we'd need to
+// update the window style, but updating the style would mean the window size would change
+// causing the window to lose its Maximized state which would mean we'd need to update the
+// window size and then update the window style a second time to restore that maximized
+// state. This implementation is able to handle any/all of these special situations with a
+// retry loop that continues until we either reach the desired state or the state stops changing
+static void UpdateFlags(HWND hwnd, unsigned desiredFlags, int width, int height)
+{
+    // Flags that just apply immediately without needing any operations
+    CORE.Window.flags |= (desiredFlags & FLAG_MASK_NO_UPDATE);
+
+    int vsync = (CORE.Window.flags & FLAG_VSYNC_HINT)? 1 : 0;
+    if (wglSwapIntervalEXT)
+    {
+        (*wglSwapIntervalEXT)(vsync);
+        if (vsync) CORE.Window.flags |= FLAG_VSYNC_HINT;
+        else CORE.Window.flags &= ~FLAG_VSYNC_HINT;
+    }
+
+    // TODO: Review all this code...
+    DWORD previousStyle = 0;
+    for (unsigned attempt = 1; ; attempt++)
+    {
+        CheckFlags("UpdateFlags", hwnd, CORE.Window.flags, MakeWindowStyle(CORE.Window.flags), STYLE_MASK_ALL);
+
+        bool windowSizeUpdated = false;
+        if (MakeWindowStyle(CORE.Window.flags) == MakeWindowStyle(desiredFlags))
+        {
+            windowSizeUpdated = UpdateWindowSize(1, hwnd, width, height, desiredFlags);
+            if ((FLAG_MASK_REQUIRED & desiredFlags) == (FLAG_MASK_REQUIRED & CORE.Window.flags)) break;
+        }
+
+
+        if ((attempt > 1) && (previousStyle == MakeWindowStyle(CORE.Window.flags)) && !windowSizeUpdated)
+        {
+            TRACELOG(LOG_ERROR, "WIN32: WINDOW: UpdateFlags() failed after %u attempt(s) wanted 0x%x but is 0x%x (diff=0x%x)",
+                attempt, desiredFlags, CORE.Window.flags, desiredFlags ^ CORE.Window.flags);
+        }
+
+        previousStyle = MakeWindowStyle(CORE.Window.flags);
+        UpdateWindowStyle(hwnd, desiredFlags);
+    }
+}
+
+// Check if OpenGL extension is available
+static bool IsWglExtensionAvailable(HDC hdc, const char *extension)
+{
+    bool result = false;
+
+    if (wglGetExtensionsStringARB != NULL)
+    {
+        const char *extList = wglGetExtensionsStringARB(hdc);
+        if (extList != NULL)
+        {
+            // Simple substring search (could use strtok or strstr)
+            if (strstr(extList, extension) != NULL) result = true;
+        }
+    }
+
+    return result;
+}
diff --git a/raylib/src/platforms/rcore_drm.c b/raylib/src/platforms/rcore_drm.c
--- a/raylib/src/platforms/rcore_drm.c
+++ b/raylib/src/platforms/rcore_drm.c
@@ -13,1930 +13,2734 @@
 *       - Improvement 01
 *       - Improvement 02
 *
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
-*
-*   CONFIGURATION:
-*       #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
-*           Reconfigure standard input to receive key inputs, works with SSH connection.
-*           WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
-*           running processes orblocking the device if not restored properly. Use with care.
-*
-*   DEPENDENCIES:
-*       - DRM and GLM: System libraries for display initialization and configuration
-*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#include <fcntl.h>   // POSIX file control definitions - open(), creat(), fcntl()
-#include <unistd.h>  // POSIX standard function definitions - read(), close(), STDIN_FILENO
-#include <termios.h> // POSIX terminal control definitions - tcgetattr(), tcsetattr()
-#include <pthread.h> // POSIX threads management (inputs reading)
-#include <dirent.h>  // POSIX directory browsing
-
-#include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
-#include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
-#include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
-#include <linux/joystick.h> // Linux: Joystick support library
-
-// WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12
-// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
-// so the enum KEY_F12 from raylib is used
-#undef KEY_F12
-
-#include <gbm.h>            // Generic Buffer Management (native platform for EGL on DRM)
-#include <xf86drm.h>        // Direct Rendering Manager user-level library interface
-#include <xf86drmMode.h>    // Direct Rendering Manager mode setting (KMS) interface
-
-#include "EGL/egl.h"        // Native platform windowing system interface
-#include "EGL/eglext.h"     // EGL extensions
-
-#ifndef EGL_OPENGL_ES3_BIT
-    #define EGL_OPENGL_ES3_BIT  0x40
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#define USE_LAST_TOUCH_DEVICE       // When multiple touchscreens are connected, only use the one with the highest event<N> number
-
-#define DEFAULT_EVDEV_PATH       "/dev/input/"      // Path to the linux input events
-
-// So actually the biggest key is KEY_CNT but we only really map the keys up to
-// KEY_ALS_TOGGLE
-#define KEYMAP_SIZE KEY_ALS_TOGGLE
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-
-typedef struct {
-    // Display data
-    int fd;                             // File descriptor for /dev/dri/...
-    drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
-    drmModeCrtc *crtc;                  // CRT Controller
-    int modeIndex;                      // Index of the used mode of connector->modes
-    struct gbm_device *gbmDevice;       // GBM device
-    struct gbm_surface *gbmSurface;     // GBM surface
-    struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
-    uint32_t prevFB;                    // Previous GBM framebufer (during frame swapping)
-
-    EGLDisplay device;                  // Native display device (physical screen connection)
-    EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
-    EGLContext context;                 // Graphic context, mode in which drawing can be done
-    EGLConfig config;                   // Graphic config
-
-    // Keyboard data
-    int defaultKeyboardMode;            // Default keyboard mode
-    bool eventKeyboardMode;             // Keyboard in event mode
-    int defaultFileFlags;               // Default IO file flags
-    struct termios defaultSettings;     // Default keyboard settings
-    int keyboardFd;                     // File descriptor for the evdev keyboard
-
-    // Mouse data
-    Vector2 eventWheelMove;             // Registers the event mouse wheel variation
-    // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
-    char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
-    bool cursorRelative;                // Relative cursor mode
-    int mouseFd;                        // File descriptor for the evdev mouse/touch/gestures
-    Rectangle absRange;                 // Range of values for absolute pointing devices (touchscreens)
-    int touchSlot;                      // Hold the touch slot number of the currently being sent multitouch block
-
-    // Gamepad data
-    int gamepadStreamFd[MAX_GAMEPADS];  // Gamepad device file descriptor
-    int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXIS][2]; // [0] = min, [1] = range value of the axis
-    int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
-    int gamepadCount;                   // The number of gamepads registered
-} PlatformData;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-extern CoreData CORE;                   // Global CORE state context
-
-static PlatformData platform = { 0 };   // Platform specific data
-
-//----------------------------------------------------------------------------------
-// Local Variables Definition
-//----------------------------------------------------------------------------------
-
-// NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h
-// TODO: Complete the LUT with all unicode decimal values
-// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
-// Currently non US keyboards will have the wrong mapping for some keys
-// NOTE: Replacing this with the keymap from X11 would probably be useless, as people use the drm
-// backend to *avoid* X11
-static const int evkeyToUnicodeLUT[] = {
-    0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114,
-    116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59,
-    39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32
-    // LUT currently incomplete, just mapped the most essential keys
-};
-
-// This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h'
-// NOTE: Use short here to save a little memory
-static const short linuxToRaylibMap[KEYMAP_SIZE] = {
-    // We don't map those with designated initialization, because we would getting
-    // into loads of naming conflicts
-    0,   256, 49,  50,  51,  52,  53,  54,
-    55,  56,  57,  48,  45,  61,  259, 258,
-    81,  87,  69,  82,  84,  89,  85,  73,
-    79,  80,  91,  93,  257, 341, 65,  83,
-    68,  70,  71,  72,  74,  75,  76,  59,
-    39,  96,  340, 92,  90,  88,  67,  86,
-    66,  78,  77,  44,  46,  47,  344, 332,
-    342, 32,  280, 290, 291, 292, 293, 294,
-    295, 296, 297, 298, 299, 282, 281, 327,
-    328, 329, 333, 324, 325, 326, 334, 321,
-    322, 323, 320, 330, 0,   85,  86,  300,
-    301, 89,  90,  91,  92,  93,  94,  95,
-    335, 345, 331, 283, 346, 101, 268, 265,
-    266, 263, 262, 269, 264, 267, 260, 261,
-    112, 113, 114, 115, 116, 117, 118, 119,
-    120, 121, 122, 123, 124, 125, 347, 127,
-    128, 129, 130, 131, 132, 133, 134, 135,
-    136, 137, 138, 139, 140, 141, 142, 143,
-    144, 145, 146, 147, 148, 149, 150, 151,
-    152, 153, 154, 155, 156, 157, 158, 159,
-    160, 161, 162, 163, 164, 165, 166, 167,
-    168, 169, 170, 171, 172, 173, 174, 175,
-    176, 177, 178, 179, 180, 181, 182, 183,
-    184, 185, 186, 187, 188, 189, 190, 191,
-    192, 193, 194, 0,   0,   0,   0,   0,
-    200, 201, 202, 203, 204, 205, 206, 207,
-    208, 209, 210, 211, 212, 213, 214, 215,
-    216, 217, 218, 219, 220, 221, 222, 223,
-    224, 225, 226, 227, 228, 229, 230, 231,
-    232, 233, 234, 235, 236, 237, 238, 239,
-    240, 241, 242, 243, 244, 245, 246, 247,
-    248, 0,   0,   0,   0,   0,   0,   0,
-
-    // Gamepads are mapped according to:
-    // https://www.kernel.org/doc/html/next/input/gamepad.html
-    // Those mappings are standardized, but that doesn't mean people follow
-    // the standards, so this is more of an approximation
-    [BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
-    [BTN_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
-    [BTN_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
-    [BTN_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
-    [BTN_Y] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
-    [BTN_B] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
-    [BTN_A] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
-    [BTN_X] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
-    [BTN_TL] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
-    [BTN_TL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
-    [BTN_TR] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
-    [BTN_TR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
-    [BTN_SELECT] = GAMEPAD_BUTTON_MIDDLE_LEFT,
-    [BTN_MODE] = GAMEPAD_BUTTON_MIDDLE,
-    [BTN_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
-    [BTN_THUMBL] = GAMEPAD_BUTTON_LEFT_THUMB,
-    [BTN_THUMBR] = GAMEPAD_BUTTON_RIGHT_THUMB,
-};
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Declaration
-//----------------------------------------------------------------------------------
-int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
-void ClosePlatform(void);        // Close platform
-
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-static void InitKeyboard(void);                 // Initialize raw keyboard system
-static void RestoreKeyboard(void);              // Restore keyboard system
-static void ProcessKeyboard(void);              // Process keyboard events
-#endif
-
-static void InitEvdevInput(void);               // Initialize evdev inputs
-static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
-static void PollKeyboardEvents(void);           // Process evdev keyboard events
-static void PollGamepadEvents(void);            // Process evdev gamepad events
-static void PollMouseEvents(void);              // Process evdev mouse events
-
-static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode);                               // Search matching DRM mode in connector's mode list
-static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);      // Search exactly matching DRM connector mode in connector's list
-static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);    // Search the nearest matching DRM connector mode in connector's list
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-// NOTE: Functions declaration is provided by raylib.h
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Window and Graphics Device
-//----------------------------------------------------------------------------------
-
-// Check if application should close
-// NOTE: By default, if KEY_ESCAPE pressed
-bool WindowShouldClose(void)
-{
-    if (CORE.Window.ready) return CORE.Window.shouldClose;
-    else return true;
-}
-
-// Toggle fullscreen mode
-void ToggleFullscreen(void)
-{
-    TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
-}
-
-// Toggle borderless windowed mode
-void ToggleBorderlessWindowed(void)
-{
-    TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
-}
-
-// Set window state: maximized, if resizable
-void MaximizeWindow(void)
-{
-    TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
-}
-
-// Set window state: minimized
-void MinimizeWindow(void)
-{
-    TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
-}
-
-// Set window state: not minimized/maximized
-void RestoreWindow(void)
-{
-    TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
-}
-
-// Set window configuration state using flags
-void SetWindowState(unsigned int flags)
-{
-    TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
-}
-
-// Clear window configuration state flags
-void ClearWindowState(unsigned int flags)
-{
-    TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
-}
-
-// Set icon for window
-void SetWindowIcon(Image image)
-{
-    TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
-}
-
-// Set icon for window
-void SetWindowIcons(Image *images, int count)
-{
-    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
-}
-
-// Set title for window
-void SetWindowTitle(const char *title)
-{
-    CORE.Window.title = title;
-}
-
-// Set window position on screen (windowed mode)
-void SetWindowPosition(int x, int y)
-{
-    TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
-}
-
-// Set monitor for the current window
-void SetWindowMonitor(int monitor)
-{
-    TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
-}
-
-// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
-void SetWindowMinSize(int width, int height)
-{
-    CORE.Window.screenMin.width = width;
-    CORE.Window.screenMin.height = height;
-}
-
-// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
-void SetWindowMaxSize(int width, int height)
-{
-    CORE.Window.screenMax.width = width;
-    CORE.Window.screenMax.height = height;
-}
-
-// Set window dimensions
-void SetWindowSize(int width, int height)
-{
-    TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
-}
-
-// Set window opacity, value opacity is between 0.0 and 1.0
-void SetWindowOpacity(float opacity)
-{
-    TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
-}
-
-// Set window focused
-void SetWindowFocused(void)
-{
-    TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
-}
-
-// Get native window handle
-void *GetWindowHandle(void)
-{
-    TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
-    return NULL;
-}
-
-// Get number of monitors
-int GetMonitorCount(void)
-{
-    TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
-    return 1;
-}
-
-// Get number of monitors
-int GetCurrentMonitor(void)
-{
-    TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
-    return 0;
-}
-
-// Get selected monitor position
-Vector2 GetMonitorPosition(int monitor)
-{
-    TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
-    return (Vector2){ 0, 0 };
-}
-
-// Get selected monitor width (currently used by monitor)
-int GetMonitorWidth(int monitor)
-{
-    int width = 0;
-
-    if (monitor != 0)
-    {
-        TRACELOG(LOG_WARNING, "GetMonitorWidth() implemented for first monitor only");
-    }
-    else if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        width = platform.connector->modes[platform.modeIndex].hdisplay;
-    }
-
-    return width;
-}
-
-// Get selected monitor height (currently used by monitor)
-int GetMonitorHeight(int monitor)
-{
-    int height = 0;
-
-    if (monitor != 0)
-    {
-        TRACELOG(LOG_WARNING, "GetMonitorHeight() implemented for first monitor only");
-    }
-    else if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        height = platform.connector->modes[platform.modeIndex].vdisplay;
-    }
-
-    return height;
-}
-
-// Get selected monitor physical width in millimetres
-int GetMonitorPhysicalWidth(int monitor)
-{
-    int physicalWidth = 0;
-
-    if (monitor != 0)
-    {
-        TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() implemented for first monitor only");
-    }
-    else if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        physicalWidth = platform.connector->mmWidth;
-    }
-
-    return physicalWidth;
-}
-
-// Get selected monitor physical height in millimetres
-int GetMonitorPhysicalHeight(int monitor)
-{
-    int physicalHeight = 0;
-
-    if (monitor != 0)
-    {
-        TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() implemented for first monitor only");
-    }
-    else if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        physicalHeight = platform.connector->mmHeight;
-    }
-
-    return physicalHeight;
-}
-
-// Get selected monitor refresh rate
-int GetMonitorRefreshRate(int monitor)
-{
-    int refresh = 0;
-
-    if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        refresh = platform.connector->modes[platform.modeIndex].vrefresh;
-    }
-
-    return refresh;
-}
-
-// Get the human-readable, UTF-8 encoded name of the selected monitor
-const char *GetMonitorName(int monitor)
-{
-    const char *name = "";
-
-    if (monitor != 0)
-    {
-        TRACELOG(LOG_WARNING, "GetMonitorName() implemented for first monitor only");
-    }
-    else if ((platform.connector) && (platform.modeIndex >= 0))
-    {
-        name = platform.connector->modes[platform.modeIndex].name;
-    }
-
-    return name;
-}
-
-// Get window position XY on monitor
-Vector2 GetWindowPosition(void)
-{
-    return (Vector2){ 0, 0 };
-}
-
-// Get window scale DPI factor for current monitor
-Vector2 GetWindowScaleDPI(void)
-{
-    return (Vector2){ 1.0f, 1.0f };
-}
-
-// Set clipboard text content
-void SetClipboardText(const char *text)
-{
-    TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
-}
-
-// Get clipboard text content
-// NOTE: returned string is allocated and freed by GLFW
-const char *GetClipboardText(void)
-{
-    TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
-    return NULL;
-}
-
-// Show mouse cursor
-void ShowCursor(void)
-{
-    CORE.Input.Mouse.cursorHidden = false;
-}
-
-// Hides mouse cursor
-void HideCursor(void)
-{
-    CORE.Input.Mouse.cursorHidden = true;
-}
-
-// Enables cursor (unlock cursor)
-void EnableCursor(void)
-{
-    // Set cursor position in the middle
-    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
-
-    platform.cursorRelative = false;
-    CORE.Input.Mouse.cursorHidden = false;
-}
-
-// Disables cursor (lock cursor)
-void DisableCursor(void)
-{
-    // Set cursor position in the middle
-    SetMousePosition(0, 0);
-
-    platform.cursorRelative = true;
-    CORE.Input.Mouse.cursorHidden = true;
-}
-
-// Swap back buffer with front buffer (screen drawing)
-void SwapScreenBuffer(void)
-{
-    eglSwapBuffers(platform.device, platform.surface);
-
-    if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
-
-    struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
-    if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
-
-    uint32_t fb = 0;
-    int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
-    if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
-
-    result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]);
-    if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
-
-    if (platform.prevFB)
-    {
-        result = drmModeRmFB(platform.fd, platform.prevFB);
-        if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
-    }
-
-    platform.prevFB = fb;
-
-    if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
-
-    platform.prevBO = bo;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Misc
-//----------------------------------------------------------------------------------
-
-// Get elapsed time measure in seconds since InitTimer()
-double GetTime(void)
-{
-    double time = 0.0;
-    struct timespec ts = { 0 };
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-    unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
-    time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
-
-    return time;
-}
-
-// Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
-void OpenURL(const char *url)
-{
-    TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Inputs
-//----------------------------------------------------------------------------------
-
-// Set internal gamepad mappings
-int SetGamepadMappings(const char *mappings)
-{
-    TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
-    return 0;
-}
-
-// Set gamepad vibration
-void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
-{
-    TRACELOG(LOG_WARNING, "GamepadSetVibration() not implemented on target platform");
-}
-
-// Set mouse position XY
-void SetMousePosition(int x, int y)
-{
-    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
-    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
-}
-
-// Set mouse cursor
-void SetMouseCursor(int cursor)
-{
-    TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
-}
-
-// Get physical key name.
-const char *GetKeyName(int key)
-{
-    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
-    return "";
-}
-
-// Register all input events
-void PollInputEvents(void)
-{
-#if defined(SUPPORT_GESTURES_SYSTEM)
-    // NOTE: Gestures update must be called every frame to reset gestures correctly
-    // because ProcessGestureEvent() is just called on an event, not every frame
-    UpdateGestures();
-#endif
-
-    // Reset keys/chars pressed registered
-    CORE.Input.Keyboard.keyPressedQueueCount = 0;
-    CORE.Input.Keyboard.charPressedQueueCount = 0;
-
-    // Reset last gamepad button/axis registered state
-    CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
-    //CORE.Input.Gamepad.axisCount = 0;
-
-    // Register previous keys states
-    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
-    {
-        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
-        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
-    }
-
-    PollKeyboardEvents();
-
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-    // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here.
-    // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
-    if (!platform.eventKeyboardMode) ProcessKeyboard();
-#endif
-
-    // Check exit key
-    if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
-
-    // Register previous mouse position
-    if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
-    else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
-
-    // Register previous mouse states
-    CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
-    CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove;
-    platform.eventWheelMove = (Vector2){ 0.0f, 0.0f };
-
-    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
-    {
-        CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
-        CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i];
-        CORE.Input.Touch.currentTouchState[i] = platform.currentButtonStateEvdev[i];
-    }
-
-    // Register gamepads buttons events
-    PollGamepadEvents();
-
-    // Register previous touch states
-    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
-
-    // Reset touch positions
-    //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
-
-    // Map touch position to mouse position for convenience
-    CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
-
-    // Handle the mouse/touch/gestures events:
-    PollMouseEvents();
-}
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Definition
-//----------------------------------------------------------------------------------
-
-// Initialize platform: graphics, inputs and more
-int InitPlatform(void)
-{
-    platform.fd = -1;
-    platform.connector = NULL;
-    platform.modeIndex = -1;
-    platform.crtc = NULL;
-    platform.gbmDevice = NULL;
-    platform.gbmSurface = NULL;
-    platform.prevBO = NULL;
-    platform.prevFB = 0;
-
-    // Initialize graphic device: display/window and graphic context
-    //----------------------------------------------------------------------------
-    CORE.Window.fullscreen = true;
-    CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
-
-#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
-    platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
-#else
-    TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card");
-    platform.fd = open("/dev/dri/by-path/platform-gpu-card",  O_RDWR); // VideoCore VI (Raspberry Pi 4)
-
-    if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
-    {
-        TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1");
-        platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
-    }
-
-    if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL))
-    {
-        TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0");
-        platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
-    }
-#endif
-
-    if (platform.fd == -1)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
-        return -1;
-    }
-
-    drmModeRes *res = drmModeGetResources(platform.fd);
-    if (!res)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
-        return -1;
-    }
-
-    TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
-
-    for (size_t i = 0; i < res->count_connectors; i++)
-    {
-        TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
-
-        drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]);
-        TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes);
-
-        // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected.
-        // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output.
-        if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->encoder_id))
-        {
-            TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected");
-            platform.connector = con;
-            break;
-        }
-        else
-        {
-            TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)");
-            drmModeFreeConnector(con);
-        }
-    }
-
-    if (!platform.connector)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
-        drmModeFreeResources(res);
-        return -1;
-    }
-
-    drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
-    if (!enc)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
-        drmModeFreeResources(res);
-        return -1;
-    }
-
-    platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
-    if (!platform.crtc)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
-        drmModeFreeEncoder(enc);
-        drmModeFreeResources(res);
-        return -1;
-    }
-
-    // If InitWindow should use the current mode find it in the connector's mode list
-    if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
-    {
-        TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
-
-        platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode);
-
-        if (platform.modeIndex < 0)
-        {
-            TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
-            drmModeFreeEncoder(enc);
-            drmModeFreeResources(res);
-            return -1;
-        }
-
-        CORE.Window.screen.width = CORE.Window.display.width;
-        CORE.Window.screen.height = CORE.Window.display.height;
-    }
-
-    const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT;
-    const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
-
-    // Try to find an exact matching mode
-    platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
-
-    // If nothing found, try to find a nearly matching mode
-    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
-
-    // If nothing found, try to find an exactly matching mode including interlaced
-    if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
-
-    // If nothing found, try to find a nearly matching mode including interlaced
-    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
-
-    // If nothing found, there is no suitable mode
-    if (platform.modeIndex < 0)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
-        drmModeFreeEncoder(enc);
-        drmModeFreeResources(res);
-        return -1;
-    }
-
-    CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
-    CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay;
-
-    TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
-        platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
-        (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
-        platform.connector->modes[platform.modeIndex].vrefresh);
-
-    // Use the width and height of the surface for render
-    CORE.Window.render.width = CORE.Window.screen.width;
-    CORE.Window.render.height = CORE.Window.screen.height;
-
-    drmModeFreeEncoder(enc);
-    enc = NULL;
-
-    drmModeFreeResources(res);
-    res = NULL;
-
-    platform.gbmDevice = gbm_create_device(platform.fd);
-    if (!platform.gbmDevice)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
-        return -1;
-    }
-
-    platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
-        platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
-    if (!platform.gbmSurface)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
-        return -1;
-    }
-
-    EGLint samples = 0;
-    EGLint sampleBuffer = 0;
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
-    {
-        samples = 4;
-        sampleBuffer = 1;
-        TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
-    }
-
-    const EGLint framebufferAttribs[] =
-    {
-        EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,      // Type of context support
-        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,          // Don't use it on Android!
-        EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
-        EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
-        EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
-        EGL_ALPHA_SIZE, 8,        // ALPHA bit depth (required for transparent framebuffer)
-        //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
-        EGL_DEPTH_SIZE, 16,         // Depth buffer size (Required to use Depth testing!)
-        //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
-        EGL_SAMPLE_BUFFERS, sampleBuffer,    // Activate MSAA
-        EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
-        EGL_NONE
-    };
-
-    const EGLint contextAttribs[] = {
-        EGL_CONTEXT_CLIENT_VERSION, 2,
-        EGL_NONE
-    };
-
-    EGLint numConfigs = 0;
-
-    // Get an EGL device connection
-    platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
-    if (platform.device == EGL_NO_DISPLAY)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return -1;
-    }
-
-    // Initialize the EGL device connection
-    if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
-    {
-        // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return -1;
-    }
-
-    if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
-        return -1;
-    }
-
-    TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
-
-    EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs));
-    if (!configs)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
-        return -1;
-    }
-
-    EGLint matchingNumConfigs = 0;
-    if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
-        free(configs);
-        return -1;
-    }
-
-    TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
-
-    // find the EGL config that matches the previously setup GBM format
-    int found = 0;
-    for (EGLint i = 0; i < matchingNumConfigs; ++i)
-    {
-        EGLint id = 0;
-        if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
-        {
-            TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
-            continue;
-        }
-
-        if (GBM_FORMAT_ARGB8888 == id)
-        {
-            TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
-            platform.config = configs[i];
-            found = 1;
-            break;
-        }
-    }
-
-    RL_FREE(configs);
-
-    if (!found)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
-        return -1;
-    }
-
-    // Set rendering API
-    eglBindAPI(EGL_OPENGL_ES_API);
-
-    // Create an EGL rendering context
-    platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
-    if (platform.context == EGL_NO_CONTEXT)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
-        return -1;
-    }
-
-    // Create an EGL window surface
-    platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL);
-    if (EGL_NO_SURFACE == platform.surface)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
-        return -1;
-    }
-
-    // At this point we need to manage render size vs screen size
-    // NOTE: This function use and modify global module variables:
-    //  -> CORE.Window.screen.width/CORE.Window.screen.height
-    //  -> CORE.Window.render.width/CORE.Window.render.height
-    //  -> CORE.Window.screenScale
-    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
-
-    // There must be at least one frame displayed before the buffers are swapped
-    //eglSwapInterval(platform.device, 1);
-
-    EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
-
-    // Check surface and context activation
-    if (result != EGL_FALSE)
-    {
-        CORE.Window.ready = true;
-
-        CORE.Window.render.width = CORE.Window.screen.width;
-        CORE.Window.render.height = CORE.Window.screen.height;
-        CORE.Window.currentFbo.width = CORE.Window.render.width;
-        CORE.Window.currentFbo.height = CORE.Window.render.height;
-
-        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
-        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
-        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
-        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
-        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
-    }
-    else
-    {
-        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
-        return -1;
-    }
-
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
-
-    // If graphic device is no properly initialized, we end program
-    if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
-    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
-
-    // Set some default window flags
-    CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN;       // false
-    CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;    // false
-    CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;     // true
-    CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;    // false
-
-    // Load OpenGL extensions
-    // NOTE: GL procedures address loader is required to load extensions
-    rlLoadExtensions(eglGetProcAddress);
-    //----------------------------------------------------------------------------
-
-    // Initialize timming system
-    //----------------------------------------------------------------------------
-    // NOTE: timming system must be initialized before the input events system
-    InitTimer();
-    //----------------------------------------------------------------------------
-
-    // Initialize input events system
-    //----------------------------------------------------------------------------
-    InitEvdevInput();   // Evdev inputs initialization
-
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-    InitKeyboard();     // Keyboard init (stdin)
-#endif
-    //----------------------------------------------------------------------------
-
-    // Initialize storage system
-    //----------------------------------------------------------------------------
-    CORE.Storage.basePath = GetWorkingDirectory();
-    //----------------------------------------------------------------------------
-
-    TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
-
-    return 0;
-}
-
-// Close platform
-void ClosePlatform(void)
-{
-    if (platform.prevFB)
-    {
-        drmModeRmFB(platform.fd, platform.prevFB);
-        platform.prevFB = 0;
-    }
-
-    if (platform.prevBO)
-    {
-        gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
-        platform.prevBO = NULL;
-    }
-
-    if (platform.gbmSurface)
-    {
-        gbm_surface_destroy(platform.gbmSurface);
-        platform.gbmSurface = NULL;
-    }
-
-    if (platform.gbmDevice)
-    {
-        gbm_device_destroy(platform.gbmDevice);
-        platform.gbmDevice = NULL;
-    }
-
-    if (platform.crtc)
-    {
-        if (platform.connector)
-        {
-            drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id,
-                platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode);
-            drmModeFreeConnector(platform.connector);
-            platform.connector = NULL;
-        }
-
-        drmModeFreeCrtc(platform.crtc);
-        platform.crtc = NULL;
-    }
-
-    if (platform.fd != -1)
-    {
-        close(platform.fd);
-        platform.fd = -1;
-    }
-
-    // Close surface, context and display
-    if (platform.device != EGL_NO_DISPLAY)
-    {
-        if (platform.surface != EGL_NO_SURFACE)
-        {
-            eglDestroySurface(platform.device, platform.surface);
-            platform.surface = EGL_NO_SURFACE;
-        }
-
-        if (platform.context != EGL_NO_CONTEXT)
-        {
-            eglDestroyContext(platform.device, platform.context);
-            platform.context = EGL_NO_CONTEXT;
-        }
-
-        eglTerminate(platform.device);
-        platform.device = EGL_NO_DISPLAY;
-    }
-
-    CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
-
-    // Close the evdev devices
-
-    if (platform.mouseFd != -1)
-    {
-        close(platform.mouseFd);
-        platform.mouseFd = -1;
-    }
-
-    for (int i = 0; i < platform.gamepadCount; i++)
-    {
-        close(platform.gamepadStreamFd[i]);
-        platform.gamepadStreamFd[i] = -1;
-    }
-
-    if (platform.keyboardFd != -1)
-    {
-        close(platform.keyboardFd);
-        platform.keyboardFd = -1;
-    }
-}
-
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-// Initialize Keyboard system (using standard input)
-static void InitKeyboard(void)
-{
-    // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
-    // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
-
-    // Save terminal keyboard settings
-    tcgetattr(STDIN_FILENO, &platform.defaultSettings);
-
-    // Reconfigure terminal with new settings
-    struct termios keyboardNewSettings = { 0 };
-    keyboardNewSettings = platform.defaultSettings;
-
-    // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
-    // NOTE: ISIG controls if ^C and ^Z generate break signals or not
-    keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG);
-    //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
-    keyboardNewSettings.c_cc[VMIN] = 1;
-    keyboardNewSettings.c_cc[VTIME] = 0;
-
-    // Set new keyboard settings (change occurs immediately)
-    tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
-
-    // Save old keyboard mode to restore it at the end
-    platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
-    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
-
-    // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
-    int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode);
-
-    // In case of failure, it could mean a remote keyboard is used (SSH)
-    if (result < 0) TRACELOG(LOG_WARNING, "DRM: Failed to change keyboard mode, an SSH keyboard is probably used");
-    else
-    {
-        // Reconfigure keyboard mode to get:
-        //    - scancodes (K_RAW)
-        //    - keycodes (K_MEDIUMRAW)
-        //    - ASCII chars (K_XLATE)
-        //    - UNICODE chars (K_UNICODE)
-        ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE);  // ASCII chars
-    }
-
-    // Register keyboard restore when program finishes
-    atexit(RestoreKeyboard);
-}
-
-// Restore default keyboard input
-static void RestoreKeyboard(void)
-{
-    // Reset to default keyboard settings
-    tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings);
-
-    // Reconfigure keyboard to default mode
-    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags);
-    ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
-}
-
-// Process keyboard inputs
-static void ProcessKeyboard(void)
-{
-    #define MAX_KEYBUFFER_SIZE      32      // Max size in bytes to read
-
-    // Keyboard input polling (fill keys[256] array with status)
-    int bufferByteCount = 0;                        // Bytes available on the buffer
-    char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 };    // Max keys to be read at a time
-
-    // Read availables keycodes from stdin
-    bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE);     // POSIX system call
-
-    // Reset pressed keys array (it will be filled below)
-    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
-    {
-        CORE.Input.Keyboard.currentKeyState[i] = 0;
-        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
-    }
-
-    // Fill all read bytes (looking for keys)
-    for (int i = 0; i < bufferByteCount; i++)
-    {
-        // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
-        // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
-        if (keysBuffer[i] == 0x1b)
-        {
-            // Check if ESCAPE key has been pressed to stop program
-            if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
-            else
-            {
-                if (keysBuffer[i + 1] == 0x5b)    // Special function key
-                {
-                    if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
-                    {
-                        // Process special function keys (F1 - F12)
-                        switch (keysBuffer[i + 3])
-                        {
-                            case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break;    // raylib KEY_F1
-                            case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break;    // raylib KEY_F2
-                            case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break;    // raylib KEY_F3
-                            case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break;    // raylib KEY_F4
-                            case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break;    // raylib KEY_F5
-                            case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break;    // raylib KEY_F6
-                            case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break;    // raylib KEY_F7
-                            case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break;    // raylib KEY_F8
-                            case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break;    // raylib KEY_F9
-                            case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break;    // raylib KEY_F10
-                            case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break;    // raylib KEY_F11
-                            case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break;    // raylib KEY_F12
-                            default: break;
-                        }
-
-                        if (keysBuffer[i + 2] == 0x5b) i += 4;
-                        else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
-                    }
-                    else
-                    {
-                        switch (keysBuffer[i + 2])
-                        {
-                            case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break;    // raylib KEY_UP
-                            case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break;    // raylib KEY_DOWN
-                            case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break;    // raylib KEY_RIGHT
-                            case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break;    // raylib KEY_LEFT
-                            default: break;
-                        }
-
-                        i += 3;  // Jump to next key
-                    }
-
-                    // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
-                }
-            }
-        }
-        else if (keysBuffer[i] == 0x0a)     // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
-        {
-            CORE.Input.Keyboard.currentKeyState[257] = 1;
-
-            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
-            CORE.Input.Keyboard.keyPressedQueueCount++;
-        }
-        else if (keysBuffer[i] == 0x7f)     // raylib KEY_BACKSPACE
-        {
-            CORE.Input.Keyboard.currentKeyState[259] = 1;
-
-            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
-            CORE.Input.Keyboard.keyPressedQueueCount++;
-        }
-        else
-        {
-            // Translate lowercase a-z letters to A-Z
-            if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
-            {
-                CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
-            }
-            else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
-
-            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i];     // Add keys pressed into queue
-            CORE.Input.Keyboard.keyPressedQueueCount++;
-        }
-    }
-}
-#endif  // SUPPORT_SSH_KEYBOARD_RPI
-
-// Initialise user input from evdev(/dev/input/event<N>)
-// this means mouse, keyboard or gamepad devices
-static void InitEvdevInput(void)
-{
-    char path[MAX_FILEPATH_LENGTH] = { 0 };
-    DIR *directory = NULL;
-    struct dirent *entity = NULL;
-
-    // Initialise keyboard file descriptor
-    platform.keyboardFd = -1;
-    platform.mouseFd = -1;
-
-    // Reset variables
-    for (int i = 0; i < MAX_TOUCH_POINTS; ++i)
-    {
-        CORE.Input.Touch.position[i].x = -1;
-        CORE.Input.Touch.position[i].y = -1;
-    }
-
-    // Reset keyboard key state
-    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
-    {
-        CORE.Input.Keyboard.currentKeyState[i] = 0;
-        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
-    }
-
-    // Open the linux directory of "/dev/input"
-    directory = opendir(DEFAULT_EVDEV_PATH);
-
-    if (directory)
-    {
-        while ((entity = readdir(directory)) != NULL)
-        {
-            if ((strncmp("event", entity->d_name, strlen("event")) == 0) ||     // Search for devices named "event*"
-                (strncmp("mouse", entity->d_name, strlen("mouse")) == 0))       // Search for devices named "mouse*"
-            {
-                sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
-                ConfigureEvdevDevice(path);                                     // Configure the device if appropriate
-            }
-        }
-
-        closedir(directory);
-    }
-    else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
-}
-
-// Identifies a input device and configures it for use if appropriate
-static void ConfigureEvdevDevice(char *device)
-{
-    #define BITS_PER_LONG   (8*sizeof(long))
-    #define NBITS(x)        ((((x) - 1)/BITS_PER_LONG) + 1)
-    #define OFF(x)          ((x)%BITS_PER_LONG)
-    #define BIT(x)          (1UL<<OFF(x))
-    #define LONG(x)         ((x)/BITS_PER_LONG)
-    #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
-
-    unsigned long evBits[NBITS(EV_MAX)] = { 0 };
-    unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
-    unsigned long relBits[NBITS(REL_MAX)] = { 0 };
-    unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
-
-    // Open the device
-    int fd = open(device, O_RDONLY | O_NONBLOCK);
-    if (fd < 0)
-    {
-        TRACELOG(LOG_WARNING, "DRM: Failed to open input device: %s", device);
-        return;
-    }
-
-    // At this point we have a connection to the device, but we don't yet know what the device is.
-    // It could be many things, even as simple as a power button...
-    //-------------------------------------------------------------------------------------------------------
-
-    // Identify the device
-    //-------------------------------------------------------------------------------------------------------
-    struct {
-        bool exist;
-        struct input_absinfo info;
-    } absinfo[ABS_CNT] = { 0 };
-
-    // These flags aren't really a one of
-    // Some devices could have properties we assosciate with keyboards as well as properties
-    // we assosciate with mice
-    bool isKeyboard = false;
-    bool isMouse = false;
-    bool isTouch = false;
-    bool isGamepad = false;
-
-    int absAxisCount = 0;
-
-    ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);
-    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
-
-    if (TEST_BIT(evBits, EV_ABS))
-    {
-        ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
-
-        // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
-        bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
-
-        if (hasAbsXY)
-        {
-            absAxisCount += 2;
-
-            absinfo[ABS_X].exist = true;
-            absinfo[ABS_Y].exist = true;
-
-            ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X].info);
-            ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y].info);
-        }
-
-        // If it has any of these buttons it's a touch device
-        if (hasAbsXY &&
-           (TEST_BIT(keyBits, BTN_STYLUS) ||
-            TEST_BIT(keyBits, BTN_TOOL_PEN) ||
-            TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
-            TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
-
-        // Absolute mice should really only exist with VMWare, but it shouldn't
-        // matter if we support them
-        else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
-
-        // If any of the common joystick axis is present, we assume it's a gamepad
-        else
-        {
-            for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
-            {
-                if (TEST_BIT(absBits, axis))
-                {
-                    absinfo[axis].exist = true;
-                    isGamepad = true;
-                    absAxisCount++;
-
-                    ioctl(fd, EVIOCGABS(axis), &absinfo[axis].info);
-                }
-            }
-        }
-
-        // If the device has multitouch axes, it's a touch device
-        if (TEST_BIT(absBits, ABS_MT_POSITION_X) &&
-            TEST_BIT(absBits, ABS_MT_POSITION_Y)) isTouch = true;
-    }
-
-    if (TEST_BIT(evBits, EV_REL))
-    {
-        ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
-
-        // If it has any of the gamepad or touch features we tested so far, it's not a mouse
-        if (!isTouch &&
-            !isGamepad &&
-            TEST_BIT(relBits, REL_X) &&
-            TEST_BIT(relBits, REL_Y) &&
-            TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
-    }
-
-    if (TEST_BIT(evBits, EV_KEY))
-    {
-        // The first 32 keys as defined in input-event-codes.h are pretty much
-        // exclusive to keyboards, so we can test them using a mask
-        // Leave out the first bit to not test KEY_RESERVED
-        const unsigned long mask = 0xFFFFFFFE;
-        if ((keyBits[0] & mask) == mask) isKeyboard = true;
-
-        // If we find any of the common gamepad buttons we assume it's a gamepad
-        else
-        {
-            for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
-            {
-                if (TEST_BIT(keyBits, button)) isGamepad = true;
-            }
-
-            for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
-            {
-                if (TEST_BIT(keyBits, button)) isGamepad = true;
-            }
-        }
-    }
-
-    const char *deviceKindStr = "unknown";
-    if (isMouse || isTouch)
-    {
-        deviceKindStr = "mouse";
-        if (platform.mouseFd != -1) close(platform.mouseFd);
-        platform.mouseFd = fd;
-
-        if (absAxisCount > 0)
-        {
-            platform.absRange.x = absinfo[ABS_X].info.minimum;
-            platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum;
-
-            platform.absRange.y = absinfo[ABS_Y].info.minimum;
-            platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
-        }
-    }
-    else if (isGamepad && !isMouse && !isKeyboard && platform.gamepadCount < MAX_GAMEPADS)
-    {
-        deviceKindStr = "gamepad";
-        int index = platform.gamepadCount++;
-
-        platform.gamepadStreamFd[index] = fd;
-        CORE.Input.Gamepad.ready[index] = true;
-
-        ioctl(platform.gamepadStreamFd[index], EVIOCGNAME(64), &CORE.Input.Gamepad.name[index]);
-        CORE.Input.Gamepad.axisCount[index] = absAxisCount;
-
-        if (absAxisCount > 0)
-        {
-            // TODO / NOTE
-            // So gamepad axis (as in the actual linux joydev.c) are just simply enumerated
-            // and (at least for some input drivers like xpat) it's convention to use
-            // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
-            // shoulder buttons
-            // If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
-            // That means they don't match the GamepadAxis enum
-            // This could be fixed
-            int axisIndex = 0;
-            for (int axis = ABS_X; axis < ABS_PRESSURE; axis++)
-            {
-                if (absinfo[axis].exist)
-                {
-                    platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].info.minimum;
-                    platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].info.maximum - absinfo[axisIndex].info.minimum;
-
-                    platform.gamepadAbsAxisMap[index][axis] = axisIndex;
-                    axisIndex++;
-                }
-            }
-        }
-    }
-    else if (isKeyboard && (platform.keyboardFd == -1))
-    {
-        deviceKindStr = "keyboard";
-        platform.keyboardFd = fd;
-    }
-    else
-    {
-        close(fd);
-        return;
-    }
-
-    TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
-}
-
-// Poll and process evdev keyboard events
-static void PollKeyboardEvents(void)
-{
-    int fd = platform.keyboardFd;
-    if (fd == -1) return;
-
-    struct input_event event = { 0 };
-    int keycode = -1;
-
-    // Try to read data from the keyboard and only continue if successful
-    while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
-    {
-        // Check if the event is a key event
-        if (event.type != EV_KEY) continue;
-
-#if defined(SUPPORT_SSH_KEYBOARD_RPI)
-        // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
-        platform.eventKeyboardMode = true;
-#endif
-
-        // Keyboard keys appear for codes 1 to 255, ignore everthing else
-        if ((event.code >= 1) && (event.code <= 255))
-        {
-
-            // Lookup the scancode in the keymap to get a keycode
-            keycode = linuxToRaylibMap[event.code];
-
-            // Make sure we got a valid keycode
-            if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
-            {
-
-                // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
-                // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
-                // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
-                CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1);
-                CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2);
-
-                // If the key is pressed add it to the queues
-                if (event.value == 1)
-                {
-                    if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
-                    {
-                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
-                        CORE.Input.Keyboard.keyPressedQueueCount++;
-                    }
-
-                    if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
-                    {
-                        // TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
-                        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = evkeyToUnicodeLUT[event.code];
-                        CORE.Input.Keyboard.charPressedQueueCount++;
-                    }
-                }
-
-                TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0)? "UP  " : "DOWN", event.code, keycode);
-            }
-        }
-    }
-}
-
-// Poll gamepad input events
-static void PollGamepadEvents(void)
-{
-    // Read gamepad event
-    struct input_event event = { 0 };
-
-    for (int i = 0; i < platform.gamepadCount; i++)
-    {
-        if (!CORE.Input.Gamepad.ready[i]) continue;
-
-        // Register previous gamepad states
-        for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
-
-        while (read(platform.gamepadStreamFd[i], &event, sizeof(event)) == (int)sizeof(event))
-        {
-            if (event.type == EV_KEY)
-            {
-                if (event.code < KEYMAP_SIZE)
-                {
-                    short keycodeRaylib = linuxToRaylibMap[event.code];
-
-                    TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycodeRaylib);
-
-                    if ((keycodeRaylib != 0) && (keycodeRaylib < MAX_GAMEPAD_BUTTONS))
-                    {
-                        // 1 - button pressed, 0 - button released
-                        CORE.Input.Gamepad.currentButtonState[i][keycodeRaylib] = event.value;
-
-                        CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycodeRaylib : GAMEPAD_BUTTON_UNKNOWN;
-                    }
-                }
-            }
-            else if (event.type == EV_ABS)
-            {
-                if (event.code < ABS_CNT)
-                {
-                    int axisRaylib = platform.gamepadAbsAxisMap[i][event.code];
-
-                    TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value);
-
-                    if (axisRaylib < MAX_GAMEPAD_AXIS)
-                    {
-                        int min = platform.gamepadAbsAxisRange[i][event.code][0];
-                        int range = platform.gamepadAbsAxisRange[i][event.code][1];
-
-                        // NOTE: Scaling of event.value to get values between -1..1
-                        CORE.Input.Gamepad.axisState[i][axisRaylib] = (2*(float)(event.value - min)/range) - 1;
-                    }
-                }
-            }
-        }
-    }
-}
-
-// Poll mouse input events
-static void PollMouseEvents(void)
-{
-    int fd = platform.mouseFd;
-    if (fd == -1) return;
-
-    struct input_event event = { 0 };
-    int touchAction = -1;           // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
-
-    // Try to read data from the mouse/touch/gesture and only continue if successful
-    while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
-    {
-        // Relative movement parsing
-        if (event.type == EV_REL)
-        {
-            if (event.code == REL_X)
-            {
-                if (platform.cursorRelative)
-                {
-                    CORE.Input.Mouse.currentPosition.x = event.value;
-                    CORE.Input.Mouse.previousPosition.x = 0.0f;
-                }
-                else CORE.Input.Mouse.currentPosition.x += event.value;
-
-                CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
-                touchAction = 2;    // TOUCH_ACTION_MOVE
-            }
-
-            if (event.code == REL_Y)
-            {
-                if (platform.cursorRelative)
-                {
-                    CORE.Input.Mouse.currentPosition.y = event.value;
-                    CORE.Input.Mouse.previousPosition.y = 0.0f;
-                }
-                else CORE.Input.Mouse.currentPosition.y += event.value;
-
-                CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
-                touchAction = 2;    // TOUCH_ACTION_MOVE
-            }
-
-            if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
-        }
-
-        // Absolute movement parsing
-        if (event.type == EV_ABS)
-        {
-            // Basic movement
-            if (event.code == ABS_X)
-            {
-                CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;    // Scale according to absRange
-                CORE.Input.Touch.position[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;        // Scale according to absRange
-
-                touchAction = 2;    // TOUCH_ACTION_MOVE
-            }
-
-            if (event.code == ABS_Y)
-            {
-                CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;  // Scale according to absRange
-                CORE.Input.Touch.position[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;      // Scale according to absRange
-
-                touchAction = 2;    // TOUCH_ACTION_MOVE
-            }
-
-            // Multitouch movement
-            if (event.code == ABS_MT_SLOT) platform.touchSlot = event.value;   // Remember the slot number for the folowing events
-
-            if (event.code == ABS_MT_POSITION_X)
-            {
-                if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;    // Scale according to absRange
-            }
-
-            if (event.code == ABS_MT_POSITION_Y)
-            {
-                if (platform.touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;  // Scale according to absRange
-            }
-
-            if (event.code == ABS_MT_TRACKING_ID)
-            {
-                if ((event.value < 0) && (platform.touchSlot < MAX_TOUCH_POINTS))
-                {
-                    // Touch has ended for this point
-                    CORE.Input.Touch.position[platform.touchSlot].x = -1;
-                    CORE.Input.Touch.position[platform.touchSlot].y = -1;
-                }
-            }
-
-            // Touchscreen tap
-            if (event.code == ABS_PRESSURE)
-            {
-                int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
-
-                if (!event.value && previousMouseLeftButtonState)
-                {
-                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
-                    touchAction = 0;    // TOUCH_ACTION_UP
-                }
-
-                if (event.value && !previousMouseLeftButtonState)
-                {
-                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
-                    touchAction = 1;    // TOUCH_ACTION_DOWN
-                }
-            }
-
-        }
-
-        // Button parsing
-        if (event.type == EV_KEY)
-        {
-            // Mouse button parsing
-            if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
-            {
-                platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
-
-                if (event.value > 0) touchAction = 1;   // TOUCH_ACTION_DOWN
-                else touchAction = 0;       // TOUCH_ACTION_UP
-            }
-
-            if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
-            if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
-            if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
-            if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
-            if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
-            if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
-        }
-
-        // Screen confinement
-        if (!CORE.Input.Mouse.cursorHidden)
-        {
-            if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
-            if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
-
-            if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
-            if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
-        }
-
-        // Update touch point count
-        CORE.Input.Touch.pointCount = 0;
-        for (int i = 0; i < MAX_TOUCH_POINTS; i++)
-        {
-            if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++;
-        }
-
-#if defined(SUPPORT_GESTURES_SYSTEM)
-        if (touchAction > -1)
-        {
-            GestureEvent gestureEvent = { 0 };
-
-            gestureEvent.touchAction = touchAction;
-            gestureEvent.pointCount = CORE.Input.Touch.pointCount;
-
-            for (int i = 0; i < MAX_TOUCH_POINTS; i++)
-            {
-                gestureEvent.pointId[i] = i;
-                gestureEvent.position[i] = CORE.Input.Touch.position[i];
-            }
-
-            ProcessGestureEvent(gestureEvent);
-
-            touchAction = -1;
-        }
-#endif
-    }
-}
-
-// Search matching DRM mode in connector's mode list
-static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
-{
-    if (NULL == connector) return -1;
-    if (NULL == mode) return -1;
-
-    // safe bitwise comparison of two modes
-    #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b))? sizeof(a) : sizeof(b))
-
-    for (size_t i = 0; i < connector->count_modes; i++)
-    {
-        TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
-            connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
-
-        if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
-    }
-
-    return -1;
-
-    #undef BINCMP
-}
-
-// Search exactly matching DRM connector mode in connector's list
-static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
-{
-    TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
-
-    if (NULL == connector) return -1;
-
-    for (int i = 0; i < platform.connector->count_modes; i++)
-    {
-        const drmModeModeInfo *const mode = &platform.connector->modes[i];
-
-        TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
-
-        if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue;
-
-        if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
-    }
-
-    TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
-    return -1;
-}
-
-// Search the nearest matching DRM connector mode in connector's list
-static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
-{
-    TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
-
-    if (NULL == connector) return -1;
-
-    int nearestIndex = -1;
-    for (int i = 0; i < platform.connector->count_modes; i++)
-    {
-        const drmModeModeInfo *const mode = &platform.connector->modes[i];
-
-        TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
-            (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive");
-
-        if ((mode->hdisplay < width) || (mode->vdisplay < height))
-        {
-            TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
-            continue;
-        }
-
-        if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced))
-        {
-            TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
-            continue;
-        }
-
-        if (nearestIndex < 0)
-        {
-            nearestIndex = i;
-            continue;
-        }
-
-        const int widthDiff = abs(mode->hdisplay - width);
-        const int heightDiff = abs(mode->vdisplay - height);
-        const int fpsDiff = abs(mode->vrefresh - fps);
-
-        const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width);
-        const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height);
-        const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps);
-
-        if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) nearestIndex = i;
-    }
-
-    return nearestIndex;
+*   CONFIGURATION:
+*       #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only)
+*           Reconfigure standard input to receive key inputs, works with SSH connection
+*           WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other
+*           running processes orblocking the device if not restored properly. Use with care
+*
+*   DEPENDENCIES:
+*       - DRM and GLM: System libraries for display initialization and configuration
+*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include <fcntl.h>          // POSIX file control definitions - open(), creat(), fcntl()
+#include <unistd.h>         // POSIX standard function definitions - read(), close(), STDIN_FILENO
+#include <termios.h>        // POSIX terminal control definitions - tcgetattr(), tcsetattr()
+#include <pthread.h>        // POSIX threads management (inputs reading)
+#include <dirent.h>         // POSIX directory browsing
+#include <limits.h>         // INT_MAX
+
+#include <sys/ioctl.h>      // Required for: ioctl() - UNIX System call for device-specific input/output operations
+#include <linux/kd.h>       // Linux: KDSKBMODE, K_MEDIUMRAM constants definition
+#include <linux/input.h>    // Linux: Keycodes constants definition (KEY_A, ...)
+#include <linux/joystick.h> // Linux: Joystick support library
+
+// WARNING: Both 'linux/input.h' and 'raylib.h' define KEY_F12
+// To avoid conflict with the capturing code in rcore.c we undefine the macro KEY_F12,
+// so the enum KEY_F12 from raylib is used
+#undef KEY_F12
+
+#include <xf86drm.h>        // Direct Rendering Manager user-level library interface
+#include <xf86drmMode.h>    // Direct Rendering Manager mode setting (KMS) interface
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    #include <gbm.h>            // Generic Buffer Management (native platform for EGL on DRM)
+    #include "EGL/egl.h"        // Native platform windowing system interface
+    #include "EGL/eglext.h"     // EGL extensions
+#else
+    #include <sys/mman.h>       // For mmap when copying to the dumb buffer
+    #include <errno.h>          // For the conversion of certain error messages
+#endif
+
+// NOTE: DRM cache enables triple buffered DRM caching
+#if defined(SUPPORT_DRM_CACHE)
+    #include <poll.h>       // Required for: drmHandleEvent() poll
+    #include <errno.h>      // Required for: EBUSY, EAGAIN
+
+    #define MAX_DRM_CACHED_BUFFERS  3
+#endif // SUPPORT_DRM_CACHE
+
+#ifndef EGL_OPENGL_ES3_BIT
+    #define EGL_OPENGL_ES3_BIT  0x40
+#endif
+
+#ifndef EGL_PLATFORM_GBM_KHR
+    #define EGL_PLATFORM_GBM_KHR  0x31D7
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define USE_LAST_TOUCH_DEVICE           // When multiple touchscreens are connected, only use the one with the highest event<N> number
+
+#define DEFAULT_EVDEV_PATH "/dev/input/"    // Path to the linux input events
+
+// Actually biggest key is KEY_CNT but we only really map the keys up to KEY_ALS_TOGGLE
+#define KEYMAP_SIZE KEY_ALS_TOGGLE
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+    // Display data
+    int fd;                             // File descriptor for /dev/dri/...
+    drmModeConnector *connector;        // Direct Rendering Manager (DRM) mode connector
+    drmModeCrtc *crtc;                  // CRT Controller
+    int modeIndex;                      // Index of the used mode of connector->modes
+    uint32_t prevFB;                    // Previous DRM framebufer (during frame swapping)
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    struct gbm_device *gbmDevice;       // GBM device
+    struct gbm_surface *gbmSurface;     // GBM surface
+    struct gbm_bo *prevBO;              // Previous GBM buffer object (during frame swapping)
+    EGLDisplay device;                  // Native display device (physical screen connection)
+    EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
+    EGLContext context;                 // Graphic context, mode in which drawing can be done
+    EGLConfig config;                   // Graphic config
+#else
+    uint32_t prevDumbHandle;            // Handle to the previous dumb buffer (during frame swapping)
+#endif
+
+    // Keyboard data
+    int defaultKeyboardMode;            // Default keyboard mode
+    bool eventKeyboardMode;             // Keyboard in event mode
+    int defaultFileFlags;               // Default IO file flags
+    struct termios defaultSettings;     // Default keyboard settings
+    int keyboardFd;                     // File descriptor for the evdev keyboard
+
+    // Mouse data
+    Vector2 eventWheelMove;             // Registers the event mouse wheel variation
+    // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update
+    char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab
+    bool cursorRelative;                // Relative cursor mode
+    int mouseFd;                        // File descriptor for the evdev mouse/touch/gestures
+    bool mouseIsTouch;                  // Check if the current mouse device is actually a touchscreen
+    Rectangle absRange;                 // Range of values for absolute pointing devices (touchscreens)
+    int touchSlot;                      // Hold the touch slot number of the currently being sent multitouch block
+    bool touchActive[MAX_TOUCH_POINTS]; // Track which touch points are currently active
+    Vector2 touchPosition[MAX_TOUCH_POINTS]; // Track touch positions for each slot
+    int touchId[MAX_TOUCH_POINTS];      // Track touch IDs for each slot
+
+    // Gamepad data
+    int gamepadStreamFd[MAX_GAMEPADS];  // Gamepad device file descriptor
+    int gamepadAbsAxisRange[MAX_GAMEPADS][MAX_GAMEPAD_AXES][2]; // [0] = min, [1] = range value of the axes
+    int gamepadAbsAxisMap[MAX_GAMEPADS][ABS_CNT]; // Maps the axes gamepads from the evdev api to a sequential one
+    int gamepadCount;                   // The number of gamepads registered
+} PlatformData;
+
+#if defined(SUPPORT_DRM_CACHE)
+typedef struct {
+    struct gbm_bo *bo;      // Graphics buffer object
+    uint32_t fbId;          // DRM framebuffer ID
+} FramebufferCache;
+
+static FramebufferCache fbCache[MAX_DRM_CACHED_BUFFERS] = { 0 };
+static volatile int fbCacheCount = 0;
+static volatile bool pendingFlip = false;
+static bool crtcSet = false;
+#endif // SUPPORT_DRM_CACHE
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+
+// NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h
+// TODO: Complete the LUT with all unicode decimal values
+// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard:
+// Currently non US keyboards will have the wrong mapping for some keys
+// NOTE: Replacing this with the keymap from X11 would probably be useless, as people use the drm
+// backend to *avoid* X11
+static const int evkeyToUnicodeLUT[] = {
+    0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114,
+    116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59,
+    39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32
+    // LUT currently incomplete, just mapped the most essential keys
+};
+
+// This is the map used to map any keycode returned from linux to a raylib code from 'raylib.h'
+// NOTE: Use short here to save a little memory
+static const short linuxToRaylibMap[KEYMAP_SIZE] = {
+    // We don't map those with designated initialization, because we would getting
+    // into loads of naming conflicts
+    0,   256, 49,  50,  51,  52,  53,  54,
+    55,  56,  57,  48,  45,  61,  259, 258,
+    81,  87,  69,  82,  84,  89,  85,  73,
+    79,  80,  91,  93,  257, 341, 65,  83,
+    68,  70,  71,  72,  74,  75,  76,  59,
+    39,  96,  340, 92,  90,  88,  67,  86,
+    66,  78,  77,  44,  46,  47,  344, 332,
+    342, 32,  280, 290, 291, 292, 293, 294,
+    295, 296, 297, 298, 299, 282, 281, 327,
+    328, 329, 333, 324, 325, 326, 334, 321,
+    322, 323, 320, 330, 0,   85,  86,  300,
+    301, 89,  90,  91,  92,  93,  94,  95,
+    335, 345, 331, 283, 346, 101, 268, 265,
+    266, 263, 262, 269, 264, 267, 260, 261,
+    112, 113, 114, 115, 116, 117, 118, 119,
+    120, 121, 122, 123, 124, 125, 347, 127,
+    128, 129, 130, 131, 132, 133, 134, 135,
+    136, 137, 138, 139, 140, 141, 142, 143,
+    144, 145, 146, 147, 148, 149, 150, 151,
+    152, 153, 154, 155, 156, 157, 158, 159,
+    160, 161, 162, 163, 164, 165, 166, 167,
+    168, 169, 170, 171, 172, 173, 174, 175,
+    176, 177, 178, 179, 180, 181, 182, 183,
+    184, 185, 186, 187, 188, 189, 190, 191,
+    192, 193, 194, 0,   0,   0,   0,   0,
+    200, 201, 202, 203, 204, 205, 206, 207,
+    208, 209, 210, 211, 212, 213, 214, 215,
+    216, 217, 218, 219, 220, 221, 222, 223,
+    224, 225, 226, 227, 228, 229, 230, 231,
+    232, 233, 234, 235, 236, 237, 238, 239,
+    240, 241, 242, 243, 244, 245, 246, 247,
+    248, 0,   0,   0,   0,   0,   0,   0,
+
+    // Gamepads are mapped according to:
+    // REF: https://www.kernel.org/doc/html/next/input/gamepad.html
+    // Those mappings are standardized, but that doesn't mean people follow
+    // the standards, so this is more of an approximation
+    [BTN_DPAD_UP] = GAMEPAD_BUTTON_LEFT_FACE_UP,
+    [BTN_DPAD_RIGHT] = GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
+    [BTN_DPAD_DOWN] = GAMEPAD_BUTTON_LEFT_FACE_DOWN,
+    [BTN_DPAD_LEFT] = GAMEPAD_BUTTON_LEFT_FACE_LEFT,
+    [BTN_Y] = GAMEPAD_BUTTON_RIGHT_FACE_UP,
+    [BTN_B] = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
+    [BTN_A] = GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
+    [BTN_X] = GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
+    [BTN_TL] = GAMEPAD_BUTTON_LEFT_TRIGGER_1,
+    [BTN_TL2] = GAMEPAD_BUTTON_LEFT_TRIGGER_2,
+    [BTN_TR] = GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
+    [BTN_TR2] = GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
+    [BTN_SELECT] = GAMEPAD_BUTTON_MIDDLE_LEFT,
+    [BTN_MODE] = GAMEPAD_BUTTON_MIDDLE,
+    [BTN_START] = GAMEPAD_BUTTON_MIDDLE_RIGHT,
+    [BTN_THUMBL] = GAMEPAD_BUTTON_LEFT_THUMB,
+    [BTN_THUMBR] = GAMEPAD_BUTTON_RIGHT_THUMB,
+};
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+void ClosePlatform(void);        // Close platform
+
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+static void InitKeyboard(void);                 // Initialize raw keyboard system
+static void RestoreKeyboard(void);              // Restore keyboard system
+static void ProcessKeyboard(void);              // Process keyboard events
+#endif
+
+// Input management functions
+static void InitEvdevInput(void);               // Initialize evdev inputs
+static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate
+static void PollKeyboardEvents(void);           // Process evdev keyboard events
+static void PollGamepadEvents(void);            // Process evdev gamepad events
+static void PollMouseEvents(void);              // Process evdev mouse events
+
+static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode);                               // Search matching DRM mode in connector's mode list
+static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);      // Search exactly matching DRM connector mode in connector's list
+static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced);    // Search the nearest matching DRM connector mode in connector's list
+
+static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform())
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+// NOTE: By default, if KEY_ESCAPE pressed
+bool WindowShouldClose(void)
+{
+    if (CORE.Window.ready) return CORE.Window.shouldClose;
+    else return true;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+    TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+    TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+    TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
+}
+
+// Set window state: minimized
+void MinimizeWindow(void)
+{
+    TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
+}
+
+// Restore window from being minimized/maximized
+void RestoreWindow(void)
+{
+    TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+    TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+    TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
+}
+
+// Set icon for window
+void SetWindowIcon(Image image)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
+}
+
+// Set icon for window
+void SetWindowIcons(Image *images, int count)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
+}
+
+// Set title for window
+void SetWindowTitle(const char *title)
+{
+    CORE.Window.title = title;
+}
+
+// Set window position on screen (windowed mode)
+void SetWindowPosition(int x, int y)
+{
+    TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
+}
+
+// Set monitor for the current window
+void SetWindowMonitor(int monitor)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
+}
+
+// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMinSize(int width, int height)
+{
+    CORE.Window.screenMin.width = width;
+    CORE.Window.screenMin.height = height;
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+    CORE.Window.screenMax.width = width;
+    CORE.Window.screenMax.height = height;
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+    TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+    TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
+}
+
+// Set window focused
+void SetWindowFocused(void)
+{
+    TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
+}
+
+// Get native window handle
+void *GetWindowHandle(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
+    return NULL;
+}
+
+// Get number of monitors
+int GetMonitorCount(void)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
+    return 1;
+}
+
+// Get current monitor where window is placed
+int GetCurrentMonitor(void)
+{
+    TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor position
+Vector2 GetMonitorPosition(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
+    return (Vector2){ 0, 0 };
+}
+
+// Get selected monitor width (currently used by monitor)
+int GetMonitorWidth(int monitor)
+{
+    int width = 0;
+
+    if (monitor != 0)
+    {
+        TRACELOG(LOG_WARNING, "GetMonitorWidth() implemented for first monitor only");
+    }
+    else if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        width = platform.connector->modes[platform.modeIndex].hdisplay;
+    }
+
+    return width;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+    int height = 0;
+
+    if (monitor != 0)
+    {
+        TRACELOG(LOG_WARNING, "GetMonitorHeight() implemented for first monitor only");
+    }
+    else if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        height = platform.connector->modes[platform.modeIndex].vdisplay;
+    }
+
+    return height;
+}
+
+// Get selected monitor physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+    int physicalWidth = 0;
+
+    if (monitor != 0)
+    {
+        TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() implemented for first monitor only");
+    }
+    else if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        physicalWidth = platform.connector->mmWidth;
+    }
+
+    return physicalWidth;
+}
+
+// Get selected monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+    int physicalHeight = 0;
+
+    if (monitor != 0)
+    {
+        TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() implemented for first monitor only");
+    }
+    else if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        physicalHeight = platform.connector->mmHeight;
+    }
+
+    return physicalHeight;
+}
+
+// Get selected monitor refresh rate
+int GetMonitorRefreshRate(int monitor)
+{
+    int refresh = 0;
+
+    if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        refresh = platform.connector->modes[platform.modeIndex].vrefresh;
+    }
+
+    return refresh;
+}
+
+// Get the human-readable, UTF-8 encoded name of the selected monitor
+const char *GetMonitorName(int monitor)
+{
+    const char *name = "";
+
+    if (monitor != 0)
+    {
+        TRACELOG(LOG_WARNING, "GetMonitorName() implemented for first monitor only");
+    }
+    else if ((platform.connector) && (platform.modeIndex >= 0))
+    {
+        name = platform.connector->modes[platform.modeIndex].name;
+    }
+
+    return name;
+}
+
+// Get window position XY on monitor
+Vector2 GetWindowPosition(void)
+{
+    return (Vector2){ 0, 0 };
+}
+
+// Get window scale DPI factor for current monitor
+Vector2 GetWindowScaleDPI(void)
+{
+    return (Vector2){ 1.0f, 1.0f };
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+    TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
+}
+
+// Get clipboard text content
+// NOTE: returned string is allocated and freed by GLFW
+const char *GetClipboardText(void)
+{
+    TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
+    return NULL;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+    CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+    // Set cursor position in the middle
+    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+    platform.cursorRelative = false;
+    CORE.Input.Mouse.cursorLocked = false;
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+    // Set cursor position in the middle
+    SetMousePosition(0, 0);
+
+    platform.cursorRelative = true;
+    CORE.Input.Mouse.cursorLocked = true;
+}
+
+#if defined(SUPPORT_DRM_CACHE)
+
+// Destroy cached framebuffer callback, set by gbm_bo_set_user_data()
+static void DestroyFrameBufferCallback(struct gbm_bo *bo, void *data)
+{
+    uint32_t fbId = (uintptr_t)data;
+
+    // Remove from cache
+    for (int i = 0; i < fbCacheCount; i++)
+    {
+        if (fbCache[i].bo == bo)
+        {
+            TRACELOG(LOG_INFO, "DISPLAY: DRM: Framebuffer removed [%u]", (uintptr_t)fbId);
+            drmModeRmFB(platform.fd, fbCache[i].fbId); // Release DRM FB
+
+            // Shift remaining entries
+            for (int j = i; j < fbCacheCount - 1; j++) fbCache[j] = fbCache[j + 1];
+
+            fbCacheCount--;
+            break;
+        }
+    }
+}
+
+// Create or retrieve cached DRM FB for BO
+static uint32_t GetOrCreateFbForBo(struct gbm_bo *bo)
+{
+    // Try to find existing cache entry
+    for (int i = 0; i < fbCacheCount; i++)
+    {
+        if (fbCache[i].bo == bo) return fbCache[i].fbId;
+    }
+
+    // Create new entry if cache not full
+    if (fbCacheCount >= MAX_DRM_CACHED_BUFFERS) return 0; // FB cache full
+
+    uint32_t handle = gbm_bo_get_handle(bo).u32;
+    uint32_t stride = gbm_bo_get_stride(bo);
+    uint32_t width = gbm_bo_get_width(bo);
+    uint32_t height = gbm_bo_get_height(bo);
+
+    uint32_t fbId = 0;
+    if (drmModeAddFB(platform.fd, width, height, 24, 32, stride, handle, &fbId)) return 0;
+
+    // Store in cache
+    fbCache[fbCacheCount] = (FramebufferCache){ .bo = bo, .fbId = fbId };
+    fbCacheCount++;
+
+    // Set destroy callback to auto-cleanup
+    gbm_bo_set_user_data(bo, (void *)(uintptr_t)fbId, DestroyFrameBufferCallback);
+
+    TRACELOG(LOG_INFO, "DISPLAY: DRM: Added new buffer object [%u]" , (uintptr_t)fbId);
+
+    return fbId;
+}
+
+// Renders a blank frame to allocate initial buffers
+// TODO: WARNING: Platform backend should not include OpenGL code
+void RenderBlankFrame()
+{
+    glClearColor(0, 0, 0, 1);
+    glClear(GL_COLOR_BUFFER_BIT);
+    eglSwapBuffers(platform.device, platform.surface);
+    glFinish(); // Ensure the buffer is processed
+}
+
+// Initialize with first buffer only
+int InitSwapScreenBuffer()
+{
+    if (!platform.gbmSurface || (platform.fd < 0))
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Swap buffers can not be initialized");
+        return -1;
+    }
+
+    // Render a blank frame to allocate buffers
+    RenderBlankFrame();
+
+    // Get first buffer
+    struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
+    if (!bo)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to lock initial swap buffer");
+        return -1;
+    }
+
+    // Create FB for first buffer
+    uint32_t fbId = GetOrCreateFbForBo(bo);
+    if (!fbId)
+    {
+        gbm_surface_release_buffer(platform.gbmSurface, bo);
+        return -1;
+    }
+
+    // Initial CRTC setup
+    if (drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fbId, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]))
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM: Failed to initialize CRTC setup. ERROR: %s", strerror(errno));
+        gbm_surface_release_buffer(platform.gbmSurface, bo);
+        return -1;
+    }
+
+    // Keep first buffer locked until flipped
+    platform.prevBO = bo;
+    crtcSet = true;
+
+    return 0;
+}
+
+// Static page flip handler
+// NOTE: Called once the drmModePageFlip() finished from the drmHandleEvent() context
+static void PageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
+{
+    // Unused inputs
+    (void)fd;
+    (void)frame;
+    (void)sec;
+    (void)usec;
+
+    pendingFlip = false;
+    struct gbm_bo *boToRelease = (struct gbm_bo *)data;
+
+    // Buffers are released after the flip completes (via page_flip_handler), ensuring they're no longer in use
+    // Prevents the GPU from writing to a buffer being scanned out
+    if (boToRelease) gbm_surface_release_buffer(platform.gbmSurface, boToRelease);
+}
+
+// Swap implementation with proper caching
+void SwapScreenBuffer()
+{
+    if (!crtcSet || !platform.gbmSurface) return;
+
+    static int loopCnt = 0;
+    static int errCnt[5] = { 0 };
+    loopCnt++;
+
+    // Call this only, if pendingFlip is not set
+    eglSwapBuffers(platform.device, platform.surface);
+
+    // Process pending events non-blocking
+    drmEventContext evctx = {
+        .version = DRM_EVENT_CONTEXT_VERSION,
+        .page_flip_handler = PageFlipHandler
+    };
+
+    struct pollfd pfd = { .fd = platform.fd, .events = POLLIN };
+
+    // Polling for event for 0ms
+    while (poll(&pfd, 1, 0) > 0) drmHandleEvent(platform.fd, &evctx);
+
+    // Skip if previous flip pending
+    if (pendingFlip)
+    {
+        errCnt[0]++; // Skip frame: flip pending
+        return;
+    }
+
+    // Get new front buffer
+    struct gbm_bo *nextBO = gbm_surface_lock_front_buffer(platform.gbmSurface);
+    if (!nextBO) // Failed to lock front buffer
+    {
+        errCnt[1]++;
+        return;
+    }
+
+    // Get FB ID (creates new one if needed)
+    uint32_t fbId = GetOrCreateFbForBo(nextBO);
+    if (!fbId)
+    {
+        gbm_surface_release_buffer(platform.gbmSurface, nextBO);
+        errCnt[2]++;
+        return;
+    }
+
+    // Attempt page flip
+    // NOTE: rmModePageFlip() schedules a buffer-flip for the next vblank and then notifies us about it
+    // It takes a CRTC-id, fb-id and an arbitrary data-pointer and then schedules the page-flip
+    // This is fully asynchronous and when the page-flip happens, the DRM-fd will become readable and we can call drmHandleEvent()
+    // This will read all vblank/page-flip events and call our modeset_page_flip_event() callback with the data-pointer that we passed to drmModePageFlip()
+    // We simply call modeset_draw_dev() then so the next frame is rendered... returns immediately
+    if (drmModePageFlip(platform.fd, platform.crtc->crtc_id, fbId, DRM_MODE_PAGE_FLIP_EVENT, platform.prevBO))
+    {
+        if (errno == EBUSY) errCnt[3]++; // Display busy - skip flip
+        else errCnt[4]++; // Page flip failed
+
+        gbm_surface_release_buffer(platform.gbmSurface, nextBO);
+        return;
+    }
+
+    // Success: update state
+    pendingFlip = true;
+    platform.prevBO = nextBO;
+
+/*
+    // Some benchmarking code
+    if (loopCnt >= 600)
+    {
+        TRACELOG(LOG_INFO, "DRM: Error counters: %d, %d, %d, %d, %d, %d", errCnt[0], errCnt[1], errCnt[2], errCnt[3], errCnt[4], loopCnt);
+        for (int i = 0; i < 5; i++) errCnt[i] = 0;
+        loopCnt = 0;
+    }
+*/
+}
+
+#else // !SUPPORT_DRM_CACHE
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Hardware rendering buffer swap with EGL
+    eglSwapBuffers(platform.device, platform.surface);
+
+    if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
+
+    struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface);
+    if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer");
+
+    uint32_t fb = 0;
+    int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb);
+    if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result);
+
+    result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]);
+    if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result);
+
+    if (platform.prevFB)
+    {
+        result = drmModeRmFB(platform.fd, platform.prevFB);
+        if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result);
+    }
+
+    platform.prevFB = fb;
+
+    if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
+
+    platform.prevBO = bo;
+#else
+    // Software rendering buffer swap
+    if ((platform.fd == -1) || !platform.connector || (platform.modeIndex < 0))
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap");
+        return;
+    }
+
+    // Retrieving the dimensions of the display mode used
+    drmModeModeInfo *mode = &platform.connector->modes[platform.modeIndex];
+    uint32_t width = mode->hdisplay;
+    uint32_t height = mode->vdisplay;
+
+    // Dumb buffers use a fixed format based on bpp
+#if SW_COLOR_BUFFER_BITS == 24
+    const uint32_t bpp = 32;    // 32 bits per pixel (XRGB8888 format)
+    const uint32_t depth = 24;  // Color depth, here only 24 bits, alpha is not used
+#else
+    // REVIEW: Not sure how it will be interpreted (RGB or RGBA?)
+    const uint32_t bpp = SW_COLOR_BUFFER_BITS;
+    const uint32_t depth = SW_COLOR_BUFFER_BITS;
+#endif
+
+    // Create a dumb buffer for software rendering
+    struct drm_mode_create_dumb creq = { 0 };
+    creq.width = width;
+    creq.height = height;
+    creq.bpp = bpp;
+
+    int result = drmIoctl(platform.fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
+    if (result < 0)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: Failed to create dumb buffer: %s", strerror(errno));
+        return;
+    }
+
+    // Create framebuffer with the correct format
+    uint32_t fb = 0;
+    result = drmModeAddFB(platform.fd, width, height, depth, bpp, creq.pitch, creq.handle, &fb);
+    if (result != 0)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d (%s)", result, strerror(errno));
+        struct drm_mode_destroy_dumb dreq = { 0 };
+        dreq.handle = creq.handle;
+        drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+        return;
+    }
+
+    // Map the dumb buffer to copy our software rendered buffer
+    struct drm_mode_map_dumb mreq = { 0 };
+    mreq.handle = creq.handle;
+    result = drmIoctl(platform.fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
+    if (result != 0)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: Failed to map dumb buffer: %s", strerror(errno));
+        drmModeRmFB(platform.fd, fb);
+        struct drm_mode_destroy_dumb dreq = { 0 };
+        dreq.handle = creq.handle;
+        drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+        return;
+    }
+
+    // Map the buffer into userspace
+    void *dumbBuffer = mmap(0, creq.size, PROT_READ | PROT_WRITE, MAP_SHARED, platform.fd, mreq.offset);
+    if (dumbBuffer == MAP_FAILED)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: Failed to mmap dumb buffer: %s", strerror(errno));
+        drmModeRmFB(platform.fd, fb);
+        struct drm_mode_destroy_dumb dreq = { 0 };
+        dreq.handle = creq.handle;
+        drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+        return;
+    }
+
+    // Copy the software rendered buffer to the dumb buffer with scaling if needed
+    // NOTE: RLSW will make a simple copy if the dimensions match
+    swBlitFramebuffer(0, 0, width, height, 0, 0, width, height, SW_RGBA, SW_UNSIGNED_BYTE, dumbBuffer);
+
+    // Unmap the buffer
+    munmap(dumbBuffer, creq.size);
+
+    // Find a CRTC compatible with the connector
+    uint32_t crtcId = 0;
+    if (platform.crtc) crtcId = platform.crtc->crtc_id;
+    else
+    {
+        // Find a CRTC that's compatible with this connector
+        drmModeRes *res = drmModeGetResources(platform.fd);
+        if (!res)
+        {
+            TRACELOG(LOG_ERROR, "DISPLAY: Failed to get DRM resources");
+            drmModeRmFB(platform.fd, fb);
+            struct drm_mode_destroy_dumb dreq = {0};
+            dreq.handle = creq.handle;
+            drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+            return;
+        }
+
+        // Check which CRTCs are compatible with this connector
+        drmModeEncoder *encoder = NULL;
+        if (platform.connector->encoder_id) encoder = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
+
+        if (encoder && encoder->crtc_id)
+        {
+            crtcId = encoder->crtc_id;
+            platform.crtc = drmModeGetCrtc(platform.fd, crtcId);
+        }
+        else
+        {
+            // Find a free CRTC
+            for (int i = 0; i < res->count_crtcs; i++)
+            {
+                drmModeCrtc *crtc = drmModeGetCrtc(platform.fd, res->crtcs[i]);
+                if (crtc && !crtc->buffer_id) // CRTC is free
+                {
+                    crtcId = res->crtcs[i];
+                    if (platform.crtc) drmModeFreeCrtc(platform.crtc);
+                    platform.crtc = crtc;
+                    break;
+                }
+
+                if (crtc) drmModeFreeCrtc(crtc);
+            }
+        }
+
+        if (encoder) drmModeFreeEncoder(encoder);
+        drmModeFreeResources(res);
+
+        if (!crtcId)
+        {
+            TRACELOG(LOG_ERROR, "DISPLAY: No compatible CRTC found");
+            drmModeRmFB(platform.fd, fb);
+            struct drm_mode_destroy_dumb dreq = {0};
+            dreq.handle = creq.handle;
+            drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+            return;
+        }
+    }
+
+    // Set CRTC with better error handling
+    result = drmModeSetCrtc(platform.fd, crtcId, fb, 0, 0, &platform.connector->connector_id, 1, mode);
+    if (result != 0)
+    {
+        TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d (%s)", result, strerror(errno));
+        TRACELOG(LOG_ERROR, "DISPLAY: CRTC ID: %u, FB ID: %u, Connector ID: %u", crtcId, fb, platform.connector->connector_id);
+        TRACELOG(LOG_ERROR, "DISPLAY: Mode: %dx%d@%d", mode->hdisplay, mode->vdisplay, mode->vrefresh);
+
+        drmModeRmFB(platform.fd, fb);
+        struct drm_mode_destroy_dumb dreq = {0};
+        dreq.handle = creq.handle;
+        drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+        return;
+    }
+
+    // Clean up previous framebuffer
+    if (platform.prevFB)
+    {
+        result = drmModeRmFB(platform.fd, platform.prevFB);
+        if (result != 0) TRACELOG(LOG_WARNING, "DISPLAY: drmModeRmFB() failed with result: %d", result);
+    }
+
+    platform.prevFB = fb;
+
+    // Clean up previous dumb buffer
+    if (platform.prevDumbHandle)
+    {
+        struct drm_mode_destroy_dumb dreq = {0};
+        dreq.handle = platform.prevDumbHandle;
+        drmIoctl(platform.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
+    }
+
+    platform.prevDumbHandle = creq.handle;
+#endif
+}
+#endif // SUPPORT_DRM_CACHE
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds since InitTimer()
+double GetTime(void)
+{
+    double time = 0.0;
+    struct timespec ts = { 0 };
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
+
+    time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
+
+    return time;
+}
+
+// Open URL with default system browser (if available)
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
+// REF: https://github.com/raysan5/raylib/issues/686
+void OpenURL(const char *url)
+{
+    TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform");
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
+    return 0;
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+    TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
+}
+
+// Get physical key name
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
+// Register all input events
+void PollInputEvents(void)
+{
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    // NOTE: Gestures update must be called every frame to reset gestures correctly
+    // because ProcessGestureEvent() is just called on an event, not every frame
+    UpdateGestures();
+#endif
+
+    // Reset keys/chars pressed registered
+    CORE.Input.Keyboard.keyPressedQueueCount = 0;
+    CORE.Input.Keyboard.charPressedQueueCount = 0;
+
+    // Reset last gamepad button/axis registered state
+    CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
+    //CORE.Input.Gamepad.axisCount = 0;
+
+    // Register previous keys states
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
+    {
+        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    PollKeyboardEvents();
+
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+    // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here
+    // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console
+    if (!platform.eventKeyboardMode) ProcessKeyboard();
+#endif
+
+    // Check exit key
+    if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true;
+
+    // Register previous mouse position
+    if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f };
+    else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+
+    // Register previous mouse states
+    CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
+    CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove;
+    platform.eventWheelMove = (Vector2){ 0.0f, 0.0f };
+
+    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++)
+    {
+        CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
+        CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i];
+        CORE.Input.Touch.currentTouchState[i] = platform.currentButtonStateEvdev[i];
+    }
+
+    // Register gamepads buttons events
+    PollGamepadEvents();
+
+    // Register previous touch states
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
+
+    // Map touch position to mouse position for convenience
+    // NOTE: For DRM touchscreen devices, this mapping is disabled to avoid false touch detection
+    // CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
+
+    // Handle the mouse/touch/gestures events:
+    PollMouseEvents();
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+    platform.fd = -1;
+    platform.connector = NULL;
+    platform.modeIndex = -1;
+    platform.crtc = NULL;
+    platform.prevFB = 0;
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    platform.gbmDevice = NULL;
+    platform.gbmSurface = NULL;
+    platform.prevBO = NULL;
+#else
+    platform.prevDumbHandle = 0;
+#endif
+
+    // Initialize graphic device: display/window and graphic context
+    //----------------------------------------------------------------------------
+    FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+
+#if defined(DEFAULT_GRAPHIC_DEVICE_DRM)
+    platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR);
+    if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: Default graphic device DRM opened successfully");
+#else
+    TRACELOG(LOG_WARNING, "DISPLAY: No graphic card set, trying platform-gpu-card");
+    platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4)
+    if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: platform-gpu-card opened successfully");
+
+    drmModeRes *res = NULL;
+    if ((platform.fd == -1) || ((res = drmModeGetResources(platform.fd)) == NULL))
+    {
+        if (platform.fd != -1) close(platform.fd);
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to open platform-gpu-card, trying card1");
+        platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded
+        if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card1 opened successfully");
+    }
+
+    if ((platform.fd == -1) || ((res = drmModeGetResources(platform.fd)) == NULL))
+    {
+        if (platform.fd != -1) close(platform.fd);
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card1, trying card0");
+        platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3)
+        if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card0 opened successfully");
+    }
+
+    if ((platform.fd == -1) || ((res = drmModeGetResources(platform.fd)) == NULL))
+    {
+        if (platform.fd != -1) close(platform.fd);
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card0, trying card2");
+        platform.fd = open("/dev/dri/card2", O_RDWR);
+        if (platform.fd != -1) TRACELOG(LOG_INFO, "DISPLAY: card2 opened successfully");
+    }
+#endif
+
+    if (platform.fd == -1)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card");
+        return -1;
+    }
+
+    if (!res)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources");
+        close(platform.fd);
+        return -1;
+    }
+
+    TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors);
+
+    // Connector detection
+    for (size_t i = 0; i < res->count_connectors; i++)
+    {
+        TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i);
+
+        drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]);
+        if (!con)
+        {
+            TRACELOG(LOG_WARNING, "DISPLAY: Failed to get connector %i", i);
+            continue;
+        }
+
+        TRACELOG(LOG_TRACE, "DISPLAY: Connector %i modes detected: %i", i, con->count_modes);
+        TRACELOG(LOG_TRACE, "DISPLAY: Connector %i status: %s", i,
+                 (con->connection == DRM_MODE_CONNECTED)? "CONNECTED" :
+                 (con->connection == DRM_MODE_DISCONNECTED)? "DISCONNECTED" :
+                 (con->connection == DRM_MODE_UNKNOWNCONNECTION)? "UNKNOWN" : "OTHER");
+
+        // In certain cases the status of the conneciton is reported as UKNOWN, but it is still connected
+        // This might be a hardware or software limitation like on Raspberry Pi Zero with composite output
+        // WARNING: Accept CONNECTED, UNKNOWN and even those without encoder_id connectors for software mode
+        if (((con->connection == DRM_MODE_CONNECTED) || (con->connection == DRM_MODE_UNKNOWNCONNECTION)) && (con->count_modes > 0))
+        {
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+            // For hardware rendering, we need an encoder_id
+            if (con->encoder_id)
+            {
+                TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected with encoder", i);
+                platform.connector = con;
+                break;
+            }
+            else TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i connected but no encoder", i);
+#else
+            // For software rendering, we can accept even without encoder_id
+            TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i suitable for software rendering", i);
+            platform.connector = con;
+            break;
+#endif
+        }
+
+        if (!platform.connector)
+        {
+            TRACELOG(LOG_TRACE, "DISPLAY: DRM connector %i NOT suitable (deleting)", i);
+            drmModeFreeConnector(con);
+        }
+    }
+
+    if (!platform.connector)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found");
+        drmModeFreeResources(res);
+        close(platform.fd);
+        return -1;
+    }
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id);
+    if (!enc)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder");
+        drmModeFreeConnector(platform.connector);
+        drmModeFreeResources(res);
+        close(platform.fd);
+        return -1;
+    }
+
+    platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id);
+    if (!platform.crtc)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc");
+        drmModeFreeEncoder(enc);
+        drmModeFreeConnector(platform.connector);
+        drmModeFreeResources(res);
+        close(platform.fd);
+        return -1;
+    }
+
+    // If InitWindow should use the current mode find it in the connector's mode list
+    if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0))
+    {
+        TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode...");
+
+        platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode);
+
+        if (platform.modeIndex < 0)
+        {
+            TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found");
+            drmModeFreeEncoder(enc);
+            drmModeFreeConnector(platform.connector);
+            drmModeFreeResources(res);
+            close(platform.fd);
+            return -1;
+        }
+
+        CORE.Window.screen.width = CORE.Window.display.width;
+        CORE.Window.screen.height = CORE.Window.display.height;
+    }
+
+    const bool allowInterlaced = FLAG_IS_SET(CORE.Window.flags, FLAG_INTERLACED_HINT);
+    const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60;
+
+    // Try to find an exact matching mode
+    platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
+
+    // If nothing found, try to find a nearly matching mode
+    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced);
+
+    // If nothing found, try to find an exactly matching mode including interlaced
+    if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
+
+    // If nothing found, try to find a nearly matching mode including interlaced
+    if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true);
+
+    // If nothing found, there is no suitable mode
+    if (platform.modeIndex < 0)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode");
+        drmModeFreeEncoder(enc);
+        drmModeFreeConnector(platform.connector);
+        drmModeFreeResources(res);
+        close(platform.fd);
+        return -1;
+    }
+
+    CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay;
+    CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay;
+
+    TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name,
+        platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay,
+        FLAG_IS_SET(platform.connector->modes[platform.modeIndex].flags, DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
+        platform.connector->modes[platform.modeIndex].vrefresh);
+
+    drmModeFreeEncoder(enc);
+    enc = NULL;
+#else
+    // For software rendering, the first available mode can be used
+    if (platform.connector->count_modes > 0)
+    {
+        platform.modeIndex = 0;
+        CORE.Window.display.width = platform.connector->modes[0].hdisplay;
+        CORE.Window.display.height = platform.connector->modes[0].vdisplay;
+
+        TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u) for software rendering",
+                 platform.connector->modes[0].name,
+                 platform.connector->modes[0].hdisplay,
+                 platform.connector->modes[0].vdisplay,
+                 (platform.connector->modes[0].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p',
+                 platform.connector->modes[0].vrefresh);
+    }
+    else
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: No modes available for connector");
+        drmModeFreeConnector(platform.connector);
+        drmModeFreeResources(res);
+        close(platform.fd);
+        return -1;
+    }
+#endif
+
+    // Use the width and height of the surface for render
+    CORE.Window.render.width = CORE.Window.screen.width;
+    CORE.Window.render.height = CORE.Window.screen.height;
+
+    drmModeFreeResources(res);
+    res = NULL;
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Hardware rendering initialization with EGL
+    platform.gbmDevice = gbm_create_device(platform.fd);
+    if (!platform.gbmDevice)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device");
+        return -1;
+    }
+
+    platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay,
+        platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+    if (!platform.gbmSurface)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface");
+        return -1;
+    }
+
+    EGLint samples = 0;
+    EGLint sampleBuffer = 0;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
+    {
+        samples = 4;
+        sampleBuffer = 1;
+        TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
+    }
+
+    const EGLint framebufferAttribs[] = {
+        EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support
+        EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android!
+        EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
+        EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
+        EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
+        EGL_ALPHA_SIZE, 8,        // ALPHA bit depth (required for transparent framebuffer)
+        //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
+        EGL_DEPTH_SIZE, 24,         // Depth buffer size (Required to use Depth testing!)
+        //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
+        EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA
+        EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
+        EGL_NONE
+    };
+
+    const EGLint contextAttribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE
+    };
+
+    EGLint numConfigs = 0;
+    const char *eglClientExtensions = NULL;
+
+    // Get an EGL device connection
+    // NOTE: eglGetPlatformDisplay() is preferred over eglGetDisplay() legacy call
+    platform.device = EGL_NO_DISPLAY;
+#if defined(EGL_VERSION_1_5)
+    platform.device = eglGetPlatformDisplay(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL);
+#else
+    // Check if extension is available for eglGetPlatformDisplayEXT()
+    // NOTE: Better compatibility with some drivers (e.g. Mali Midgard)
+    eglClientExtensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
+    if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL))
+    {
+        PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
+        
+        if (eglGetPlatformDisplayEXT != NULL) platform.device = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, platform.gbmDevice, NULL);
+    }
+
+    // In case extension not found or display could not be retrieved, try useing legacy version
+    if (platform.device == EGL_NO_DISPLAY) platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice);
+#endif
+    if (platform.device == EGL_NO_DISPLAY)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
+        return -1;
+    }
+
+    // Initialize the EGL device connection
+    if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
+    {
+        // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
+        return -1;
+    }
+
+    if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs))
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError());
+        return -1;
+    }
+
+    TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs);
+
+    EGLConfig *configs = (EGLConfig *)RL_CALLOC(numConfigs, sizeof(*configs));
+    if (!configs)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs");
+        return -1;
+    }
+
+    EGLint matchingNumConfigs = 0;
+    if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs))
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError());
+        free(configs);
+        return -1;
+    }
+
+    TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs);
+
+    // find the EGL config that matches the previously setup GBM format
+    int found = 0;
+    for (EGLint i = 0; i < matchingNumConfigs; i++)
+    {
+        EGLint id = 0;
+        if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id))
+        {
+            TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError());
+            continue;
+        }
+
+        if (GBM_FORMAT_ARGB8888 == id)
+        {
+            TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i);
+            platform.config = configs[i];
+            found = 1;
+            break;
+        }
+    }
+
+    RL_FREE(configs);
+
+    if (!found)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config");
+        return -1;
+    }
+
+    // Set rendering API
+    eglBindAPI(EGL_OPENGL_ES_API);
+
+    // Create an EGL rendering context
+    platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
+    if (platform.context == EGL_NO_CONTEXT)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
+        return -1;
+    }
+
+    // Create an EGL window surface
+    platform.surface = EGL_NO_SURFACE;
+
+    if ((eglClientExtensions != NULL) && (strstr(eglClientExtensions, "EGL_EXT_platform_base") != NULL))
+    {
+        PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
+
+        if (eglCreatePlatformWindowSurfaceEXT != NULL) platform.surface = eglCreatePlatformWindowSurfaceEXT(platform.device, platform.config, platform.gbmSurface, NULL);
+    }
+
+    if (platform.surface == EGL_NO_SURFACE)
+    {
+        platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL);
+    }
+
+    if (platform.surface == EGL_NO_SURFACE)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError());
+        return -1;
+    }
+
+    // At this point we need to manage render size vs screen size
+    // NOTE: This function use and modify global module variables:
+    //  -> CORE.Window.screen.width/CORE.Window.screen.height
+    //  -> CORE.Window.render.width/CORE.Window.render.height
+    //  -> CORE.Window.screenScale
+    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
+
+    // There must be at least one frame displayed before the buffers are swapped
+    //eglSwapInterval(platform.device, 1);
+
+    EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
+
+    // Check surface and context activation
+    if (result != EGL_FALSE)
+    {
+        CORE.Window.ready = true;
+
+        CORE.Window.render.width = CORE.Window.screen.width;
+        CORE.Window.render.height = CORE.Window.screen.height;
+        CORE.Window.currentFbo.width = CORE.Window.render.width;
+        CORE.Window.currentFbo.height = CORE.Window.render.height;
+
+        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
+        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+    }
+    else
+    {
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+        return -1;
+    }
+
+    // Load OpenGL extensions
+    // NOTE: GL procedures address loader is required to load extensions
+    rlLoadExtensions(eglGetProcAddress);
+#else
+    // At this point we need to manage render size vs screen size
+    // NOTE: This function use and modify global module variables:
+    //  -> CORE.Window.screen.width/CORE.Window.screen.height
+    //  -> CORE.Window.render.width/CORE.Window.render.height
+    //  -> CORE.Window.screenScale
+    SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
+
+    // Setup window ready state for software rendering
+    CORE.Window.ready = true;
+
+    CORE.Window.render.width = CORE.Window.screen.width;
+    CORE.Window.render.height = CORE.Window.screen.height;
+    CORE.Window.currentFbo.width = CORE.Window.render.width;
+    CORE.Window.currentFbo.height = CORE.Window.render.height;
+
+    TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully (Software Rendering)");
+    TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+    TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+    TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+    TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+#endif
+
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
+
+    // If graphic device is no properly initialized, we end program
+    if (!CORE.Window.ready)
+    {
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device");
+        return -1;
+    }
+    else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2);
+
+    // Set some default window flags
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);       // false
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);    // false
+    FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);      // true
+    FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);    // false
+
+    //----------------------------------------------------------------------------
+    // Initialize timing system
+    //----------------------------------------------------------------------------
+    // NOTE: timming system must be initialized before the input events system
+    InitTimer();
+    //----------------------------------------------------------------------------
+
+    // Initialize input events system
+    //----------------------------------------------------------------------------
+    InitEvdevInput();   // Evdev inputs initialization
+
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+    InitKeyboard();     // Keyboard init (stdin)
+#endif
+    //----------------------------------------------------------------------------
+
+    // Initialize storage system
+    //----------------------------------------------------------------------------
+    CORE.Storage.basePath = GetWorkingDirectory();
+    //----------------------------------------------------------------------------
+
+#if defined(SUPPORT_DRM_CACHE)
+    if (InitSwapScreenBuffer() == 0)
+    {
+        TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
+        return 0;
+    }
+    else
+    {
+        TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized failed");
+        return -1;
+    }
+#else // !SUPPORT_DRM_CACHE
+    TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully");
+    return 0;
+#endif
+}
+
+// Close platform
+void ClosePlatform(void)
+{
+    if (platform.prevFB)
+    {
+        drmModeRmFB(platform.fd, platform.prevFB);
+        platform.prevFB = 0;
+    }
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    if (platform.prevBO)
+    {
+        gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO);
+        platform.prevBO = NULL;
+    }
+
+    if (platform.gbmSurface)
+    {
+        gbm_surface_destroy(platform.gbmSurface);
+        platform.gbmSurface = NULL;
+    }
+
+    if (platform.gbmDevice)
+    {
+        gbm_device_destroy(platform.gbmDevice);
+        platform.gbmDevice = NULL;
+    }
+#endif
+
+    if (platform.crtc)
+    {
+        if (platform.connector)
+        {
+            drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id,
+                platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode);
+            drmModeFreeConnector(platform.connector);
+            platform.connector = NULL;
+        }
+
+        drmModeFreeCrtc(platform.crtc);
+        platform.crtc = NULL;
+    }
+
+    if (platform.fd != -1)
+    {
+        close(platform.fd);
+        platform.fd = -1;
+    }
+
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Close surface, context and display
+    if (platform.device != EGL_NO_DISPLAY)
+    {
+        if (platform.surface != EGL_NO_SURFACE)
+        {
+            eglDestroySurface(platform.device, platform.surface);
+            platform.surface = EGL_NO_SURFACE;
+        }
+
+        if (platform.context != EGL_NO_CONTEXT)
+        {
+            eglDestroyContext(platform.device, platform.context);
+            platform.context = EGL_NO_CONTEXT;
+        }
+
+        eglTerminate(platform.device);
+        platform.device = EGL_NO_DISPLAY;
+    }
+#endif
+
+    CORE.Window.shouldClose = true;   // Added to force threads to exit when the close window is called
+
+    // Close the evdev devices
+
+    if (platform.mouseFd != -1)
+    {
+        close(platform.mouseFd);
+        platform.mouseFd = -1;
+    }
+
+    for (int i = 0; i < platform.gamepadCount; i++)
+    {
+        close(platform.gamepadStreamFd[i]);
+        platform.gamepadStreamFd[i] = -1;
+    }
+
+    if (platform.keyboardFd != -1)
+    {
+        close(platform.keyboardFd);
+        platform.keyboardFd = -1;
+    }
+}
+
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+// Initialize Keyboard system (using standard input)
+static void InitKeyboard(void)
+{
+    // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor,
+    // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE
+
+    // Save terminal keyboard settings
+    tcgetattr(STDIN_FILENO, &platform.defaultSettings);
+
+    // Reconfigure terminal with new settings
+    struct termios keyboardNewSettings = { 0 };
+    keyboardNewSettings = platform.defaultSettings;
+
+    // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing
+    // NOTE: ISIG controls if ^C and ^Z generate break signals or not
+    FLAG_CLEAR(keyboardNewSettings.c_lflag, ICANON | ECHO | ISIG);
+    //FLAG_CLEAR(keyboardNewSettings.c_iflag, ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF);
+    keyboardNewSettings.c_cc[VMIN] = 1;
+    keyboardNewSettings.c_cc[VTIME] = 0;
+
+    // Set new keyboard settings (change occurs immediately)
+    tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings);
+
+    // Save old keyboard mode to restore it at the end
+    platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0);          // F_GETFL: Get the file access mode and the file status flags
+    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified
+
+    // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno)
+    int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode);
+
+    // In case of failure, it could mean a remote keyboard is used (SSH)
+    if (result < 0) TRACELOG(LOG_WARNING, "DRM: Failed to change keyboard mode, an SSH keyboard is probably used");
+    else
+    {
+        // Reconfigure keyboard mode to get:
+        // - scancodes (K_RAW)
+        // - keycodes (K_MEDIUMRAW)
+        // - ASCII chars (K_XLATE)
+        // - UNICODE chars (K_UNICODE)
+        ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE);  // ASCII chars
+    }
+
+    // Register keyboard restore when program finishes
+    atexit(RestoreKeyboard);
+}
+
+// Restore default keyboard input
+static void RestoreKeyboard(void)
+{
+    // Reset to default keyboard settings
+    tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings);
+
+    // Reconfigure keyboard to default mode
+    fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags);
+    ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode);
+}
+
+// Process keyboard inputs
+static void ProcessKeyboard(void)
+{
+    #define MAX_KEYBUFFER_SIZE      32      // Max size in bytes to read
+
+    // Keyboard input polling (fill keys[256] array with status)
+    int bufferByteCount = 0;                        // Bytes available on the buffer
+    char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 };    // Max keys to be read at a time
+
+    // Read availables keycodes from stdin
+    bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE);     // POSIX system call
+
+    // Reset pressed keys array (it will be filled below)
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
+    {
+        CORE.Input.Keyboard.currentKeyState[i] = 0;
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    // Fill all read bytes (looking for keys)
+    for (int i = 0; i < bufferByteCount; i++)
+    {
+        // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code!
+        // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42
+        if (keysBuffer[i] == 0x1b)
+        {
+            // Check if ESCAPE key has been pressed to stop program
+            if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1;
+            else
+            {
+                if (keysBuffer[i + 1] == 0x5b)    // Special function key
+                {
+                    if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32))
+                    {
+                        // Process special function keys (F1 - F12)
+                        switch (keysBuffer[i + 3])
+                        {
+                            case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break;    // raylib KEY_F1
+                            case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break;    // raylib KEY_F2
+                            case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break;    // raylib KEY_F3
+                            case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break;    // raylib KEY_F4
+                            case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break;    // raylib KEY_F5
+                            case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break;    // raylib KEY_F6
+                            case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break;    // raylib KEY_F7
+                            case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break;    // raylib KEY_F8
+                            case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break;    // raylib KEY_F9
+                            case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break;    // raylib KEY_F10
+                            case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break;    // raylib KEY_F11
+                            case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break;    // raylib KEY_F12
+                            default: break;
+                        }
+
+                        if (keysBuffer[i + 2] == 0x5b) i += 4;
+                        else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5;
+                    }
+                    else
+                    {
+                        switch (keysBuffer[i + 2])
+                        {
+                            case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break;    // raylib KEY_UP
+                            case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break;    // raylib KEY_DOWN
+                            case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break;    // raylib KEY_RIGHT
+                            case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break;    // raylib KEY_LEFT
+                            default: break;
+                        }
+
+                        i += 3;  // Jump to next key
+                    }
+
+                    // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT)
+                }
+            }
+        }
+        else if (keysBuffer[i] == 0x0a)     // raylib KEY_ENTER (don't mix with <linux/input.h> KEY_*)
+        {
+            CORE.Input.Keyboard.currentKeyState[257] = 1;
+
+            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257;     // Add keys pressed into queue
+            CORE.Input.Keyboard.keyPressedQueueCount++;
+        }
+        else if (keysBuffer[i] == 0x7f)     // raylib KEY_BACKSPACE
+        {
+            CORE.Input.Keyboard.currentKeyState[259] = 1;
+
+            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 259;     // Add keys pressed into queue
+            CORE.Input.Keyboard.keyPressedQueueCount++;
+        }
+        else
+        {
+            // Translate lowercase a-z letters to A-Z
+            if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122))
+            {
+                CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1;
+            }
+            else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1;
+
+            CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i];     // Add keys pressed into queue
+            CORE.Input.Keyboard.keyPressedQueueCount++;
+        }
+    }
+}
+#endif  // SUPPORT_SSH_KEYBOARD_RPI
+
+// Initialize user input from evdev(/dev/input/event<N>)
+// this means mouse, keyboard or gamepad devices
+static void InitEvdevInput(void)
+{
+    char path[MAX_FILEPATH_LENGTH] = { 0 };
+    DIR *directory = NULL;
+    struct dirent *entity = NULL;
+
+    // Initialize keyboard file descriptor
+    platform.keyboardFd = -1;
+    platform.mouseFd = -1;
+
+    // Reset variables
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+    {
+        CORE.Input.Touch.position[i].x = -1;
+        CORE.Input.Touch.position[i].y = -1;
+        platform.touchActive[i] = false;
+        platform.touchPosition[i].x = -1;
+        platform.touchPosition[i].y = -1;
+        platform.touchId[i] = -1;
+    }
+
+    // Initialize touch slot
+    platform.touchSlot = 0;
+
+    // Reset keyboard key state
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
+    {
+        CORE.Input.Keyboard.currentKeyState[i] = 0;
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    // Open the linux directory of "/dev/input"
+    directory = opendir(DEFAULT_EVDEV_PATH);
+
+    if (directory)
+    {
+        while ((entity = readdir(directory)) != NULL)
+        {
+            if ((strncmp("event", entity->d_name, strlen("event")) == 0) ||     // Search for devices named "event*"
+                (strncmp("mouse", entity->d_name, strlen("mouse")) == 0))       // Search for devices named "mouse*"
+            {
+                snprintf(path, MAX_FILEPATH_LENGTH, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name);
+                ConfigureEvdevDevice(path);                                     // Configure the device if appropriate
+            }
+        }
+
+        closedir(directory);
+    }
+    else TRACELOG(LOG_WARNING, "INPUT: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH);
+}
+
+// Identifies a input device and configures it for use if appropriate
+static void ConfigureEvdevDevice(char *device)
+{
+    #define BITS_PER_LONG   (8*sizeof(long))
+    #define NBITS(x)        ((((x) - 1)/BITS_PER_LONG) + 1)
+    #define OFF(x)          ((x)%BITS_PER_LONG)
+    #define BIT(x)          (1UL<<OFF(x))
+    #define LONG(x)         ((x)/BITS_PER_LONG)
+    #define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+    unsigned long evBits[NBITS(EV_MAX)] = { 0 };
+    unsigned long absBits[NBITS(ABS_MAX)] = { 0 };
+    unsigned long relBits[NBITS(REL_MAX)] = { 0 };
+    unsigned long keyBits[NBITS(KEY_MAX)] = { 0 };
+
+    // Open the device
+    int fd = open(device, O_RDONLY | O_NONBLOCK);
+    if (fd < 0)
+    {
+        TRACELOG(LOG_WARNING, "SYSTEM: Failed to open input device: %s", device);
+        return;
+    }
+
+    // At this point we have a connection to the device, but we don't yet know what the device is
+    // It could be many things, even as simple as a power button...
+    //-------------------------------------------------------------------------------------------------------
+
+    // Identify the device
+    //-------------------------------------------------------------------------------------------------------
+    struct {
+        bool exist;
+        struct input_absinfo info;
+    } absinfo[ABS_CNT] = { 0 };
+
+    // These flags aren't really a one of
+    // Some devices could have properties we assosciate with keyboards as well as properties
+    // we assosciate with mice
+    bool isKeyboard = false;
+    bool isMouse = false;
+    bool isTouch = false;
+    bool isGamepad = false;
+
+    int absAxisCount = 0;
+
+    ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits);
+    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits);
+
+    if (TEST_BIT(evBits, EV_ABS))
+    {
+        ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits);
+
+        // If the device has an X an Y axis it's either a touch device, a special mouse or a gamepad
+        bool hasAbsXY = TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y);
+
+        if (hasAbsXY)
+        {
+            absAxisCount += 2;
+
+            absinfo[ABS_X].exist = true;
+            absinfo[ABS_Y].exist = true;
+
+            ioctl(fd, EVIOCGABS(ABS_X), &absinfo[ABS_X].info);
+            ioctl(fd, EVIOCGABS(ABS_Y), &absinfo[ABS_Y].info);
+        }
+
+        // If it has any of these buttons it's a touch device
+        if (hasAbsXY &&
+           (TEST_BIT(keyBits, BTN_STYLUS) ||
+            TEST_BIT(keyBits, BTN_TOOL_PEN) ||
+            TEST_BIT(keyBits, BTN_TOOL_FINGER) ||
+            TEST_BIT(keyBits, BTN_TOUCH))) isTouch = true;
+
+        // Absolute mice should really only exist with VMWare, but it shouldn't
+        // matter if we support them
+        else if (hasAbsXY && TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
+
+        // If any of the common joystick axes are present, we assume it's a gamepad
+        else
+        {
+            for (int axis = (hasAbsXY? ABS_Z : ABS_X); axis < ABS_PRESSURE; axis++)
+            {
+                if (TEST_BIT(absBits, axis))
+                {
+                    absinfo[axis].exist = true;
+                    isGamepad = true;
+                    absAxisCount++;
+
+                    ioctl(fd, EVIOCGABS(axis), &absinfo[axis].info);
+                }
+            }
+        }
+
+        // If the device has multitouch axes, it's a touch device
+        if (TEST_BIT(absBits, ABS_MT_POSITION_X) &&
+            TEST_BIT(absBits, ABS_MT_POSITION_Y)) isTouch = true;
+    }
+
+    if (TEST_BIT(evBits, EV_REL))
+    {
+        ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits);
+
+        // If it has any of the gamepad or touch features we tested so far, it's not a mouse
+        if (!isTouch &&
+            !isGamepad &&
+            TEST_BIT(relBits, REL_X) &&
+            TEST_BIT(relBits, REL_Y) &&
+            TEST_BIT(keyBits, BTN_MOUSE)) isMouse = true;
+    }
+
+    if (TEST_BIT(evBits, EV_KEY))
+    {
+        // The first 32 keys as defined in input-event-codes.h are pretty much
+        // exclusive to keyboards, so we can test them using a mask
+        // Leave out the first bit to not test KEY_RESERVED
+        const unsigned long mask = 0xFFFFFFFE;
+        if ((keyBits[0] & mask) == mask) isKeyboard = true;
+
+        // If we find any of the common gamepad buttons we assume it's a gamepad
+        else
+        {
+            for (int button = BTN_JOYSTICK; button < BTN_DIGI; ++button)
+            {
+                if (TEST_BIT(keyBits, button)) isGamepad = true;
+            }
+
+            for (int button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40; button++)
+            {
+                if (TEST_BIT(keyBits, button)) isGamepad = true;
+            }
+        }
+    }
+
+    const char *deviceKindStr = "unknown";
+    if (isMouse || isTouch)
+    {
+        bool prioritize = false;
+
+        // Priority logic: touchscreens override Mice
+        // 1. No device set yet? Take it
+        if (platform.mouseFd == -1) prioritize = true;
+        // 2. Current is mouse, new is touch? Upgrade to touch
+        else if (isTouch && !platform.mouseIsTouch) prioritize = true;
+        // 3. Current is touch, new is touch? Use the new one (last one found wins, standard behavior)
+        else if (isTouch && platform.mouseIsTouch) prioritize = true;
+        // 4. Current is mouse, new is mouse? Use the new one
+        else if (!isTouch && !platform.mouseIsTouch) prioritize = true;
+        // 5. Current is touch, new is mouse? Ignore the mouse, keep the touchscreen
+        else prioritize = false;
+
+        if (prioritize)
+        {
+            deviceKindStr = isTouch? "touchscreen" : "mouse";
+
+            if (platform.mouseFd != -1)
+            {
+                TRACELOG(LOG_INFO, "INPUT: Overwriting previous input device with new %s", deviceKindStr);
+                close(platform.mouseFd);
+            }
+
+            platform.mouseFd = fd;
+            platform.mouseIsTouch = isTouch;
+
+            if (absAxisCount > 0)
+            {
+                platform.absRange.x = absinfo[ABS_X].info.minimum;
+                platform.absRange.width = absinfo[ABS_X].info.maximum - absinfo[ABS_X].info.minimum;
+
+                platform.absRange.y = absinfo[ABS_Y].info.minimum;
+                platform.absRange.height = absinfo[ABS_Y].info.maximum - absinfo[ABS_Y].info.minimum;
+            }
+
+            TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
+        }
+        else
+        {
+            TRACELOG(LOG_INFO, "INPUT: Ignoring device %s (keeping higher priority %s device)", device, platform.mouseIsTouch ? "touchscreen" : "mouse");
+            close(fd);
+            return;
+        }
+    }
+    else if (isGamepad && !isMouse && !isKeyboard && (platform.gamepadCount < MAX_GAMEPADS))
+    {
+        deviceKindStr = "gamepad";
+        int index = platform.gamepadCount++;
+
+        platform.gamepadStreamFd[index] = fd;
+        CORE.Input.Gamepad.ready[index] = true;
+
+        ioctl(platform.gamepadStreamFd[index], EVIOCGNAME(64), &CORE.Input.Gamepad.name[index]);
+        CORE.Input.Gamepad.axisCount[index] = absAxisCount;
+
+        if (absAxisCount > 0)
+        {
+            // TODO / NOTE
+            // So gamepad axes (as in the actual linux joydev.c) are just simply enumerated
+            // and (at least for some input drivers like xpat) it's convention to use
+            // ABS_X, ABX_Y for one joystick ABS_RX, ABS_RY for the other and the Z axes for the
+            // shoulder buttons
+            // If these are now enumerated you get LJOY_X, LJOY_Y, LEFT_SHOULDERB, RJOY_X, ...
+            // That means they don't match the GamepadAxis enum
+            // This could be fixed
+            int axisIndex = 0;
+            for (int axis = ABS_X; axis < ABS_PRESSURE; axis++)
+            {
+                if (absinfo[axis].exist)
+                {
+                    platform.gamepadAbsAxisRange[index][axisIndex][0] = absinfo[axisIndex].info.minimum;
+                    platform.gamepadAbsAxisRange[index][axisIndex][1] = absinfo[axisIndex].info.maximum - absinfo[axisIndex].info.minimum;
+
+                    platform.gamepadAbsAxisMap[index][axis] = axisIndex;
+                    axisIndex++;
+                }
+            }
+        }
+    }
+    else if (isKeyboard && (platform.keyboardFd == -1))
+    {
+        deviceKindStr = "keyboard";
+        platform.keyboardFd = fd;
+    }
+    else
+    {
+        close(fd);
+        return;
+    }
+
+    TRACELOG(LOG_INFO, "INPUT: Initialized input device %s as %s", device, deviceKindStr);
+}
+
+// Poll and process evdev keyboard events
+static void PollKeyboardEvents(void)
+{
+    int fd = platform.keyboardFd;
+    if (fd == -1) return;
+
+    struct input_event event = { 0 };
+    int keycode = -1;
+
+    // Try to read data from the keyboard and only continue if successful
+    while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
+    {
+        // Check if the event is a key event
+        if (event.type != EV_KEY) continue;
+
+#if defined(SUPPORT_SSH_KEYBOARD_RPI)
+        // If the event was a key, we know a working keyboard is connected, so disable the SSH keyboard
+        platform.eventKeyboardMode = true;
+#endif
+        // Keyboard keys appear for codes 1 to 255, ignore everthing else
+        if ((event.code >= 1) && (event.code <= 255))
+        {
+            // Lookup the scancode in the keymap to get a keycode
+            keycode = linuxToRaylibMap[event.code];
+
+            // Make sure we got a valid keycode
+            if ((keycode > 0) && (keycode < MAX_KEYBOARD_KEYS))
+            {
+                // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt
+                // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL,
+                // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat
+                CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1);
+                CORE.Input.Keyboard.keyRepeatInFrame[keycode] = (event.value == 2);
+
+                // If the key is pressed add it to the queues
+                if (event.value == 1)
+                {
+                    if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
+                    {
+                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode;
+                        CORE.Input.Keyboard.keyPressedQueueCount++;
+                    }
+
+                    if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
+                    {
+                        // TODO/FIXME: This is not actually converting to unicode properly because it's not taking things like shift into account
+                        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = evkeyToUnicodeLUT[event.code];
+                        CORE.Input.Keyboard.charPressedQueueCount++;
+                    }
+                }
+
+                TRACELOG(LOG_DEBUG, "INPUT: KEY_%s Keycode(linux): %4i KeyCode(raylib): %4i", (event.value == 0)? "UP  " : "DOWN", event.code, keycode);
+            }
+        }
+    }
+}
+
+// Poll gamepad input events
+static void PollGamepadEvents(void)
+{
+    // Read gamepad event
+    struct input_event event = { 0 };
+
+    for (int i = 0; i < platform.gamepadCount; i++)
+    {
+        if (!CORE.Input.Gamepad.ready[i]) continue;
+
+        // Register previous gamepad states
+        for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
+
+        while (read(platform.gamepadStreamFd[i], &event, sizeof(event)) == (int)sizeof(event))
+        {
+            if (event.type == EV_KEY)
+            {
+                if (event.code < KEYMAP_SIZE)
+                {
+                    short keycode = linuxToRaylibMap[event.code]; // raylib keycode
+
+                    TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: KEY_%s Keycode(linux): %4i Keycode(raylib): %4i", i, (event.value == 0)? "UP" : "DOWN", event.code, keycode);
+
+                    if ((keycode != 0) && (keycode < MAX_GAMEPAD_BUTTONS))
+                    {
+                        // 1 - button pressed, 0 - button released
+                        CORE.Input.Gamepad.currentButtonState[i][keycode] = event.value;
+                        CORE.Input.Gamepad.lastButtonPressed = (event.value == 1)? keycode : GAMEPAD_BUTTON_UNKNOWN;
+                    }
+                }
+            }
+            else if (event.type == EV_ABS)
+            {
+                if (event.code < ABS_CNT)
+                {
+                    int axisRaylib = platform.gamepadAbsAxisMap[i][event.code];
+
+                    TRACELOG(LOG_DEBUG, "INPUT: Gamepad %2i: Axis: %2i Value: %i", i, axisRaylib, event.value);
+
+                    if (axisRaylib < MAX_GAMEPAD_AXES)
+                    {
+                        int min = platform.gamepadAbsAxisRange[i][event.code][0];
+                        int range = platform.gamepadAbsAxisRange[i][event.code][1];
+
+                        // NOTE: Scaling of event.value to get values between -1..1
+                        CORE.Input.Gamepad.axisState[i][axisRaylib] = (2*(float)(event.value - min)/range) - 1;
+                    }
+                }
+            }
+        }
+    }
+}
+
+// Poll mouse input events
+static void PollMouseEvents(void)
+{
+    int fd = platform.mouseFd;
+    if (fd == -1) return;
+
+    struct input_event event = { 0 };
+    int touchAction = -1;           // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE
+    static bool isMultitouch = false; // Detect if device supports MT events
+
+    // Try to read data from the mouse/touch/gesture and only continue if successful
+    while (read(fd, &event, sizeof(event)) == (int)sizeof(event))
+    {
+        // Relative movement parsing
+        if (event.type == EV_REL)
+        {
+            if (event.code == REL_X)
+            {
+                if (platform.cursorRelative)
+                {
+                    CORE.Input.Mouse.currentPosition.x = event.value;
+                    CORE.Input.Mouse.previousPosition.x = 0.0f;
+                }
+                else CORE.Input.Mouse.currentPosition.x += event.value;
+
+                // NOTE: For DRM touchscreen, do not simulate touch from mouse movement
+                // CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x;
+                touchAction = 2;    // TOUCH_ACTION_MOVE
+            }
+
+            if (event.code == REL_Y)
+            {
+                if (platform.cursorRelative)
+                {
+                    CORE.Input.Mouse.currentPosition.y = event.value;
+                    CORE.Input.Mouse.previousPosition.y = 0.0f;
+                }
+                else CORE.Input.Mouse.currentPosition.y += event.value;
+
+                // NOTE: For DRM touchscreen, do not simulate touch from mouse movement
+                // CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y;
+                touchAction = 2;    // TOUCH_ACTION_MOVE
+            }
+
+            if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value;
+        }
+
+        // Absolute movement parsing
+        if (event.type == EV_ABS)
+        {
+            // Basic movement
+            if (event.code == ABS_X)
+            {
+                CORE.Input.Mouse.currentPosition.x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;    // Scale according to absRange
+
+                // Update single touch position only if it's active and no MT events are being used
+                if (platform.touchActive[0] && !isMultitouch)
+                {
+                    platform.touchPosition[0].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;
+                    if (touchAction == -1) touchAction = 2;    // TOUCH_ACTION_MOVE
+                }
+            }
+
+            if (event.code == ABS_Y)
+            {
+                CORE.Input.Mouse.currentPosition.y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;  // Scale according to absRange
+
+                // Update single touch position only if it's active and no MT events are being used
+                if (platform.touchActive[0] && !isMultitouch)
+                {
+                    platform.touchPosition[0].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;
+                    if (touchAction == -1) touchAction = 2;    // TOUCH_ACTION_MOVE
+                }
+            }
+
+            // Multitouch movement
+            if (event.code == ABS_MT_SLOT)
+            {
+                platform.touchSlot = event.value;
+                isMultitouch = true;
+            }
+
+            if (event.code == ABS_MT_POSITION_X)
+            {
+                isMultitouch = true;
+                if (platform.touchSlot < MAX_TOUCH_POINTS)
+                {
+                    platform.touchPosition[platform.touchSlot].x = (event.value - platform.absRange.x)*CORE.Window.screen.width/platform.absRange.width;
+
+                    // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
+                    // Only set to MOVE if we haven't already detected a DOWN or UP event this frame
+                    if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2;    // TOUCH_ACTION_MOVE
+                }
+            }
+
+            if (event.code == ABS_MT_POSITION_Y)
+            {
+                if (platform.touchSlot < MAX_TOUCH_POINTS)
+                {
+                    platform.touchPosition[platform.touchSlot].y = (event.value - platform.absRange.y)*CORE.Window.screen.height/platform.absRange.height;
+
+                    // If this slot is active, it's a move. If not, we are just updating the buffer for when it becomes active.
+                    // Only set to MOVE if we haven't already detected a DOWN or UP event this frame
+                    if (platform.touchActive[platform.touchSlot] && touchAction == -1) touchAction = 2;    // TOUCH_ACTION_MOVE
+                }
+            }
+
+            if (event.code == ABS_MT_TRACKING_ID)
+            {
+                if (platform.touchSlot < MAX_TOUCH_POINTS)
+                {
+                    if (event.value >= 0)
+                    {
+
+                        platform.touchActive[platform.touchSlot] = true;
+                        platform.touchId[platform.touchSlot] = event.value; // Use Tracking ID for unique IDs
+
+                        touchAction = 1; // TOUCH_ACTION_DOWN
+                    }
+                    else
+                    {
+                        // Touch has ended for this point
+                        platform.touchActive[platform.touchSlot] = false;
+                        platform.touchPosition[platform.touchSlot].x = -1;
+                        platform.touchPosition[platform.touchSlot].y = -1;
+                        platform.touchId[platform.touchSlot] = -1;
+
+                        // Force UP action if we haven't already set a DOWN action
+                        // (DOWN takes priority over UP if both happen in one frame, though rare)
+                        if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
+                    }
+                }
+            }
+
+            // Handle ABS_MT_PRESSURE (0x3a) if available, as some devices use it for lift-off
+            #ifndef ABS_MT_PRESSURE
+                #define ABS_MT_PRESSURE 0x3a
+            #endif
+            if (event.code == ABS_MT_PRESSURE)
+            {
+                if (platform.touchSlot < MAX_TOUCH_POINTS)
+                {
+                    if (event.value <= 0) // Pressure 0 means lift
+                    {
+                        platform.touchActive[platform.touchSlot] = false;
+                        platform.touchPosition[platform.touchSlot].x = -1;
+                        platform.touchPosition[platform.touchSlot].y = -1;
+                        platform.touchId[platform.touchSlot] = -1;
+                        if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
+                    }
+                }
+            }
+
+            // Touchscreen tap
+            if (event.code == ABS_PRESSURE)
+            {
+                int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT];
+
+                if (!event.value && previousMouseLeftButtonState)
+                {
+                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0;
+                    if (touchAction != 1) touchAction = 0; // TOUCH_ACTION_UP
+                }
+
+                if (event.value && !previousMouseLeftButtonState)
+                {
+                    platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1;
+                    touchAction = 1; // TOUCH_ACTION_DOWN
+                }
+            }
+        }
+
+        // Button parsing
+        if (event.type == EV_KEY)
+        {
+            // Mouse button parsing
+            if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT))
+            {
+                platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value;
+
+                if (event.value > 0)
+                {
+                    bool activateSlot0 = false;
+
+                    if (event.code == BTN_LEFT) activateSlot0 = true; // Mouse click always activates
+                    else if (event.code == BTN_TOUCH)
+                    {
+                        bool anyActive = false;
+                        for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+                        {
+                            if (platform.touchActive[i]) { anyActive = true; break; }
+                        }
+
+                        if (!anyActive) activateSlot0 = true;
+                    }
+
+                    if (activateSlot0)
+                    {
+                        platform.touchActive[0] = true;
+                        platform.touchId[0] = 0;
+                    }
+
+                    touchAction = 1; // TOUCH_ACTION_DOWN
+                }
+                else
+                {
+                    // Only clear touch 0 for actual mouse clicks (BTN_LEFT)
+                    if (event.code == BTN_LEFT)
+                    {
+                        platform.touchActive[0] = false;
+                        platform.touchPosition[0].x = -1;
+                        platform.touchPosition[0].y = -1;
+                    }
+                    else if (event.code == BTN_TOUCH) platform.touchSlot = 0;            // Reset slot index to 0
+
+                    touchAction = 0;       // TOUCH_ACTION_UP
+                }
+            }
+
+            if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value;
+            if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value;
+            if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value;
+            if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value;
+            if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value;
+            if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value;
+        }
+
+        // Screen confinement
+        if (!CORE.Input.Mouse.cursorLocked)
+        {
+            if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0;
+            if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x)
+                CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x;
+
+            if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0;
+            if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y)
+                CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y;
+        }
+
+        // Repack active touches into CORE.Input.Touch
+        int k = 0;
+        for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+        {
+            if (platform.touchActive[i])
+            {
+                CORE.Input.Touch.position[k] = platform.touchPosition[i];
+                CORE.Input.Touch.pointId[k] = platform.touchId[i];
+                k++;
+            }
+        }
+
+        CORE.Input.Touch.pointCount = k;
+
+        // Clear remaining slots
+        for (int i = k; i < MAX_TOUCH_POINTS; i++)
+        {
+            CORE.Input.Touch.position[i].x = -1;
+            CORE.Input.Touch.position[i].y = -1;
+            CORE.Input.Touch.pointId[i] = -1;
+        }
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+        if (touchAction > -1)
+        {
+            GestureEvent gestureEvent = { 0 };
+            gestureEvent.touchAction = touchAction;
+            gestureEvent.pointCount = CORE.Input.Touch.pointCount;
+
+            for (int i = 0; i < MAX_TOUCH_POINTS; i++)
+            {
+                gestureEvent.pointId[i] = i;
+                gestureEvent.position[i] = CORE.Input.Touch.position[i];
+            }
+
+            ProcessGestureEvent(gestureEvent);
+            touchAction = -1;
+        }
+#endif
+    }
+}
+
+// Search matching DRM mode in connector's mode list
+static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode)
+{
+    if (NULL == connector) return -1;
+    if (NULL == mode) return -1;
+
+    // safe bitwise comparison of two modes
+    #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b))? sizeof(a) : sizeof(b))
+
+    for (size_t i = 0; i < connector->count_modes; i++)
+    {
+        TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay,
+            connector->modes[i].vrefresh, (FLAG_IS_SET(connector->modes[i].flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
+
+        if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i;
+    }
+
+    return -1;
+
+    #undef BINCMP
+}
+
+// Search exactly matching DRM connector mode in connector's list
+static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
+{
+    TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
+
+    if (NULL == connector) return -1;
+
+    for (int i = 0; i < platform.connector->count_modes; i++)
+    {
+        const drmModeModeInfo *const mode = &platform.connector->modes[i];
+
+        TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
+
+        if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced) continue;
+
+        if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i;
+    }
+
+    TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found");
+    return -1;
+}
+
+// Search the nearest matching DRM connector mode in connector's list
+static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced)
+{
+    TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no");
+
+    if (NULL == connector) return -1;
+
+    int nearestIndex = -1;
+    int minUnusedPixels = INT_MAX;
+    int minFpsDiff = INT_MAX;
+    for (int i = 0; i < platform.connector->count_modes; i++)
+    {
+        const drmModeModeInfo *const mode = &platform.connector->modes[i];
+
+        TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh,
+            (FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0)? "interlaced" : "progressive");
+
+        if ((mode->hdisplay < width) || (mode->vdisplay < height))
+        {
+            TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small");
+            continue;
+        }
+
+        if ((FLAG_IS_SET(mode->flags, DRM_MODE_FLAG_INTERLACE) > 0) && !allowInterlaced)
+        {
+            TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode");
+            continue;
+        }
+
+        const int unusedPixels = (mode->hdisplay - width)*(mode->vdisplay - height);
+        const int fpsDiff = mode->vrefresh - fps;
+
+        if ((unusedPixels < minUnusedPixels) ||
+            ((unusedPixels == minUnusedPixels) && (abs(fpsDiff) < abs(minFpsDiff))) ||
+            ((unusedPixels == minUnusedPixels) && (abs(fpsDiff) == abs(minFpsDiff)) && (fpsDiff > 0)))
+        {
+            nearestIndex = i;
+            minUnusedPixels = unusedPixels;
+            minFpsDiff = fpsDiff;
+        }
+    }
+
+    return nearestIndex;
+}
+
+// Compute framebuffer size relative to screen size and display size
+// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
+static void SetupFramebuffer(int width, int height)
+{
+    // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
+    if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+        // Downscaling to fit display with border-bars
+        float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
+        float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
+
+        if (widthRatio <= heightRatio)
+        {
+            CORE.Window.render.width = CORE.Window.display.width;
+            CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
+            CORE.Window.renderOffset.x = 0;
+            CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
+        }
+        else
+        {
+            CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
+            CORE.Window.render.height = CORE.Window.display.height;
+            CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
+            CORE.Window.renderOffset.y = 0;
+        }
+
+        // Screen scaling required
+        float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
+        CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
+
+        // NOTE: We render to full display resolution!
+        // We just need to calculate above parameters for downscale matrix and offsets
+        CORE.Window.render.width = CORE.Window.display.width;
+        CORE.Window.render.height = CORE.Window.display.height;
+
+        TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
+    }
+    else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
+    {
+        // Required screen size is smaller than display size
+        TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
+
+        if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
+        {
+            CORE.Window.screen.width = CORE.Window.display.width;
+            CORE.Window.screen.height = CORE.Window.display.height;
+        }
+
+        // Upscaling to fit display with border-bars
+        float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
+        float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
+
+        if (displayRatio <= screenRatio)
+        {
+            CORE.Window.render.width = CORE.Window.screen.width;
+            CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
+            CORE.Window.renderOffset.x = 0;
+            CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
+        }
+        else
+        {
+            CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
+            CORE.Window.render.height = CORE.Window.screen.height;
+            CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
+            CORE.Window.renderOffset.y = 0;
+        }
+    }
+    else
+    {
+        CORE.Window.render.width = CORE.Window.screen.width;
+        CORE.Window.render.height = CORE.Window.screen.height;
+        CORE.Window.renderOffset.x = 0;
+        CORE.Window.renderOffset.y = 0;
+    }
 }
 
 // EOF
diff --git a/raylib/src/platforms/rcore_memory.c b/raylib/src/platforms/rcore_memory.c
new file mode 100644
--- /dev/null
+++ b/raylib/src/platforms/rcore_memory.c
@@ -0,0 +1,591 @@
+/**********************************************************************************************
+*
+*   rcore_memory - Functions to manage window, graphics device and inputs
+*
+*   PLATFORM: MEMORY (No OS)
+*       - Memory framebuffer output (no os)
+*
+*   LIMITATIONS:
+*       - Software renderer (rlsw)
+*       - No input system
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - Improvement 01
+*       - Improvement 02
+*
+*   CONFIGURATION:
+*       #define RCORE_PLATFORM_CUSTOM_FLAG
+*           Custom flag for rcore on target platform -not used-
+*
+*   DEPENDENCIES:
+*       - rlsw: Software renderer
+*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#if defined(_WIN32)
+    #include <conio.h>              // Required for: kbhit()
+#else
+    // Provide kbhit() function in non-Windows platforms
+    #include <termios.h>
+    #include <unistd.h>
+    #include <fcntl.h>
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// Platform-specific required data for timming (Win32)
+#if defined(_WIN32)
+typedef struct _LARGE_INTEGER { int64_t QuadPart; } LARGE_INTEGER;
+__declspec(dllimport) int __stdcall QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
+__declspec(dllimport) int __stdcall QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
+#endif
+
+typedef struct {
+    unsigned int *pixels;   // Pointer to pixel data buffer (RGBA8888 format)
+#if defined(_WIN32)
+    LARGE_INTEGER timerFrequency;
+#endif
+} PlatformData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void);                 // Initialize platform (graphics, inputs and more)
+bool InitGraphicsDevice(void);          // Initialize graphics device
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+#if !defined(_WIN32)
+static int kbhit(void);                         // Check if a key has been pressed
+static char getch(void) { return getchar(); }   // Get pressed character
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+bool WindowShouldClose(void)
+{
+    if (CORE.Window.ready) return CORE.Window.shouldClose;
+    else return true;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+    TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform");
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+    TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform");
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+    TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform");
+}
+
+// Set window state: minimized
+void MinimizeWindow(void)
+{
+    TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
+}
+
+// Restore window from being minimized/maximized
+void RestoreWindow(void)
+{
+    TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+    TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform");
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+    TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform");
+}
+
+// Set icon for window
+void SetWindowIcon(Image image)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
+}
+
+// Set icon for window
+void SetWindowIcons(Image *images, int count)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
+}
+
+// Set title for window
+void SetWindowTitle(const char *title)
+{
+    CORE.Window.title = title;
+}
+
+// Set window position on screen (windowed mode)
+void SetWindowPosition(int x, int y)
+{
+    TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
+}
+
+// Set monitor for the current window
+void SetWindowMonitor(int monitor)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
+}
+
+// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMinSize(int width, int height)
+{
+    CORE.Window.screenMin.width = width;
+    CORE.Window.screenMin.height = height;
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+    CORE.Window.screenMax.width = width;
+    CORE.Window.screenMax.height = height;
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+    TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform");
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+    TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform");
+}
+
+// Set window focused
+void SetWindowFocused(void)
+{
+    TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
+}
+
+// Get native window handle
+void *GetWindowHandle(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
+    return NULL;
+}
+
+// Get number of monitors
+int GetMonitorCount(void)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
+    return 1;
+}
+
+// Get current monitor where window is placed
+int GetCurrentMonitor(void)
+{
+    TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor position
+Vector2 GetMonitorPosition(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
+    return (Vector2){ 0, 0 };
+}
+
+// Get selected monitor width (currently used by monitor)
+int GetMonitorWidth(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor refresh rate
+int GetMonitorRefreshRate(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
+    return 0;
+}
+
+// Get the human-readable, UTF-8 encoded name of the selected monitor
+const char *GetMonitorName(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
+    return "";
+}
+
+// Get window position XY on monitor
+Vector2 GetWindowPosition(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform");
+    return (Vector2){ 0, 0 };
+}
+
+// Get window scale DPI factor for current monitor
+Vector2 GetWindowScaleDPI(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
+    return (Vector2){ 1.0f, 1.0f };
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+    TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform");
+}
+
+// Get clipboard text content
+// NOTE: returned string is allocated and freed by GLFW
+const char *GetClipboardText(void)
+{
+    TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform");
+    return NULL;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+    CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+    // Set cursor position in the middle
+    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+    CORE.Input.Mouse.cursorHidden = false;
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+    // Set cursor position in the middle
+    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+    CORE.Input.Mouse.cursorHidden = true;
+}
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+    // Update framebuffer
+    rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds since InitTimer()
+double GetTime(void)
+{
+    double time = 0.0;
+#if defined(_WIN32)
+    LARGE_INTEGER now = { 0 };
+    QueryPerformanceCounter(&now);
+    return (double)(now.QuadPart - CORE.Time.base)/(double)platform.timerFrequency.QuadPart;
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
+    struct timespec ts = { 0 };
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
+    time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
+#endif
+    return time;
+}
+
+// Open URL with default system browser (if available)
+// NOTE: This function is only safe to use if you control the URL given.
+// A user could craft a malicious string performing another action.
+// Only call this function yourself not with user input or make sure to check the string yourself.
+// REF: https://github.com/raysan5/raylib/issues/686
+void OpenURL(const char *url)
+{
+    // Security check to (partially) avoid malicious code on target platform
+    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
+    else
+    {
+        char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char));
+        sprintf(cmd, "explorer \"%s\"", url);
+        int result = system(cmd);
+        if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created");
+        RL_FREE(cmd);
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform");
+    return 0;
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+    CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+    TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform");
+}
+
+// Get physical key name
+const char *GetKeyName(int key)
+{
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
+// Register all input events
+void PollInputEvents(void)
+{
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    // NOTE: Gestures update must be called every frame to reset gestures correctly
+    // because ProcessGestureEvent() is just called on an event, not every frame
+    UpdateGestures();
+#endif
+
+    // Reset keys/chars pressed registered
+    CORE.Input.Keyboard.keyPressedQueueCount = 0;
+    CORE.Input.Keyboard.charPressedQueueCount = 0;
+
+    // Reset key repeats
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+
+    // Reset last gamepad button/axis registered state
+    CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN
+    //CORE.Input.Gamepad.axisCount = 0;
+
+    // Register previous touch states
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
+
+    // Reset touch positions
+    // TODO: It resets on target platform the mouse position and not filled again until a move-event,
+    // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
+    //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
+
+    // Register previous keys states
+    // NOTE: Android supports up to 260 keys
+    for (int i = 0; i < 260; i++)
+    {
+        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    // TODO: Poll input events for current platform
+
+    // Check for key pressed to exit
+    if (kbhit())
+    {
+        int key = getch();
+        if (key == 27) CORE.Window.shouldClose = true; // KEY_SCAPE
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+    // Memory framebuffer can only work with software renderer
+    if (rlGetVersion() != RL_OPENGL_11_SOFTWARE)
+    {
+        TRACELOG(LOG_WARNING, "DISPLAY: Memory platform requires software renderer (GRAPHICS_API_OPENGL_11_SOFTWARE)");
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+        return -1;
+    }
+    else
+    {
+        // Load memory framebuffer with desired screen size
+        platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(int));
+    }
+    //----------------------------------------------------------------------------
+
+    // If everything work as expected, we can continue
+    CORE.Window.render.width = CORE.Window.screen.width;
+    CORE.Window.render.height = CORE.Window.screen.height;
+    CORE.Window.currentFbo.width = CORE.Window.render.width;
+    CORE.Window.currentFbo.height = CORE.Window.render.height;
+
+    TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
+    TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+    TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+    TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+    TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+
+    CORE.Window.ready = true;
+
+    // TODO: Load OpenGL extensions
+    // NOTE: GL procedures address loader is required to load extensions
+    //----------------------------------------------------------------------------
+    // ...
+    //----------------------------------------------------------------------------
+
+    // TODO: Initialize input events system
+    // It could imply keyboard, mouse, gamepad, touch...
+    // Depending on the platform libraries/SDK it could use a callback mechanism
+    // For system events and inputs evens polling on a per-frame basis, use PollInputEvents()
+    //----------------------------------------------------------------------------
+    // ...
+    //----------------------------------------------------------------------------
+
+    // Initialize timing system
+    //----------------------------------------------------------------------------
+#if defined(_WIN32)
+    LARGE_INTEGER time = { 0 };
+    QueryPerformanceCounter(&time);
+    QueryPerformanceFrequency(&platform.timerFrequency);
+    CORE.Time.base = time.QuadPart;
+#endif
+    InitTimer();
+    //----------------------------------------------------------------------------
+
+    // Initialize storage system
+    //----------------------------------------------------------------------------
+    CORE.Storage.basePath = GetWorkingDirectory();
+    //----------------------------------------------------------------------------
+
+    TRACELOG(LOG_INFO, "PLATFORM: MEMORY: Initialized successfully");
+
+    return 0;
+}
+
+// Close platform
+void ClosePlatform(void)
+{
+    RL_FREE(platform.pixels);
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+#if !defined(_WIN32)
+// Check if a key has been pressed
+static int kbhit(void)
+{
+    struct termios oldt = { 0 };
+    struct termios newt = { 0 };
+    int ch = 0;
+    int oldf = 0;
+
+    tcgetattr(STDIN_FILENO, &oldt);
+    newt = oldt;
+    newt.c_lflag &= ~(ICANON | ECHO);
+    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
+    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
+
+    ch = getchar();
+
+    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+    fcntl(STDIN_FILENO, F_SETFL, oldf);
+
+    if (ch != EOF)
+    {
+        ungetc(ch, stdin);
+        return 1;
+    }
+
+    return 0;
+}
+#endif
+
+// EOF
diff --git a/raylib/src/platforms/rcore_template.c b/raylib/src/platforms/rcore_template.c
--- a/raylib/src/platforms/rcore_template.c
+++ b/raylib/src/platforms/rcore_template.c
@@ -27,7 +27,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -54,11 +54,6 @@
 typedef struct {
     // TODO: Define the platform specific variables required
 
-    // Display data
-    EGLDisplay device;                  // Native display device (physical screen connection)
-    EGLSurface surface;                 // Surface to draw on, framebuffers (connected to context)
-    EGLContext context;                 // Graphic context, mode in which drawing can be done
-    EGLConfig config;                   // Graphic config
 } PlatformData;
 
 //----------------------------------------------------------------------------------
@@ -114,7 +109,7 @@
     TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
 }
 
-// Set window state: not minimized/maximized
+// Restore window from being minimized/maximized
 void RestoreWindow(void)
 {
     TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform");
@@ -208,7 +203,7 @@
     return 1;
 }
 
-// Get number of monitors
+// Get current monitor where window is placed
 int GetCurrentMonitor(void)
 {
     TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
@@ -292,6 +287,16 @@
     return NULL;
 }
 
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
 // Show mouse cursor
 void ShowCursor(void)
 {
@@ -336,10 +341,10 @@
 double GetTime(void)
 {
     double time = 0.0;
+    
     struct timespec ts = { 0 };
     clock_gettime(CLOCK_MONOTONIC, &ts);
     unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
-
     time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
 
     return time;
@@ -356,7 +361,7 @@
     if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
     else
     {
-        // TODO:
+        // TODO: Load url using default browser
     }
 }
 
@@ -371,6 +376,12 @@
     return 0;
 }
 
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    TRACELOG(LOG_WARNING, "SetGamepadVibration() not implemented on target platform");
+}
+
 // Set mouse position XY
 void SetMousePosition(int x, int y)
 {
@@ -443,89 +454,20 @@
     // raylib uses OpenGL so, platform should create that kind of connection
     // Below example illustrates that process using EGL library
     //----------------------------------------------------------------------------
-    CORE.Window.fullscreen = true;
-    CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+    FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
 
-    EGLint samples = 0;
-    EGLint sampleBuffer = 0;
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
     {
-        samples = 4;
-        sampleBuffer = 1;
+        // TODO: Enable MSAA
+        
         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
     }
 
-    const EGLint framebufferAttribs[] =
-    {
-        EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,      // Type of context support
-        EGL_RED_SIZE, 8,            // RED color bit depth (alternative: 5)
-        EGL_GREEN_SIZE, 8,          // GREEN color bit depth (alternative: 6)
-        EGL_BLUE_SIZE, 8,           // BLUE color bit depth (alternative: 5)
-        //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI)
-        EGL_DEPTH_SIZE, 16,         // Depth buffer size (Required to use Depth testing!)
-        //EGL_STENCIL_SIZE, 8,      // Stencil buffer size
-        EGL_SAMPLE_BUFFERS, sampleBuffer,    // Activate MSAA
-        EGL_SAMPLES, samples,       // 4x Antialiasing if activated (Free on MALI GPUs)
-        EGL_NONE
-    };
-
-    const EGLint contextAttribs[] =
-    {
-        EGL_CONTEXT_CLIENT_VERSION, 2,
-        EGL_NONE
-    };
-
-    EGLint numConfigs = 0;
-
-    // Get an EGL device connection
-    platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (platform.device == EGL_NO_DISPLAY)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return false;
-    }
-
-    // Initialize the EGL device connection
-    if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE)
-    {
-        // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred.
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device");
-        return false;
-    }
-
-    // Get an appropriate EGL framebuffer configuration
-    eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs);
-
-    // Set rendering API
-    eglBindAPI(EGL_OPENGL_ES_API);
-
-    // Create an EGL rendering context
-    platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs);
-    if (platform.context == EGL_NO_CONTEXT)
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context");
-        return -1;
-    }
-
-    // Create an EGL window surface
-    EGLint displayFormat = 0;
-
-    // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry()
-    // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID
-    eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat);
-
-    // Android specific call
-    ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat);       // Force use of native display size
-
-    platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL);
-
-    // There must be at least one frame displayed before the buffers are swapped
-    eglSwapInterval(platform.device, 1);
-
-    EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context);
+    // TODO: Init display and graphic device
 
-    // Check surface and context activation
-    if (result != EGL_FALSE)
+    // TODO: Check display, device and context activation
+    bool result = true;
+    if (result)
     {
         CORE.Window.ready = true;
 
diff --git a/raylib/src/platforms/rcore_web.c b/raylib/src/platforms/rcore_web.c
--- a/raylib/src/platforms/rcore_web.c
+++ b/raylib/src/platforms/rcore_web.c
@@ -12,9 +12,6 @@
 *   POSSIBLE IMPROVEMENTS:
 *       - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js)
 *
-*   ADDITIONAL NOTES:
-*       - TRACELOG() function is located in raylib [utils] module
-*
 *   CONFIGURATION:
 *       #define RCORE_PLATFORM_CUSTOM_FLAG
 *           Custom flag for rcore on target platform -not used-
@@ -26,7 +23,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -76,6 +73,13 @@
     bool ourFullscreen;                 // Internal var to filter our handling of fullscreen vs the user handling of fullscreen
     int unmaximizedWidth;               // Internal var to store the unmaximized window (canvas) width
     int unmaximizedHeight;              // Internal var to store the unmaximized window (canvas) height
+
+    char canvasId[64];                  // Keep current canvas id where wasm app is running
+                                        // NOTE: Useful when trying to run multiple wasms in different canvases in same webpage
+
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    unsigned int *pixels;               // Pointer to pixel data buffer (RGBA 32bit format)
+#endif
 } PlatformData;
 
 //----------------------------------------------------------------------------------
@@ -86,7 +90,7 @@
 static PlatformData platform = { 0 };   // Platform specific data
 
 //----------------------------------------------------------------------------------
-// Local Variables Definition
+// Global Variables Definition
 //----------------------------------------------------------------------------------
 static const char cursorLUT[11][12] = {
     "default",     // 0  MOUSE_CURSOR_DEFAULT
@@ -102,8 +106,6 @@
     "not-allowed"  // 10 MOUSE_CURSOR_NOT_ALLOWED
 };
 
-Vector2 lockedMousePos = { 0 };
-
 //----------------------------------------------------------------------------------
 // Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
@@ -111,36 +113,45 @@
 void ClosePlatform(void);        // Close platform
 
 // Error callback event
-static void ErrorCallback(int error, const char *description);                      // GLFW3 Error Callback, runs on GLFW3 error
+static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error
 
 // Window callbacks events
-static void WindowSizeCallback(GLFWwindow *window, int width, int height);              // GLFW3 WindowSize Callback, runs when window is resized
-static void WindowIconifyCallback(GLFWwindow *window, int iconified);                   // GLFW3 WindowIconify Callback, runs when window is minimized/restored
+static void WindowSizeCallback(GLFWwindow *window, int width, int height);              // GLFW3 Window Size Callback, runs when window is resized
+static void WindowIconifyCallback(GLFWwindow *window, int iconified);                   // GLFW3 Window Iconify Callback, runs when window is minimized/restored
 //static void WindowMaximizeCallback(GLFWwindow *window, int maximized);                // GLFW3 Window Maximize Callback, runs when window is maximized
-static void WindowFocusCallback(GLFWwindow *window, int focused);                       // GLFW3 WindowFocus Callback, runs when window get/lose focus
+static void WindowFocusCallback(GLFWwindow *window, int focused);                       // GLFW3 Window Focus Callback, runs when window get/lose focus
 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);      // GLFW3 Window Drop Callback, runs when drop files into window
 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley); // GLFW3 Window Content Scale Callback, runs when window changes scale
 
 // Input callbacks events
 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed
-static void CharCallback(GLFWwindow *window, unsigned int key);                           // GLFW3 Char Key Callback, runs on key pressed (get char value)
-static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);    // GLFW3 Mouse Button Callback, runs on mouse button pressed
-static void MouseCursorPosCallback(GLFWwindow *window, double x, double y);               // GLFW3 Cursor Position Callback, runs on mouse move
-static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);      // GLFW3 Srolling Callback, runs on mouse wheel
-static void CursorEnterCallback(GLFWwindow *window, int enter);                           // GLFW3 Cursor Enter Callback, cursor enters client area
+static void CharCallback(GLFWwindow *window, unsigned int key);                         // GLFW3 Char Key Callback, runs on key pressed (get char value)
+static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods);  // GLFW3 Mouse Button Callback, runs on mouse button pressed
+static void MouseMoveCallback(GLFWwindow *window, double x, double y);                  // GLFW3 Mouse Move Callback, runs on mouse move
+static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset);    // GLFW3 Mouse Scrolling Callback, runs on mouse wheel
+static void MouseEnterCallback(GLFWwindow *window, int enter);                          // GLFW3 Mouse Enter Callback, cursor enters client area
 
 // Emscripten window callback events
 static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
-// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+//static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
 static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
+static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData);
 
 // Emscripten input callback events
+//static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData);
 static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
 static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
 static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData);
 static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
 static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
 
+// JS: Set the canvas id provided by the module configuration
+EM_JS(void, SetCanvasIdJs, (char *out, int outSize), {
+    var canvasId = "#" + Module.canvas.id;
+    stringToUTF8(canvasId, out, outSize);
+});
+
 //----------------------------------------------------------------------------------
 // Module Functions Declaration
 //----------------------------------------------------------------------------------
@@ -151,14 +162,21 @@
 //----------------------------------------------------------------------------------
 
 // Check if application should close
+// This will always return false on a web-build as web builds have no control over this functionality
+// Sleep is handled in EndDrawing() for synchronous code
 bool WindowShouldClose(void)
 {
-    // Emterpreter-Async required to run sync code
-    // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code
-    // By default, this function is never called on a web-ready raylib example because we encapsulate
-    // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously
-    // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter!
-    emscripten_sleep(16);
+    // Emscripten Asyncify is required to run synchronous code in asynchronous JS
+    // REF: https://emscripten.org/docs/porting/asyncify.html
+
+    // WindowShouldClose() is not called on a web-ready raylib application if using emscripten_set_main_loop()
+    // and encapsulating one frame execution on a UpdateDrawFrame() function,
+    // allowing the browser to manage execution asynchronously
+
+    // Optionally we can manage the time we give-control-back-to-browser if required,
+    // but it seems below line could generate stuttering on some browsers
+    emscripten_sleep(12);
+
     return false;
 }
 
@@ -171,36 +189,33 @@
     const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
     if (wasFullscreen)
     {
-        if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterFullscreen = false;
-        else if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterFullscreen = true;
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterFullscreen = false;
+        else if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterFullscreen = true;
         else
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
-            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
             if (canvasStyleWidth > canvasWidth) enterFullscreen = false;
             else enterFullscreen = true;
         }
 
         EM_ASM(document.exitFullscreen(););
 
-        CORE.Window.fullscreen = false;
-        CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
-        CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
     }
     else enterFullscreen = true;
 
     if (enterFullscreen)
     {
         // NOTE: The setTimeouts handle the browser mode change delay
-        EM_ASM
-        (
-            setTimeout(function()
-            {
+        EM_ASM(
+            setTimeout(function(){
                 Module.requestFullscreen(false, false);
             }, 100);
         );
-        CORE.Window.fullscreen = true;
-        CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+
+        FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
     }
 
     // NOTE: Old notes below:
@@ -217,17 +232,17 @@
     */
     // EM_ASM(Module.requestFullscreen(false, false););
     /*
-        if (!CORE.Window.fullscreen)
+        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
         {
             // Option 1: Request fullscreen for the canvas element
             // This option does not seem to work at all:
             // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
             // the user must click once on the canvas to hide the pointer or transition to full screen
-            //emscripten_request_fullscreen("#canvas", false);
+            //emscripten_request_fullscreen(platform.canvasId, false);
 
             // Option 2: Request fullscreen for the canvas element with strategy
             // This option does not seem to work at all
-            // Ref: https://github.com/emscripten-core/emscripten/issues/5124
+            // REF: https://github.com/emscripten-core/emscripten/issues/5124
             // EmscriptenFullscreenStrategy strategy = {
                 // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
                 // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
@@ -235,7 +250,7 @@
                 // .canvasResizedCallback = EmscriptenWindowResizedCallback,
                 // .canvasResizedCallbackUserData = NULL
             // };
-            //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
+            //emscripten_request_fullscreen_strategy(platform.canvasId, EM_FALSE, &strategy);
 
             // Option 3: Request fullscreen for the canvas element with strategy
             // It works as expected but only inside the browser (client area)
@@ -246,14 +261,14 @@
                 .canvasResizedCallback = EmscriptenWindowResizedCallback,
                 .canvasResizedCallbackUserData = NULL
             };
-            emscripten_enter_soft_fullscreen("#canvas", &strategy);
+            emscripten_enter_soft_fullscreen(platform.canvasId, &strategy);
 
-            int width, height;
-            emscripten_get_canvas_element_size("#canvas", &width, &height);
+            int width = 0;
+            int height = 0;
+            emscripten_get_canvas_element_size(platform.canvasId, &width, &height);
             TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
 
-            CORE.Window.fullscreen = true;          // Toggle fullscreen flag
-            CORE.Window.flags |= FLAG_FULLSCREEN_MODE;
+            FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
         }
         else
         {
@@ -261,11 +276,10 @@
             //emscripten_exit_soft_fullscreen();
 
             int width, height;
-            emscripten_get_canvas_element_size("#canvas", &width, &height);
+            emscripten_get_canvas_element_size(platform.canvasId, &width, &height);
             TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
 
-            CORE.Window.fullscreen = false;          // Toggle fullscreen flag
-            CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+            FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
         }
     */
 }
@@ -279,11 +293,11 @@
     const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
     if (wasFullscreen)
     {
-        if (CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) enterBorderless = false;
-        else if (CORE.Window.flags & FLAG_FULLSCREEN_MODE) enterBorderless = true;
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterBorderless = false;
+        else if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterBorderless = true;
         else
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
             const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
             if (screenWidth == canvasWidth) enterBorderless = false;
             else enterBorderless = true;
@@ -291,16 +305,15 @@
 
         EM_ASM(document.exitFullscreen(););
 
-        CORE.Window.fullscreen = false;
-        CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
-        CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
     }
     else enterBorderless = true;
 
     if (enterBorderless)
     {
-        // NOTE: 1. The setTimeouts handle the browser mode change delay
-        //       2. The style unset handles the possibility of a width="value%" like on the default shell.html file
+        // 1. The setTimeouts handle the browser mode change delay
+        // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
         EM_ASM
         (
             setTimeout(function()
@@ -312,24 +325,24 @@
                 }, 100);
             }, 100);
         );
-        CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE;
+        FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
     }
 }
 
 // Set window state: maximized, if resizable
 void MaximizeWindow(void)
 {
-    if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && !(CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
+    if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && !(FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
     {
         platform.unmaximizedWidth = CORE.Window.screen.width;
         platform.unmaximizedHeight = CORE.Window.screen.height;
 
-        const int tabWidth = EM_ASM_INT( { return window.innerWidth;  }, 0);
-        const int tabHeight = EM_ASM_INT( { return window.innerHeight; }, 0);
+        const int tabWidth = EM_ASM_INT( return window.innerWidth; );
+        const int tabHeight = EM_ASM_INT( return window.innerHeight; );
 
         if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
 
-        CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
     }
 }
 
@@ -339,113 +352,115 @@
     TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
 }
 
-// Set window state: not minimized/maximized
+// Restore window from being minimized/maximized
 void RestoreWindow(void)
 {
-    if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE && (CORE.Window.flags & FLAG_WINDOW_MAXIMIZED))
+    if ((glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) && (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)))
     {
         if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
 
-        CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
     }
 }
 
 // Set window configuration state using flags
 void SetWindowState(unsigned int flags)
 {
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
+
     // Check previous state and requested state to apply required changes
     // NOTE: In most cases the functions already change the flags internally
 
     // State change: FLAG_VSYNC_HINT
-    if ((flags & FLAG_VSYNC_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform");
     }
 
     // State change: FLAG_BORDERLESS_WINDOWED_MODE
-    if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
         // NOTE: Window state flag updated inside ToggleBorderlessWindowed() function
         const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
         if (wasFullscreen)
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
-            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
-            if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+            if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
         }
         else ToggleBorderlessWindowed();
     }
 
     // State change: FLAG_FULLSCREEN_MODE
-    if ((flags & FLAG_FULLSCREEN_MODE) > 0)
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
     {
         // NOTE: Window state flag updated inside ToggleFullscreen() function
         const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
         if (wasFullscreen)
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
             const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
-            if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || screenWidth == canvasWidth ) ToggleFullscreen();
+            if ((FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) || screenWidth == canvasWidth ) ToggleFullscreen();
         }
         else ToggleFullscreen();
     }
 
     // State change: FLAG_WINDOW_RESIZABLE
-    if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE);
-        CORE.Window.flags |= FLAG_WINDOW_RESIZABLE;
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
     }
 
     // State change: FLAG_WINDOW_UNDECORATED
-    if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_HIDDEN
-    if ((flags & FLAG_WINDOW_HIDDEN) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MINIMIZED
-    if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MAXIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
     {
         if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
         {
             platform.unmaximizedWidth = CORE.Window.screen.width;
             platform.unmaximizedHeight = CORE.Window.screen.height;
 
-            const int tabWidth = EM_ASM_INT( { return window.innerWidth;  }, 0);
-            const int tabHeight = EM_ASM_INT( { return window.innerHeight; }, 0);
+            const int tabWidth = EM_ASM_INT( return window.innerWidth; );
+            const int tabHeight = EM_ASM_INT( return window.innerHeight; );
 
             if (tabWidth && tabHeight) glfwSetWindowSize(platform.handle, tabWidth, tabHeight);
 
-            CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED;
+            FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
         }
     }
 
     // State change: FLAG_WINDOW_UNFOCUSED
-    if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_TOPMOST
-    if ((flags & FLAG_WINDOW_TOPMOST) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_ALWAYS_RUN
-    if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
     }
@@ -454,31 +469,31 @@
     // NOTE: Review for PLATFORM_WEB
 
     // State change: FLAG_WINDOW_TRANSPARENT
-    if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_HIGHDPI
-    if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
-    if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
     }
 
     // State change: FLAG_MSAA_4X_HINT
-    if ((flags & FLAG_MSAA_4X_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
     }
 
     // State change: FLAG_INTERLACED_HINT
-    if ((flags & FLAG_INTERLACED_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
     {
         TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
     }
@@ -491,90 +506,89 @@
     // NOTE: In most cases the functions already change the flags internally
 
     // State change: FLAG_VSYNC_HINT
-    if ((flags & FLAG_VSYNC_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform");
     }
 
     // State change: FLAG_BORDERLESS_WINDOWED_MODE
-    if ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
     {
         const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
         if (wasFullscreen)
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
             const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
-            if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
+            if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
         }
 
-        CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
     }
 
     // State change: FLAG_FULLSCREEN_MODE
-    if ((flags & FLAG_FULLSCREEN_MODE) > 0)
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
     {
         const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
         if (wasFullscreen)
         {
-            const int canvasWidth = EM_ASM_INT( { return document.getElementById('canvas').width; }, 0);
-            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(document.getElementById('canvas').style.width); }, 0);
-            if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+            if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
         }
 
-        CORE.Window.fullscreen = false;
-        CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
     }
 
     // State change: FLAG_WINDOW_RESIZABLE
-    if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) && (FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)))
     {
         glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE);
-        CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE;
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
     }
 
     // State change: FLAG_WINDOW_HIDDEN
-    if ((flags & FLAG_WINDOW_HIDDEN) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MINIMIZED
-    if ((flags & FLAG_WINDOW_MINIMIZED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MAXIMIZED
-    if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0))
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) && (FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)))
     {
         if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE)
         {
             if (platform.unmaximizedWidth && platform.unmaximizedHeight) glfwSetWindowSize(platform.handle, platform.unmaximizedWidth, platform.unmaximizedHeight);
 
-            CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+            FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
         }
     }
 
     // State change: FLAG_WINDOW_UNDECORATED
-    if ((flags & FLAG_WINDOW_UNDECORATED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_UNFOCUSED
-    if ((flags & FLAG_WINDOW_UNFOCUSED) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_TOPMOST
-    if ((flags & FLAG_WINDOW_TOPMOST) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_ALWAYS_RUN
-    if ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
     }
@@ -583,31 +597,31 @@
     // NOTE: Review for PLATFORM_WEB
 
     // State change: FLAG_WINDOW_TRANSPARENT
-    if ((flags & FLAG_WINDOW_TRANSPARENT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_HIGHDPI
-    if ((flags & FLAG_WINDOW_HIGHDPI) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
     }
 
     // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
-    if ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
     }
 
     // State change: FLAG_MSAA_4X_HINT
-    if ((flags & FLAG_MSAA_4X_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
     }
 
     // State change: FLAG_INTERLACED_HINT
-    if ((flags & FLAG_INTERLACED_HINT) > 0)
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
     {
         TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
     }
@@ -651,7 +665,7 @@
     CORE.Window.screenMin.height = height;
 
     // Trigger the resize event once to update the window minimum width and height
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
 }
 
 // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
@@ -661,7 +675,7 @@
     CORE.Window.screenMax.height = height;
 
     // Trigger the resize event once to update the window maximum width and height
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
 }
 
 // Set window dimensions
@@ -675,7 +689,7 @@
 {
     if (opacity >= 1.0f) opacity = 1.0f;
     else if (opacity <= 0.0f) opacity = 0.0f;
-    EM_ASM({ document.getElementById('canvas').style.opacity = $0; }, opacity);
+    EM_ASM({ Module.canvas.style.opacity = $0; }, opacity);
 }
 
 // Set window focused
@@ -698,7 +712,7 @@
     return 1;
 }
 
-// Get number of monitors
+// Get current monitor where window is placed
 int GetCurrentMonitor(void)
 {
     TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
@@ -771,8 +785,11 @@
 // Get window scale DPI factor for current monitor
 Vector2 GetWindowScaleDPI(void)
 {
-    TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform");
-    return (Vector2){ 1.0f, 1.0f };
+    // NOTE: Returned scale is relative to the current monitor where the browser window is located
+    Vector2 scale = { 1.0f, 1.0f };
+    scale.x = (float)EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
+    scale.y = scale.x;
+    return scale;
 }
 
 // Set clipboard text content
@@ -805,12 +822,22 @@
     return NULL;
 }
 
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
 // Show mouse cursor
 void ShowCursor(void)
 {
     if (CORE.Input.Mouse.cursorHidden)
     {
-        EM_ASM( { document.getElementById("canvas").style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]);
+        EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]);
 
         CORE.Input.Mouse.cursorHidden = false;
     }
@@ -821,7 +848,7 @@
 {
     if (!CORE.Input.Mouse.cursorHidden)
     {
-        EM_ASM(document.getElementById('canvas').style.cursor = 'none';);
+        EM_ASM(Module.canvas.style.cursor = 'none';);
 
         CORE.Input.Mouse.cursorHidden = true;
     }
@@ -835,25 +862,49 @@
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
-    // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
+    // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback()
 }
 
 // Disables cursor (lock cursor)
 void DisableCursor(void)
 {
-    // TODO: figure out how not to hard code the canvas ID here.
-    emscripten_request_pointerlock("#canvas", 1);
+    emscripten_request_pointerlock(platform.canvasId, 1);
 
     // Set cursor position in the middle
     SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
 
-    // NOTE: CORE.Input.Mouse.cursorHidden handled by EmscriptenPointerlockCallback()
+    // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback()
 }
 
 // Swap back buffer with front buffer (screen drawing)
 void SwapScreenBuffer(void)
 {
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Update framebuffer
+    rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
+
+    // Copy framebuffer data into canvas
+    EM_ASM({
+        const width = $0;
+        const height = $1;
+        const ptr = $2;
+
+        // Get canvas and 2d context created
+        const canvas = Module.canvas;
+        const ctx = canvas.getContext('2d');
+
+        if (!Module.__img || (Module.__img.width !== width) || (Module.__img.height !== height)) {
+            Module.__img = ctx.createImageData(width, height);
+        }
+
+        const src = HEAPU8.subarray(ptr, ptr + width*height*4); // RGBA (4 bytes)
+        Module.__img.data.set(src);
+        ctx.putImageData(Module.__img, 0, 0);
+
+    }, CORE.Window.screen.width, CORE.Window.screen.height, platform.pixels);
+#else
     glfwSwapBuffers(platform.handle);
+#endif
 }
 
 //----------------------------------------------------------------------------------
@@ -868,10 +919,9 @@
 }
 
 // Open URL with default system browser (if available)
-// NOTE: This function is only safe to use if you control the URL given.
-// A user could craft a malicious string performing another action.
-// Only call this function yourself not with user input or make sure to check the string yourself.
-// Ref: https://github.com/raysan5/raylib/issues/686
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
 void OpenURL(const char *url)
 {
     // Security check to (partially) avoid malicious code on target platform
@@ -928,7 +978,7 @@
     CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
     CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
 
-    if (CORE.Input.Mouse.cursorHidden) lockedMousePos = CORE.Input.Mouse.currentPosition;
+    if (CORE.Input.Mouse.cursorLocked) CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition;
 
     // NOTE: emscripten not implemented
     glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
@@ -939,13 +989,13 @@
 {
     if (CORE.Input.Mouse.cursor != cursor)
     {
-        if (!CORE.Input.Mouse.cursorHidden) EM_ASM( { document.getElementById('canvas').style.cursor = UTF8ToString($0); }, cursorLUT[cursor]);
+        if (!CORE.Input.Mouse.cursorLocked) EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[cursor]);
 
         CORE.Input.Mouse.cursor = cursor;
     }
 }
 
-// Get physical key name.
+// Get physical key name
 const char *GetKeyName(int key)
 {
     TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
@@ -1051,11 +1101,11 @@
                     else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
                 }
 
-                //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
+                //TRACELOG(LOG_DEBUG, "INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
             }
 
             // Register axis data for every connected gamepad
-            for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++)
+            for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++)
             {
                 CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
             }
@@ -1065,10 +1115,6 @@
     }
 
     CORE.Window.resizedLastFrame = false;
-
-    // TODO: This code does not seem to do anything??
-    //if (CORE.Window.eventWaiting) glfwWaitEvents();     // Wait for in input events before continue (drawing is paused)
-    //else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) --> WARNING: Where is key input reset?
 }
 
 //----------------------------------------------------------------------------------
@@ -1078,6 +1124,8 @@
 // Initialize platform: graphics, inputs and more
 int InitPlatform(void)
 {
+    SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
+
     glfwSetErrorCallback(ErrorCallback);
 
     // Initialize GLFW internal global state
@@ -1097,27 +1145,25 @@
     // glfwWindowHint(GLFW_AUX_BUFFERS, 0);          // Number of auxiliar buffers
 
     // Check window creation flags
-    if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true;
-
-    if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN)) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window
     else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden
 
-    if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNDECORATED)) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window
     else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window
 
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window
     else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable
 
     // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
 
     // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization
-    if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED;
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED)) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
     else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE);
     else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE);
 
     // NOTE: Some GLFW flags are not supported on HTML5
@@ -1125,10 +1171,10 @@
 
     // Scale content area based on the monitor content scale where window is placed on
     // NOTE: This feature requires emscripten 3.1.51
-    //if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
+    //if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
     //else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE);
 
-    if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
     {
         // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs
         TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4");
@@ -1136,8 +1182,8 @@
     }
 
     // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version
-    // with backward compatibility to older OpenGL versions.
-    // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context.
+    // with backward compatibility to older OpenGL versions
+    // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context
 
     // Check selection OpenGL version
     if (rlGetVersion() == RL_OPENGL_21)
@@ -1147,10 +1193,12 @@
     }
     else if (rlGetVersion() == RL_OPENGL_33)
     {
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);                 // Choose OpenGL major version (just hint)
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);                 // Choose OpenGL minor version (just hint)
-        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above!
-                                                                       // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
+        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint)
+        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint)
+        // Profiles Hint, only OpenGL 3.3 and above
+        // Possible values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE
+        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
         glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above!
         // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context
     }
@@ -1173,7 +1221,6 @@
     }
     else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context
     {
-        // TODO: It seems WebGL 2.0 context is not set despite being requested
         glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
         glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
         glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
@@ -1187,13 +1234,27 @@
     // Init fullscreen toggle required var:
     platform.ourFullscreen = false;
 
-    if (CORE.Window.fullscreen)
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Avoid creating a WebGL canvas, avoid calling glfwCreateWindow()
+    emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height);
+    EM_ASM({
+        const canvas = document.getElementById("canvas");
+        Module.canvas = canvas;
+    });
+
+    // Load memory framebuffer with desired screen size
+    // NOTE: Despite using a software framebuffer for blitting, GLFW still creates a WebGL canvas,
+    // but it is not being used, on SwapScreenBuffer() the pure software renderer is used
+    // TODO: Consider requesting another type of canvas, not a WebGL one --> Replace GLFW-web by Emscripten?
+    platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int));
+#else
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
     {
         // remember center for switchinging from fullscreen to window
         if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width))
         {
-            // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed.
-            // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11.
+            // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed
+            // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11
             CORE.Window.position.x = CORE.Window.display.width/4;
             CORE.Window.position.y = CORE.Window.display.height/4;
         }
@@ -1226,18 +1287,6 @@
 
         TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
 
-        // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example,
-        // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3),
-        // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched
-        // by the sides to fit all monitor space...
-
-        // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight
-        // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset)
-        // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale
-        // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed...
-        // HighDPI monitors are properly considered in a following similar function: SetupViewport()
-        SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height);
-
         platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL);
 
         // NOTE: Full-screen change, not working properly...
@@ -1261,28 +1310,30 @@
         TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window");
         return -1;
     }
+#endif
 
     // WARNING: glfwCreateWindow() title doesn't work with emscripten
     emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
 
     // Set window callback events
-    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default!
+    glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback);
     glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback);
     glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback);
     glfwSetDropCallback(platform.handle, WindowDropCallback);
 
-    if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
     {
-       glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
+        // Window content (framebuffer) scale callback
+        glfwSetWindowContentScaleCallback(platform.handle, WindowContentScaleCallback);
     }
 
     // Set input callback events
     glfwSetKeyCallback(platform.handle, KeyCallback);
     glfwSetCharCallback(platform.handle, CharCallback);
     glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback);
-    glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes
+    glfwSetCursorPosCallback(platform.handle, MouseMoveCallback);
     glfwSetScrollCallback(platform.handle, MouseScrollCallback);
-    glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback);
+    glfwSetCursorEnterCallback(platform.handle, MouseEnterCallback);
 
     glfwMakeContextCurrent(platform.handle);
     result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web
@@ -1312,7 +1363,7 @@
         return -1;
     }
 
-    if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow();
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) MinimizeWindow();
 
     // If graphic device is no properly initialized, we end program
     if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; }
@@ -1322,38 +1373,34 @@
     rlLoadExtensions(glfwGetProcAddress);
     //----------------------------------------------------------------------------
 
-    // Initialize input events callbacks
+    // Initialize events callbacks
     //----------------------------------------------------------------------------
-    // Setup callback functions for the DOM events
+    // Setup window events callbacks
     emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
+    emscripten_set_blur_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
+    emscripten_set_focus_callback(platform.canvasId, platform.handle, 1, EmscriptenFocusCallback);
+    emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback);
 
     // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review
-    // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas)
+    // Check fullscreen change events(note this is done on the window since most browsers don't support this on canvas)
     // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
-    // Check Resize event (note this is done on the window since most browsers don't support this on #canvas)
+    // Check Resize event (note this is done on the window since most browsers don't support this on canvas)
     emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
 
-    // Trigger this once to get initial window sizing
+    // Trigger resize callback to force initial size
     EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
 
-    // Support keyboard events -> Not used, GLFW.JS takes care of that
-    // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
-    // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback);
-
-    // Support mouse events
-    emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback);
+    // Setup input events
+    // NOTE: Keyboard callbacks only used to consume some events, libglfw.js takes care of the actual input
+    //emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback); // WRNING: Breaks input
+    //emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
+    emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
     emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
-
-    // Following the mouse delta when the mouse is locked
-    emscripten_set_mousemove_callback("#canvas", NULL, 1, EmscriptenMouseMoveCallback);
-
-    // Support touch events
-    emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-    emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback);
-
-    // Support gamepad events (not provided by GLFW3 on emscripten)
+    emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback);
+    emscripten_set_touchstart_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchend_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchmove_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchcancel_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
     emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
     emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
     //----------------------------------------------------------------------------
@@ -1380,14 +1427,15 @@
     glfwTerminate();
 }
 
-// GLFW3 Error Callback, runs on GLFW3 error
+// GLFW3 callback functions, called on GLFW registered events
+//-------------------------------------------------------------------------------------------------------
+// GLFW3: Called on errors
 static void ErrorCallback(int error, const char *description)
 {
     TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description);
 }
 
-// GLFW3 WindowSize Callback, runs when window is resizedLastFrame
-// NOTE: Window resizing not allowed by default
+// GLFW3: Called on window resizing, runs when window is resizedLastFrame
 static void WindowSizeCallback(GLFWwindow *window, int width, int height)
 {
     // Reset viewport and projection matrix for new size
@@ -1400,7 +1448,7 @@
     if (IsWindowFullscreen()) return;
 
     // Set current screen size
-    if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
     {
         Vector2 windowScaleDPI = GetWindowScaleDPI();
 
@@ -1416,34 +1464,27 @@
     // NOTE: Postprocessing texture is not scaled to new size
 }
 
+// GLFW3: Called on window content (framebuffer) scaled
 static void WindowContentScaleCallback(GLFWwindow *window, float scalex, float scaley)
 {
     CORE.Window.screenScale = MatrixScale(scalex, scaley, 1.0f);
 }
 
-// GLFW3 WindowIconify Callback, runs when window is minimized/restored
+// GLFW3: Called on windows minimized/restored
 static void WindowIconifyCallback(GLFWwindow *window, int iconified)
 {
-    if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED;  // The window was iconified
-    else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED;           // The window was restored
+    if (iconified) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);   // The window was iconified
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);           // The window was restored
 }
 
-/*
-// GLFW3 Window Maximize Callback, runs when window is maximized
-static void WindowMaximizeCallback(GLFWwindow *window, int maximized)
-{
-    // TODO.
-}
-*/
-
-// GLFW3 WindowFocus Callback, runs when window get/lose focus
+// GLFW3: Called on windows get/lose focus
 static void WindowFocusCallback(GLFWwindow *window, int focused)
 {
-    if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED;   // The window was focused
-    else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED;            // The window lost focus
+    if (focused) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);   // The window was focused
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);           // The window lost focus
 }
 
-// GLFW3 Window Drop Callback, runs when drop files into window
+// GLFW3: Called on file-drop over the window
 static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
 {
     if (count > 0)
@@ -1466,12 +1507,12 @@
         for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
         {
             CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
-            strcpy(CORE.Window.dropFilepaths[i], paths[i]);
+            strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
         }
     }
 }
 
-// GLFW3 Keyboard Callback, runs on key pressed
+// GLFW3: Called on keyboard interaction
 static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods)
 {
     if (key < 0) return;    // Security check, macOS fn key generates -1
@@ -1479,8 +1520,8 @@
     // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1
     // to work properly with our implementation (IsKeyDown/IsKeyUp checks)
     if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0;
-    else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
-    else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
+    else if (action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1;
+    else if (action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1;
 
     // Check if there is space available in the key queue
     if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS))
@@ -1494,15 +1535,15 @@
     if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE);
 }
 
-// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value)
+// GLFW3: Called on key down interaction, gets equivalent unicode char value for the key
 static void CharCallback(GLFWwindow *window, unsigned int key)
 {
     //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key);
 
     // NOTE: Registers any key down considering OS keyboard layout but
     // does not detect action events, those should be managed by user...
-    // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
-    // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char
+    // REF: https://github.com/glfw/glfw/issues/668#issuecomment-166794907
+    // REF: https://www.glfw.org/docs/latest/input_guide.html#input_char
 
     // Check if there is space available in the queue
     if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
@@ -1513,7 +1554,7 @@
     }
 }
 
-// GLFW3 Mouse Button Callback, runs on mouse button pressed
+// GLFW3: Called on mouse button interaction
 static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods)
 {
     // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now,
@@ -1529,7 +1570,7 @@
     if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
     else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
 
-    // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback()
+    // NOTE: TOUCH_ACTION_MOVE event is registered in MouseMoveCallback()
 
     // Assign a pointer ID
     gestureEvent.pointId[0] = 0;
@@ -1551,11 +1592,11 @@
 #endif
 }
 
-// GLFW3 Cursor Position Callback, runs on mouse move
-static void MouseCursorPosCallback(GLFWwindow *window, double x, double y)
+// GLFW3: Called on mouse move
+static void MouseMoveCallback(GLFWwindow *window, double x, double y)
 {
     // If the pointer is not locked, follow the position
-    if (!CORE.Input.Mouse.cursorHidden)
+    if (!CORE.Input.Mouse.cursorLocked)
     {
         CORE.Input.Mouse.currentPosition.x = (float)x;
         CORE.Input.Mouse.currentPosition.y = (float)y;
@@ -1586,143 +1627,90 @@
 #endif
 }
 
-static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
-{
-    // To emulate the GLFW_RAW_MOUSE_MOTION property.
-    if (CORE.Input.Mouse.cursorHidden)
-    {
-        CORE.Input.Mouse.previousPosition.x = lockedMousePos.x - mouseEvent->movementX;
-        CORE.Input.Mouse.previousPosition.y = lockedMousePos.y - mouseEvent->movementY;
-    }
-
-    return 1; // The event was consumed by the callback handler
-}
-
-// GLFW3 Scrolling Callback, runs on mouse wheel
+// GLFW3: Called on mouse wheel scrolling
 static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset)
 {
     CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset };
 }
 
-// GLFW3 CursorEnter Callback, when cursor enters the window
-static void CursorEnterCallback(GLFWwindow *window, int enter)
+// GLFW3: Called on mouse entering the window
+static void MouseEnterCallback(GLFWwindow *window, int enter)
 {
     if (enter) CORE.Input.Mouse.cursorOnScreen = true;
     else CORE.Input.Mouse.cursorOnScreen = false;
 }
+//-------------------------------------------------------------------------------------------------------
 
-// Register fullscreen change events
-static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
+// Emscripten callback functions, called on specific browser events
+//-------------------------------------------------------------------------------------------------------
+/*
+// Emscripten: Called on key events
+static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData)
 {
-    // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
-    //       2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets
-    if (platform.ourFullscreen) platform.ourFullscreen = false;
-    else
-    {
-        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
-        if (!wasFullscreen)
-        {
-            CORE.Window.fullscreen = false;
-            CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE;
-            CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE;
-        }
-    }
+    // WARNING: Keyboard inputs already processed through GLFW callback
 
     return 1; // The event was consumed by the callback handler
 }
-
-// Register window resize event
-// static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
-// {
-//     // TODO: Implement EmscriptenWindowResizedCallback()?
-
-//     return 1; // The event was consumed by the callback handler
-// }
-
-EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; });
-EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; });
+*/
 
-// Register DOM element resize event
-static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
+// Emscripten: Called on mouse input events
+static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
 {
-    // Don't resize non-resizeable windows
-    if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1;
-
-    // This event is called whenever the window changes sizes,
-    // so the size of the canvas object is explicitly retrieved below
-    int width = GetWindowInnerWidth();
-    int height = GetWindowInnerHeight();
-
-    if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
-    else if (width > (int)CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width;
-
-    if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
-    else if (height > (int)CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height;
-
-    emscripten_set_canvas_element_size("#canvas", width, height);
-
-    SetupViewport(width, height); // Reset viewport and projection matrix for new size
-
-    CORE.Window.currentFbo.width = width;
-    CORE.Window.currentFbo.height = height;
-    CORE.Window.resizedLastFrame = true;
-
-    if (IsWindowFullscreen()) return 1;
-
-    // Set current screen size
-    CORE.Window.screen.width = width;
-    CORE.Window.screen.height = height;
-
-    // NOTE: Postprocessing texture is not scaled to new size
+    // This is only for registering mouse click events with emscripten and doesn't need to do anything
 
-    return 0;
+    return 1; // The event was consumed by the callback handler
 }
 
-// Register mouse input events
-static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+// Emscripten: Called on mouse move events
+static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
 {
-    // This is only for registering mouse click events with emscripten and doesn't need to do anything
+    // To emulate the GLFW_RAW_MOUSE_MOTION property
+    if (CORE.Input.Mouse.cursorLocked)
+    {
+        CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.lockedPosition.x - mouseEvent->movementX;
+        CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.lockedPosition.y - mouseEvent->movementY;
+    }
 
     return 1; // The event was consumed by the callback handler
 }
 
-// Register pointer lock events
+// Emscripten: Called on pointer lock events
 static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)
 {
-    CORE.Input.Mouse.cursorHidden = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0);
+    CORE.Input.Mouse.cursorLocked = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0);
 
-    if (CORE.Input.Mouse.cursorHidden)
+    if (CORE.Input.Mouse.cursorLocked)
     {
-        lockedMousePos = CORE.Input.Mouse.currentPosition;
-        CORE.Input.Mouse.previousPosition = lockedMousePos;
+        CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition;
+        CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.lockedPosition;
     }
 
     return 1; // The event was consumed by the callback handler
 }
 
-// Register connected/disconnected gamepads events
+// Emscripten: Called on connect/disconnect gamepads events
 static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
 {
     /*
-    TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
+    TRACELOG(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
            eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
            gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
 
-    for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]);
-    for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
+    for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOG(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]);
+    for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOG(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
     */
 
-    if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS))
+    if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
     {
         CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
-        sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id);
+        snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id);
     }
     else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
 
     return 1; // The event was consumed by the callback handler
 }
 
-// Register touch input events
+// Emscripten: Called on touch input events
 static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
 {
     // Register touch points count
@@ -1733,7 +1721,7 @@
     // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
     // we are looking for actual CSS size: canvas.style.width and canvas.style.height
     // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
-    emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight);
+    emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight);
 
     for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
     {
@@ -1751,7 +1739,7 @@
         else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
     }
 
-    // Update mouse position if we detect a single touch.
+    // Update mouse position if we detect a single touch
     if (CORE.Input.Touch.pointCount == 1)
     {
         CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
@@ -1759,8 +1747,7 @@
     }
 
 #if defined(SUPPORT_GESTURES_SYSTEM)
-    GestureEvent gestureEvent = {0};
-
+    GestureEvent gestureEvent = { 0 };
     gestureEvent.pointCount = CORE.Input.Touch.pointCount;
 
     // Register touch actions
@@ -1781,12 +1768,110 @@
 
     // Gesture data is sent to gestures system for processing
     ProcessGestureEvent(gestureEvent);
-
-    // Reset the pointCount for web, if it was the last Touch End event
-    if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0;
 #endif
 
+    if (eventType == EMSCRIPTEN_EVENT_TOUCHEND)
+    {
+        // Identify the EMSCRIPTEN_EVENT_TOUCHEND and remove it from the list
+        for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
+        {
+            if (touchEvent->touches[i].isChanged)
+            {
+                // Move all touch points one position up
+                for (int j = i; j < CORE.Input.Touch.pointCount - 1; j++)
+                {
+                    CORE.Input.Touch.pointId[j] = CORE.Input.Touch.pointId[j + 1];
+                    CORE.Input.Touch.position[j] = CORE.Input.Touch.position[j + 1];
+                }
+                // Decrease touch points count to remove the last one
+                CORE.Input.Touch.pointCount--;
+                break;
+            }
+        }
+        // Clamp pointCount to avoid negative values
+        if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0;
+    }
+
     return 1; // The event was consumed by the callback handler
 }
+
+// Emscripten: Called on fullscreen change events
+static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
+{
+    // NOTE: 1. Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
+    //       2. Which is a necessary safeguard because that case will bypass the toggles CORE.Window.flags resets
+    if (platform.ourFullscreen) platform.ourFullscreen = false;
+    else
+    {
+        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+        if (!wasFullscreen)
+        {
+            FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+            FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+        }
+    }
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on resize event
+static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
+{
+    // Don't resize non-resizeable windows
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) return 1;
+
+    // This event is called whenever the window changes sizes,
+    // so the size of the canvas object is explicitly retrieved below
+    int width = EM_ASM_INT( return window.innerWidth; );
+    int height = EM_ASM_INT( return window.innerHeight; );
+
+    if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
+    else if ((width > (int)CORE.Window.screenMax.width) && (CORE.Window.screenMax.width > 0)) width = CORE.Window.screenMax.width;
+
+    if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
+    else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height;
+
+    emscripten_set_canvas_element_size(platform.canvasId, width, height);
+
+    SetupViewport(width, height); // Reset viewport and projection matrix for new size
+
+    CORE.Window.currentFbo.width = width;
+    CORE.Window.currentFbo.height = height;
+    CORE.Window.resizedLastFrame = true;
+
+    if (IsWindowFullscreen()) return 1;
+
+    // Set current screen size
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+
+    glfwSetWindowSize(platform.handle, CORE.Window.screen.width, CORE.Window.screen.height);
+
+    // NOTE: Postprocessing texture is not scaled to new size
+
+    return 0;
+}
+
+// Emscripten: Called on windows focus change events
+static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)
+{
+    EM_BOOL consumed = 1;
+    switch (eventType)
+    {
+        case EMSCRIPTEN_EVENT_BLUR: WindowFocusCallback(userData, 0); break;
+        case EMSCRIPTEN_EVENT_FOCUS: WindowFocusCallback(userData, 1); break;
+        default: consumed = 0; break;
+    }
+    return consumed;
+}
+
+// Emscripten: Called on visibility change events
+static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData)
+{
+    if (visibilityChangeEvent->hidden) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was hidden
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN);                             // The window was restored
+    return 1; // The event was consumed by the callback handler
+}
+//-------------------------------------------------------------------------------------------------------
 
 // EOF
diff --git a/raylib/src/platforms/rcore_web_emscripten.c b/raylib/src/platforms/rcore_web_emscripten.c
new file mode 100644
--- /dev/null
+++ b/raylib/src/platforms/rcore_web_emscripten.c
@@ -0,0 +1,1690 @@
+/**********************************************************************************************
+*
+*   rcore_web_emscripten - Functions to manage window, graphics device and inputs
+*
+*   PLATFORM: WEB - EMSCRIPTEN
+*       - HTML5 (WebAssembly)
+*
+*   LIMITATIONS:
+*       - TBD
+*
+*   POSSIBLE IMPROVEMENTS:
+*       - TBD
+*
+*   CONFIGURATION:
+*       #define RCORE_PLATFORM_CUSTOM_FLAG
+*           Custom flag for rcore on target platform -not used-
+*
+*   DEPENDENCIES:
+*       - emscripten: Allow interaction between browser API and C
+*       - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2025-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include <emscripten/emscripten.h>      // Emscripten functionality for C
+#include <emscripten/html5.h>           // Emscripten HTML5 library
+
+#include <sys/time.h>   // Required for: timespec, nanosleep(), select() - POSIX
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#if (_POSIX_C_SOURCE < 199309L)
+    #undef _POSIX_C_SOURCE
+    #define _POSIX_C_SOURCE 199309L     // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct {
+    char canvasId[64];                  // Current canvas id
+    EMSCRIPTEN_WEBGL_CONTEXT_HANDLE glContext; // OpenGL context
+    unsigned int *pixels;               // Pointer to pixel data buffer (RGBA 32bit format)
+} PlatformData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+extern CoreData CORE;                   // Global CORE state context
+
+static PlatformData platform = { 0 };   // Platform specific data
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static const char cursorLUT[11][12] = {
+    "default",     // 0  MOUSE_CURSOR_DEFAULT
+    "default",     // 1  MOUSE_CURSOR_ARROW
+    "text",        // 2  MOUSE_CURSOR_IBEAM
+    "crosshair",   // 3  MOUSE_CURSOR_CROSSHAIR
+    "pointer",     // 4  MOUSE_CURSOR_POINTING_HAND
+    "ew-resize",   // 5  MOUSE_CURSOR_RESIZE_EW
+    "ns-resize",   // 6  MOUSE_CURSOR_RESIZE_NS
+    "nwse-resize", // 7  MOUSE_CURSOR_RESIZE_NWSE
+    "nesw-resize", // 8  MOUSE_CURSOR_RESIZE_NESW
+    "move",        // 9  MOUSE_CURSOR_RESIZE_ALL
+    "not-allowed"  // 10 MOUSE_CURSOR_NOT_ALLOWED
+};
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+void ClosePlatform(void);        // Close platform
+
+// Emscripten window callback events
+static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData);
+static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
+static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData);
+static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData);
+// TODO: Implement GLFW3 alternative for drop callback, runs when drop files into browser/canvas
+//static void WindowDropCallback(GLFWwindow *window, int count, const char **paths);
+
+// Emscripten input callback events
+static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData);
+static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
+static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData);
+static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData);
+static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData);
+static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
+
+// JS: Set the canvas id provided by the module configuration
+EM_JS(void, SetCanvasIdJs, (char *out, int outSize), {
+    var canvasId = "#" + Module.canvas.id;
+    stringToUTF8(canvasId, out, outSize);
+});
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+// NOTE: Functions declaration is provided by raylib.h
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// Check if application should close
+// This will always return false on a web-build as web builds have no control over this functionality
+// Sleep is handled in EndDrawing() for synchronous code
+bool WindowShouldClose(void)
+{
+    // Emscripten Asyncify is required to run synchronous code in asynchronous JS
+    // REF: https://emscripten.org/docs/porting/asyncify.html
+
+    // WindowShouldClose() is not called on a web-ready raylib application if using emscripten_set_main_loop()
+    // and encapsulating one frame execution on a UpdateDrawFrame() function,
+    // allowing the browser to manage execution asynchronously
+
+    // Optionally we can manage the time we give-control-back-to-browser if required,
+    // but it seems below line could generate stuttering on some browsers
+    emscripten_sleep(12);
+
+    return false;
+}
+
+// Toggle fullscreen mode
+void ToggleFullscreen(void)
+{
+    bool enterFullscreen = false;
+
+    const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+    if (wasFullscreen)
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterFullscreen = false;
+        else if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterFullscreen = true;
+        else
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+            if (canvasStyleWidth > canvasWidth) enterFullscreen = false;
+            else enterFullscreen = true;
+        }
+
+        EM_ASM(document.exitFullscreen(););
+
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+    }
+    else enterFullscreen = true;
+
+    if (enterFullscreen)
+    {
+        // NOTE: The setTimeouts handle the browser mode change delay
+        EM_ASM
+        (
+            setTimeout(function()
+            {
+                Module.requestFullscreen(false, false);
+            }, 100);
+        );
+
+        FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+    }
+
+    // NOTE: Old notes below:
+    /*
+        EM_ASM
+        (
+            // This strategy works well while using raylib minimal web shell for emscripten,
+            // it re-scales the canvas to fullscreen using monitor resolution, for tools this
+            // is a good strategy but maybe games prefer to keep current canvas resolution and
+            // display it in fullscreen, adjusting monitor resolution if possible
+            if (document.fullscreenElement) document.exitFullscreen();
+            else Module.requestFullscreen(true, true); //false, true);
+        );
+    */
+    // EM_ASM(Module.requestFullscreen(false, false););
+    /*
+        if (!FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE))
+        {
+            // Option 1: Request fullscreen for the canvas element
+            // This option does not seem to work at all:
+            // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security,
+            // the user must click once on the canvas to hide the pointer or transition to full screen
+            //emscripten_request_fullscreen("#canvas", false);
+
+            // Option 2: Request fullscreen for the canvas element with strategy
+            // This option does not seem to work at all
+            // REF: https://github.com/emscripten-core/emscripten/issues/5124
+            // EmscriptenFullscreenStrategy strategy = {
+                // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
+                // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
+                // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
+                // .canvasResizedCallback = EmscriptenWindowResizedCallback,
+                // .canvasResizedCallbackUserData = NULL
+            // };
+            //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy);
+
+            // Option 3: Request fullscreen for the canvas element with strategy
+            // It works as expected but only inside the browser (client area)
+            EmscriptenFullscreenStrategy strategy = {
+                .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT,
+                .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF,
+                .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT,
+                .canvasResizedCallback = EmscriptenWindowResizedCallback,
+                .canvasResizedCallbackUserData = NULL
+            };
+            emscripten_enter_soft_fullscreen("#canvas", &strategy);
+
+            int width = 0;
+            int height = 0;
+            emscripten_get_canvas_element_size("#canvas", &width, &height);
+            TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height);
+
+            FLAG_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        }
+        else
+        {
+            //emscripten_exit_fullscreen();
+            //emscripten_exit_soft_fullscreen();
+
+            int width, height;
+            emscripten_get_canvas_element_size("#canvas", &width, &height);
+            TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height);
+
+            FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        }
+    */
+}
+
+// Toggle borderless windowed mode
+void ToggleBorderlessWindowed(void)
+{
+    bool enterBorderless = false;
+
+    const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+    if (wasFullscreen)
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE)) enterBorderless = false;
+        else if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) enterBorderless = true;
+        else
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
+            if (screenWidth == canvasWidth) enterBorderless = false;
+            else enterBorderless = true;
+        }
+
+        EM_ASM(document.exitFullscreen(););
+
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+    }
+    else enterBorderless = true;
+
+    if (enterBorderless)
+    {
+        // 1. The setTimeouts handle the browser mode change delay
+        // 2. The style unset handles the possibility of a width="value%" like on the default shell.html file
+        EM_ASM
+        (
+            setTimeout(function()
+            {
+                Module.requestFullscreen(false, true);
+                setTimeout(function()
+                {
+                    canvas.style.width="unset";
+                }, 100);
+            }, 100);
+        );
+        FLAG_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+    }
+}
+
+// Set window state: maximized, if resizable
+void MaximizeWindow(void)
+{
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        const int tabWidth = EM_ASM_INT( return window.innerWidth; );
+        const int tabHeight = EM_ASM_INT( return window.innerHeight; );
+
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+    }
+}
+
+// Set window state: minimized
+void MinimizeWindow(void)
+{
+    TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform");
+}
+
+// Restore window from being minimized/maximized
+void RestoreWindow(void)
+{
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+    }
+}
+
+// Set window configuration state using flags
+void SetWindowState(unsigned int flags)
+{
+    if (!CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetWindowState does nothing before window initialization, Use \"SetConfigFlags\" instead");
+
+    // Check previous state and requested state to apply required changes
+    // NOTE: In most cases the functions already change the flags internally
+
+    // State change: FLAG_VSYNC_HINT
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_VSYNC_HINT) not available on target platform");
+    }
+
+    // State change: FLAG_BORDERLESS_WINDOWED_MODE
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        // NOTE: Window state flag updated inside ToggleBorderlessWindowed() function
+        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+        if (wasFullscreen)
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+            if ((FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE)) || canvasStyleWidth > canvasWidth) ToggleBorderlessWindowed();
+        }
+        else ToggleBorderlessWindowed();
+    }
+
+    // State change: FLAG_FULLSCREEN_MODE
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
+    {
+        // NOTE: Window state flag updated inside ToggleFullscreen() function
+        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+        if (wasFullscreen)
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
+            if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) ToggleFullscreen();
+        }
+        else ToggleFullscreen();
+    }
+
+    // State change: FLAG_WINDOW_RESIZABLE
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE)) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
+    {
+        FLAG_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
+    }
+
+    // State change: FLAG_WINDOW_UNDECORATED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_HIDDEN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MINIMIZED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MAXIMIZED
+    if ((FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) != FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED)) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE))
+        {
+            const int tabWidth = EM_ASM_INT( return window.innerWidth; );
+            const int tabHeight = EM_ASM_INT( return window.innerHeight; );
+
+            FLAG_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+        }
+    }
+
+    // State change: FLAG_WINDOW_UNFOCUSED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_TOPMOST
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_ALWAYS_RUN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
+    }
+
+    // The following states can not be changed after window creation
+    // NOTE: Review for PLATFORM_WEB
+
+    // State change: FLAG_WINDOW_TRANSPARENT
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_HIGHDPI
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
+    }
+
+    // State change: FLAG_MSAA_4X_HINT
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
+    }
+
+    // State change: FLAG_INTERLACED_HINT
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
+    {
+        TRACELOG(LOG_WARNING, "SetWindowState(FLAG_INTERLACED_HINT) not available on target platform");
+    }
+}
+
+// Clear window configuration state flags
+void ClearWindowState(unsigned int flags)
+{
+    // Check previous state and requested state to apply required changes
+    // NOTE: In most cases the functions already change the flags internally
+
+    // State change: FLAG_VSYNC_HINT
+    if (FLAG_IS_SET(flags, FLAG_VSYNC_HINT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_VSYNC_HINT) not available on target platform");
+    }
+
+    // State change: FLAG_BORDERLESS_WINDOWED_MODE
+    if (FLAG_IS_SET(flags, FLAG_BORDERLESS_WINDOWED_MODE))
+    {
+        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+        if (wasFullscreen)
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int screenWidth = EM_ASM_INT( { return screen.width; }, 0);
+            if (FLAG_IS_SET(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE) || (screenWidth == canvasWidth)) EM_ASM(document.exitFullscreen(););
+        }
+
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+    }
+
+    // State change: FLAG_FULLSCREEN_MODE
+    if (FLAG_IS_SET(flags, FLAG_FULLSCREEN_MODE))
+    {
+        const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+        if (wasFullscreen)
+        {
+            const int canvasWidth = EM_ASM_INT( { return Module.canvas.width; }, 0);
+            const int canvasStyleWidth = EM_ASM_INT( { return parseInt(Module.canvas.style.width); }, 0);
+            if (FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE) || (canvasStyleWidth > canvasWidth)) EM_ASM(document.exitFullscreen(););
+        }
+
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+    }
+
+    // State change: FLAG_WINDOW_RESIZABLE
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) && FLAG_IS_SET(flags, FLAG_WINDOW_RESIZABLE))
+    {
+        FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_RESIZABLE);
+    }
+
+    // State change: FLAG_WINDOW_HIDDEN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIDDEN))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIDDEN) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MINIMIZED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MINIMIZED))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MINIMIZED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MAXIMIZED
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED) && FLAG_IS_SET(flags, FLAG_WINDOW_MAXIMIZED))
+    {
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE))
+        {
+            FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+        }
+    }
+
+    // State change: FLAG_WINDOW_UNDECORATED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNDECORATED))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNDECORATED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_UNFOCUSED
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_UNFOCUSED))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_UNFOCUSED) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_TOPMOST
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TOPMOST))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TOPMOST) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_ALWAYS_RUN
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_ALWAYS_RUN))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_ALWAYS_RUN) not available on target platform");
+    }
+
+    // The following states can not be changed after window creation
+    // NOTE: Review for PLATFORM_WEB
+
+    // State change: FLAG_WINDOW_TRANSPARENT
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_TRANSPARENT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_TRANSPARENT) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_HIGHDPI
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_HIGHDPI))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_HIGHDPI) not available on target platform");
+    }
+
+    // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH
+    if (FLAG_IS_SET(flags, FLAG_WINDOW_MOUSE_PASSTHROUGH))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_WINDOW_MOUSE_PASSTHROUGH) not available on target platform");
+    }
+
+    // State change: FLAG_MSAA_4X_HINT
+    if (FLAG_IS_SET(flags, FLAG_MSAA_4X_HINT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_MSAA_4X_HINT) not available on target platform");
+    }
+
+    // State change: FLAG_INTERLACED_HINT
+    if (FLAG_IS_SET(flags, FLAG_INTERLACED_HINT))
+    {
+        TRACELOG(LOG_WARNING, "ClearWindowState(FLAG_INTERLACED_HINT) not available on target platform");
+    }
+}
+
+// Set icon for window
+void SetWindowIcon(Image image)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform");
+}
+
+// Set icon for window, multiple images
+void SetWindowIcons(Image *images, int count)
+{
+    TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform");
+}
+
+// Set title for window
+void SetWindowTitle(const char *title)
+{
+    CORE.Window.title = title;
+    emscripten_set_window_title(title);
+}
+
+// Set window position on screen (windowed mode)
+void SetWindowPosition(int x, int y)
+{
+    TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform");
+}
+
+// Set monitor for the current window
+void SetWindowMonitor(int monitor)
+{
+    TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform");
+}
+
+// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMinSize(int width, int height)
+{
+    CORE.Window.screenMin.width = width;
+    CORE.Window.screenMin.height = height;
+
+    // Trigger the resize event once to update the window minimum width and height
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+}
+
+// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE)
+void SetWindowMaxSize(int width, int height)
+{
+    CORE.Window.screenMax.width = width;
+    CORE.Window.screenMax.height = height;
+
+    // Trigger the resize event once to update the window maximum width and height
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+}
+
+// Set window dimensions
+void SetWindowSize(int width, int height)
+{
+    // When resizing the canvas, several elements must be considered:
+    // - CSS canvas size: Web layout size, logical pixels
+    // - Canvas contained framebuffer resolution
+    // * Browser monitor, device pixel ratio (HighDPI)
+
+    double canvasCssWidth = 0.0;
+    double canvasCssHeight = 0.0;
+    emscripten_get_element_css_size(platform.canvasId, &canvasCssWidth, &canvasCssHeight);
+
+    // NOTE: emscripten_get_canvas_element_size() returns canvas framebuffer size, not CSS canvas size
+
+    // Get device pixel ratio
+    // TODO: Should DPI be considered at this point?
+    double dpr = emscripten_get_device_pixel_ratio();
+
+    // Set canvas framebuffer size
+    emscripten_set_canvas_element_size(platform.canvasId, width*dpr, height*dpr);
+
+    // Set canvas CSS size
+    // TODO: Consider canvas CSS style if already scaled 100%
+    EM_ASM({ Module.canvas.style.width = $0; }, width*dpr);
+    EM_ASM({ Module.canvas.style.height = $0; }, height*dpr);
+
+    SetupViewport(width*dpr, height*dpr); // Reset viewport and projection matrix for new size
+}
+
+// Set window opacity, value opacity is between 0.0 and 1.0
+void SetWindowOpacity(float opacity)
+{
+    if (opacity >= 1.0f) opacity = 1.0f;
+    else if (opacity <= 0.0f) opacity = 0.0f;
+
+    EM_ASM({ Module.canvas.style.opacity = $0; }, opacity);
+}
+
+// Set window focused
+void SetWindowFocused(void)
+{
+    TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform");
+}
+
+// Get native window handle
+void *GetWindowHandle(void)
+{
+    TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform");
+    return NULL;
+}
+
+// Get number of monitors
+int GetMonitorCount(void)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform");
+    return 1;
+}
+
+// Get current monitor where window is placed
+int GetCurrentMonitor(void)
+{
+    TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor position
+Vector2 GetMonitorPosition(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform");
+    return (Vector2){ 0, 0 };
+}
+
+// Get selected monitor width (currently used by monitor)
+int GetMonitorWidth(int monitor)
+{
+    // Get the width of the user's entire screen in CSS logical pixels,
+    // no physical pixels, it would require multiplying by device pixel ratio
+    // NOTE: Returned value is limited to the current monitor where the browser window is located
+    int width = 0;
+    width = EM_ASM_INT( { return window.screen.width; }, 0);
+    return width;
+}
+
+// Get selected monitor height (currently used by monitor)
+int GetMonitorHeight(int monitor)
+{
+    // Get the height of the user's entire screen in CSS logical pixels,
+    // no physical pixels, it would require multiplying by device pixel ratio
+    // NOTE: Returned value is limited to the current monitor where the browser window is located
+    int height = 0;
+    height = EM_ASM_INT( { return window.screen.height; }, 0);
+    return height;
+}
+
+// Get selected monitor physical width in millimetres
+int GetMonitorPhysicalWidth(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor physical height in millimetres
+int GetMonitorPhysicalHeight(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform");
+    return 0;
+}
+
+// Get selected monitor refresh rate
+int GetMonitorRefreshRate(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform");
+    return 0;
+}
+
+// Get the human-readable, UTF-8 encoded name of the selected monitor
+const char *GetMonitorName(int monitor)
+{
+    TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform");
+    return "";
+}
+
+// Get window position XY on monitor
+Vector2 GetWindowPosition(void)
+{
+    // Browser window position, top-left corner relative to the physical screen origin, expressed in CSS logical pixels
+    // NOTE: Returned position is relative to the current monitor where the browser window is located
+    Vector2 position = { 0, 0 };
+    position.x = (float)EM_ASM_INT( { return window.screenX; }, 0);
+    position.y = (float)EM_ASM_INT( { return window.screenY; }, 0);
+    return position;
+}
+
+// Get current monitor device pixel ratio
+Vector2 GetWindowScaleDPI(void)
+{
+    // Get device pixel ratio
+    // NOTE: Returned scale is relative to the current monitor where the browser window is located
+    Vector2 scale = { 1.0f, 1.0f };
+    scale.x = (float)EM_ASM_DOUBLE( { return window.devicePixelRatio; } );
+    scale.y = scale.x;
+    return scale;
+}
+
+// Set clipboard text content
+void SetClipboardText(const char *text)
+{
+    // Security check to (partially) avoid malicious code
+    if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character");
+    else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text);
+}
+
+// Get clipboard text content
+// NOTE: returned string is allocated and freed by GLFW
+const char *GetClipboardText(void)
+{
+/*
+    // Accessing clipboard data from browser is tricky due to security reasons
+    // The method to use is navigator.clipboard.readText() but this is an asynchronous method
+    // that will return at some moment after the function is called with the required data
+    emscripten_run_script_string("navigator.clipboard.readText() \
+        .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \
+        .catch(err => { console.error('Failed to read clipboard contents: ', err); });"
+    );
+
+    // The main issue is getting that data, one approach could be using ASYNCIFY and wait
+    // for the data but it requires adding Asyncify emscripten library on compilation
+
+    // Another approach could be just copy the data in a HTML text field and try to retrieve it
+    // later on if available... and clean it for future accesses
+*/
+    return NULL;
+}
+
+// Get clipboard image
+Image GetClipboardImage(void)
+{
+    Image image = { 0 };
+
+    // NOTE: In theory, the new navigator.clipboard.read() can be used to return arbitrary data from clipboard (2024)
+    // REF: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/read
+    TRACELOG(LOG_WARNING, "GetClipboardImage() not implemented on target platform");
+
+    return image;
+}
+
+// Show mouse cursor
+void ShowCursor(void)
+{
+    if (CORE.Input.Mouse.cursorHidden)
+    {
+        EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[CORE.Input.Mouse.cursor]);
+
+        CORE.Input.Mouse.cursorHidden = false;
+    }
+}
+
+// Hides mouse cursor
+void HideCursor(void)
+{
+    if (!CORE.Input.Mouse.cursorHidden)
+    {
+        EM_ASM(Module.canvas.style.cursor = 'none';);
+
+        CORE.Input.Mouse.cursorHidden = true;
+    }
+}
+
+// Enables cursor (unlock cursor)
+void EnableCursor(void)
+{
+    emscripten_exit_pointerlock();
+
+    // Set cursor position in the middle
+    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+    // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback()
+}
+
+// Disables cursor (lock cursor)
+void DisableCursor(void)
+{
+    emscripten_request_pointerlock(platform.canvasId, 1);
+
+    // Set cursor position in the middle
+    SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2);
+
+    // NOTE: CORE.Input.Mouse.cursorLocked handled by EmscriptenPointerlockCallback()
+}
+
+// Swap back buffer with front buffer (screen drawing)
+void SwapScreenBuffer(void)
+{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Update framebuffer
+    rlCopyFramebuffer(0, 0, CORE.Window.render.width, CORE.Window.render.height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, platform.pixels);
+
+    // Copy framebuffer data into canvas
+    EM_ASM({
+        const width = $0;
+        const height = $1;
+        const ptr = $2;
+
+        // Get canvas and 2d context created
+        const canvas = Module.canvas;
+        //const canvas = Module['canvas'];
+        const ctx = canvas.getContext('2d');
+
+        if (!Module.__img || (Module.__img.width !== width) || (Module.__img.height !== height)) {
+            Module.__img = ctx.createImageData(width, height);
+        }
+
+        const src = HEAPU8.subarray(ptr, ptr + width*height*4); // RGBA (4 bytes)
+        Module.__img.data.set(src);
+        ctx.putImageData(Module.__img, 0, 0);
+
+    }, CORE.Window.screen.width, CORE.Window.screen.height, platform.pixels);
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// Get elapsed time measure in seconds since InitTimer()
+double GetTime(void)
+{
+    double time = 0.0;
+    /*
+    struct timespec ts = { 0 };
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec;
+    time = (double)(nanoSeconds - CORE.Time.base)*1e-9;  // Elapsed time since InitTimer()
+    */
+    time = emscripten_get_now()*1000.0;
+
+    return time;
+}
+
+// Open URL with default system browser (if available)
+// NOTE: This function is only safe to use if you control the URL given
+// A user could craft a malicious string performing another action
+// Only call this function yourself not with user input or make sure to check the string yourself
+void OpenURL(const char *url)
+{
+    // Security check to (partially) avoid malicious code on target platform
+    if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character");
+    else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url));
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Inputs
+//----------------------------------------------------------------------------------
+
+// Set internal gamepad mappings
+int SetGamepadMappings(const char *mappings)
+{
+    TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c");
+
+    return 0;
+}
+
+// Set gamepad vibration
+void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration)
+{
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (duration > 0.0f))
+    {
+        if (leftMotor < 0.0f) leftMotor = 0.0f;
+        if (leftMotor > 1.0f) leftMotor = 1.0f;
+        if (rightMotor < 0.0f) rightMotor = 0.0f;
+        if (rightMotor > 1.0f) rightMotor = 1.0f;
+        if (duration > MAX_GAMEPAD_VIBRATION_TIME) duration = MAX_GAMEPAD_VIBRATION_TIME;
+        duration *= 1000.0f; // Convert duration to ms
+
+        // NOTE: [2024.10.21] Current browser support:
+        // - vibrationActuator API: Chrome, Edge, Opera, Safari, Android Chrome, Android Webview
+        // - hapticActuators API: Firefox
+        EM_ASM({
+            try { navigator.getGamepads()[$0].vibrationActuator.playEffect('dual-rumble', { startDelay: 0, duration: $3, weakMagnitude: $1, strongMagnitude: $2 }); }
+            catch (e)
+            {
+                try { navigator.getGamepads()[$0].hapticActuators[0].pulse($2, $3); }
+                catch (e) { }
+            }
+        }, gamepad, leftMotor, rightMotor, duration);
+    }
+}
+
+// Set mouse position XY
+void SetMousePosition(int x, int y)
+{
+    // WARNING: Not supported by browser for security reasons
+}
+
+// Set mouse cursor
+void SetMouseCursor(int cursor)
+{
+    if (CORE.Input.Mouse.cursor != cursor)
+    {
+        if (!CORE.Input.Mouse.cursorLocked) EM_ASM( { Module.canvas.style.cursor = UTF8ToString($0); }, cursorLUT[cursor]);
+        CORE.Input.Mouse.cursor = cursor;
+    }
+}
+
+// Get physical key name
+const char *GetKeyName(int key)
+{
+    // TODO: Browser can definitely provide a key name e->key
+    TRACELOG(LOG_WARNING, "GetKeyName() not implemented on target platform");
+    return "";
+}
+
+// Register all input events
+void PollInputEvents(void)
+{
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    // NOTE: Gestures update must be called every frame to reset gestures correctly
+    // because ProcessGestureEvent() is just called on an event, not every frame
+    UpdateGestures();
+#endif
+
+    // Reset keys/chars pressed registered
+    CORE.Input.Keyboard.keyPressedQueueCount = 0;
+    CORE.Input.Keyboard.charPressedQueueCount = 0;
+
+    // Reset last gamepad button/axis registered state
+    CORE.Input.Gamepad.lastButtonPressed = 0;       // GAMEPAD_BUTTON_UNKNOWN
+    //CORE.Input.Gamepad.axisCount = 0;
+
+    // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback)
+
+    // Register previous keys states
+    for (int i = 0; i < MAX_KEYBOARD_KEYS; i++)
+    {
+        CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i];
+        CORE.Input.Keyboard.keyRepeatInFrame[i] = 0;
+    }
+
+    // Register previous mouse states
+    for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i];
+
+    // Register previous mouse wheel state
+    CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove;
+    CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f };
+
+    // Register previous mouse position
+    CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
+
+    // Register previous touch states
+    for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i];
+
+    // Reset touch positions
+    // TODO: It resets on target platform the mouse position and not filled again until a move-event,
+    // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed!
+    //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 };
+
+    // Get number of gamepads connected
+    int numGamepads = 0;
+    if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads();
+
+    for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++)
+    {
+        // Register previous gamepad button states
+        for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
+
+        EmscriptenGamepadEvent gamepadState = { 0 };
+        int result = emscripten_get_gamepad_status(i, &gamepadState);
+
+        if (result == EMSCRIPTEN_RESULT_SUCCESS)
+        {
+            // Register buttons data for every connected gamepad
+            for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++)
+            {
+                GamepadButton button = -1;
+
+                // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface
+                switch (j)
+                {
+                    case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break;
+                    case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break;
+                    case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break;
+                    case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break;
+                    case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break;
+                    case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break;
+                    case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break;
+                    case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break;
+                    case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break;
+                    case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break;
+                    case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break;
+                    case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break;
+                    case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break;
+                    case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break;
+                    case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break;
+                    case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break;
+                    default: break;
+                }
+
+                if (button + 1 != 0)   // Check for valid button
+                {
+                    if (gamepadState.digitalButton[j] == 1)
+                    {
+                        CORE.Input.Gamepad.currentButtonState[i][button] = 1;
+                        CORE.Input.Gamepad.lastButtonPressed = button;
+                    }
+                    else CORE.Input.Gamepad.currentButtonState[i][button] = 0;
+                }
+
+                //TRACELOG(LOG_DEBUG, "INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]);
+            }
+
+            // Register axis data for every connected gamepad
+            for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXES); j++)
+            {
+                CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j];
+            }
+
+            CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes;
+        }
+    }
+
+    CORE.Window.resizedLastFrame = false;
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// Initialize platform: graphics, inputs and more
+int InitPlatform(void)
+{
+    SetCanvasIdJs(platform.canvasId, 64); // Get the current canvas id
+
+    // Initialize graphic device: display/window and graphic context
+    //----------------------------------------------------------------------------
+    emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height);
+    EmscriptenWebGLContextAttributes attribs = { 0 };
+    emscripten_webgl_init_context_attributes(&attribs);
+    attribs.alpha = EM_TRUE;
+    attribs.depth = EM_TRUE;
+    attribs.stencil = EM_FALSE;
+    attribs.antialias = EM_FALSE;
+
+    // Check window creation flags
+    // Disable FLAG_WINDOW_MINIMIZED, not supported
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+
+    // Disable FLAG_WINDOW_MAXIMIZED, not supported
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+
+    // Disable FLAG_WINDOW_TOPMOST, not supported
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_TOPMOST)) FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_TOPMOST);
+
+    // NOTE: Some other flags are not supported on HTML5
+
+    // TODO: Scale content area based on the monitor content scale where window is placed on
+
+    // Request MSAA (usually x4 on WebGL 1.0)
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT)) attribs.antialias = EM_TRUE;
+
+    // Check selection OpenGL version
+    if (rlGetVersion() == RL_OPENGL_11_SOFTWARE)
+    {
+        // Avoid creating a WebGL canvas, create 2d canvas for software rendering
+        emscripten_set_canvas_element_size(platform.canvasId, CORE.Window.screen.width, CORE.Window.screen.height);
+        EM_ASM({
+            const canvas = document.getElementById(platform.canvasId);
+            Module.canvas = canvas;
+        });
+
+        // Load memory framebuffer with desired screen size
+        platform.pixels = (unsigned int *)RL_CALLOC(CORE.Window.screen.width*CORE.Window.screen.height, sizeof(unsigned int));
+    }
+    else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context --> WebGL 1.0
+    {
+        attribs.majorVersion = 1; // WebGL 1.0 requested
+        attribs.minorVersion = 0;
+
+        // Create WebGL context
+        platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs);
+        if (platform.glContext == 0) return 0;
+
+        emscripten_webgl_make_context_current(platform.glContext);
+    }
+    else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context --> WebGL 2.0
+    {
+        attribs.majorVersion = 2; // WebGL 2.0 requested
+        attribs.minorVersion = 0;
+
+        // Create WebGL context
+        platform.glContext = emscripten_webgl_create_context(platform.canvasId, &attribs);
+        if (platform.glContext == 0) return 0;
+
+        emscripten_webgl_make_context_current(platform.glContext);
+    }
+
+    // NOTE: Getting video modes is not implemented in emscripten GLFW3 version
+    CORE.Window.display.width = CORE.Window.screen.width;
+    CORE.Window.display.height = CORE.Window.screen.height;
+    CORE.Window.render.width = CORE.Window.screen.width;
+    CORE.Window.render.height = CORE.Window.screen.height;
+
+    // Set default window title
+    emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " ");
+
+    // Check context activation
+    if ((platform.glContext != 0) || (platform.pixels != NULL))
+    {
+        CORE.Window.ready = true;
+
+        int fbWidth = CORE.Window.screen.width;
+        int fbHeight = CORE.Window.screen.height;
+
+        CORE.Window.render.width = fbWidth;
+        CORE.Window.render.height = fbHeight;
+        CORE.Window.currentFbo.width = fbWidth;
+        CORE.Window.currentFbo.height = fbHeight;
+
+        TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully");
+        TRACELOG(LOG_INFO, "    > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height);
+        TRACELOG(LOG_INFO, "    > Screen size:  %i x %i", CORE.Window.screen.width, CORE.Window.screen.height);
+        TRACELOG(LOG_INFO, "    > Render size:  %i x %i", CORE.Window.render.width, CORE.Window.render.height);
+        TRACELOG(LOG_INFO, "    > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y);
+    }
+    else
+    {
+        TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device");
+        return -1;
+    }
+
+    // Load OpenGL extensions
+    // NOTE: GL procedures address loader is required to load extensions
+    if (platform.glContext != 0) rlLoadExtensions(emscripten_webgl_get_proc_address);
+    //----------------------------------------------------------------------------
+
+    // Initialize events callbacks
+    //----------------------------------------------------------------------------
+    // Setup window/canvas events callbacks
+    emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenFullscreenChangeCallback);
+    emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback);
+    emscripten_set_blur_callback(platform.canvasId, NULL, 1, EmscriptenFocusCallback);
+    emscripten_set_focus_callback(platform.canvasId, NULL, 1, EmscriptenFocusCallback);
+    emscripten_set_visibilitychange_callback(NULL, 1, EmscriptenVisibilityChangeCallback);
+
+    // Setup input events
+    emscripten_set_keypress_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
+    emscripten_set_keydown_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
+    emscripten_set_keyup_callback(platform.canvasId, NULL, 1, EmscriptenKeyboardCallback);
+
+    emscripten_set_click_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
+    //emscripten_set_dblclick_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
+    emscripten_set_mousedown_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
+    emscripten_set_mouseup_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
+    emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseCallback);
+    emscripten_set_mousemove_callback(platform.canvasId, NULL, 1, EmscriptenMouseMoveCallback);
+    emscripten_set_wheel_callback(platform.canvasId, NULL, 1, EmscriptenMouseWheelCallback);
+    emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenPointerlockCallback);
+
+    emscripten_set_touchstart_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchend_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchmove_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+    emscripten_set_touchcancel_callback(platform.canvasId, NULL, 1, EmscriptenTouchCallback);
+
+    emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback);
+    emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback);
+
+    // Trigger resize callback to force initial size
+    EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL);
+    //----------------------------------------------------------------------------
+
+    // Initialize timing system
+    //----------------------------------------------------------------------------
+    InitTimer();
+    //----------------------------------------------------------------------------
+
+    // Initialize storage system
+    //----------------------------------------------------------------------------
+    CORE.Storage.basePath = GetWorkingDirectory();
+    //----------------------------------------------------------------------------
+
+    TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully");
+
+    return 0;
+}
+
+// Close platform
+// NOTE: Platform closing is managed by browser, so,
+// this function is actually not required, but still
+// implementing some logic behaviour
+void ClosePlatform(void)
+{
+    if (platform.pixels != NULL) RL_FREE(platform.pixels);
+    if (platform.glContext != 0) emscripten_webgl_destroy_context(platform.glContext);
+}
+
+// Emscripten callback functions, called on specific browser events
+//-------------------------------------------------------------------------------------------------------
+// Emscripten: Called on resize event
+static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData)
+{
+    // Don't resize non-resizeable windows
+    if (!FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_RESIZABLE)) return 1;
+/*
+    // Set current screen size
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+    {
+        Vector2 windowScaleDPI = GetWindowScaleDPI();
+
+        CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x);
+        CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y);
+    }
+    else
+    {
+        CORE.Window.screen.width = width;
+        CORE.Window.screen.height = height;
+    }
+*/
+    // This event is called whenever the window changes sizes,
+    // so the size of the canvas object is explicitly retrieved below
+    int width = EM_ASM_INT( return window.innerWidth; );
+    int height = EM_ASM_INT( return window.innerHeight; );
+
+    if (width < (int)CORE.Window.screenMin.width) width = CORE.Window.screenMin.width;
+    else if ((width > (int)CORE.Window.screenMax.width) && (CORE.Window.screenMax.width > 0)) width = CORE.Window.screenMax.width;
+
+    if (height < (int)CORE.Window.screenMin.height) height = CORE.Window.screenMin.height;
+    else if ((height > (int)CORE.Window.screenMax.height) && (CORE.Window.screenMax.height > 0)) height = CORE.Window.screenMax.height;
+
+    emscripten_set_canvas_element_size(platform.canvasId, width, height);
+
+    SetupViewport(width, height); // Reset viewport and projection matrix for new size
+
+    CORE.Window.currentFbo.width = width;
+    CORE.Window.currentFbo.height = height;
+    CORE.Window.resizedLastFrame = true;
+
+    if (IsWindowFullscreen()) return 1;
+
+    // Set current screen size
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+
+    // NOTE: Postprocessing texture is not scaled to new size
+
+    return 0;
+}
+
+// Emscripten: Called on windows focus change events
+static EM_BOOL EmscriptenFocusCallback(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData)
+{
+    EM_BOOL consumed = 1;
+
+    switch (eventType)
+    {
+        case EMSCRIPTEN_EVENT_BLUR: FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break; // The canvas lost focus
+        case EMSCRIPTEN_EVENT_FOCUS: FLAG_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED); break;
+        default: consumed = 0; break;
+    }
+
+    return consumed;
+}
+
+// Emscripten: Called on visibility change events
+static EM_BOOL EmscriptenVisibilityChangeCallback(int eventType, const EmscriptenVisibilityChangeEvent *visibilityChangeEvent, void *userData)
+{
+    if (visibilityChangeEvent->hidden) FLAG_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was hidden
+    else FLAG_CLEAR(CORE.Window.flags, FLAG_WINDOW_HIDDEN); // The window was restored
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on fullscreen change events
+// TODO: Review fullscreen strategy
+static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData)
+{
+    // NOTE: Reset the fullscreen flags if the user left fullscreen manually by pressing the Escape key
+    const bool wasFullscreen = EM_ASM_INT( { if (document.fullscreenElement) return 1; }, 0);
+    if (!wasFullscreen)
+    {
+        FLAG_CLEAR(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+        FLAG_CLEAR(CORE.Window.flags, FLAG_BORDERLESS_WINDOWED_MODE);
+    }
+
+    return 1; // The event was consumed by the callback handler
+}
+
+/*
+// GLFW3: Called on file-drop over the window
+// TODO: Implement Emscripten (or HTML5/JS) alternative
+static void WindowDropCallback(GLFWwindow *window, int count, const char **paths)
+{
+    if (count > 0)
+    {
+        // In case previous dropped filepaths have not been freed, we free them
+        if (CORE.Window.dropFileCount > 0)
+        {
+            for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]);
+
+            RL_FREE(CORE.Window.dropFilepaths);
+
+            CORE.Window.dropFileCount = 0;
+            CORE.Window.dropFilepaths = NULL;
+        }
+
+        // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy
+        CORE.Window.dropFileCount = count;
+        CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *));
+
+        for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++)
+        {
+            CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
+            strncpy(CORE.Window.dropFilepaths[i], paths[i], MAX_FILEPATH_LENGTH - 1);
+        }
+    }
+}
+*/
+
+// Emscripten: Called on key events
+// TODO: keyCodes should be mapped to raylib/GLFW3 Key values
+static EM_BOOL EmscriptenKeyboardCallback(int eventType, const EmscriptenKeyboardEvent *keyboardEvent, void *userData)
+{
+    switch (eventType)
+    {
+        case EMSCRIPTEN_EVENT_KEYPRESS:
+        {
+            if (keyboardEvent->repeat) CORE.Input.Keyboard.keyRepeatInFrame[keyboardEvent->keyCode] = 1;
+        } break;
+        case EMSCRIPTEN_EVENT_KEYDOWN:
+        {
+            CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 1;
+        } break;
+        case EMSCRIPTEN_EVENT_KEYUP:
+        {
+            CORE.Input.Keyboard.currentKeyState[keyboardEvent->keyCode] = 0;
+        } break;
+        default: break;
+    }
+
+    // TODO: Add char codes
+    //unsigned int charCode
+    // Check if there is space available in the queue for characters to be added
+    /*
+    if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE)
+    {
+        // Add character to the queue
+        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = keyboardEvent->charCode;
+        CORE.Input.Keyboard.charPressedQueueCount++;
+    }
+    */
+    /*
+    // Check if there is space available in the key queue
+    if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS))
+    {
+        // Add character to the queue
+        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keyboardEvent->keyCode;
+        CORE.Input.Keyboard.keyPressedQueueCount++;
+    }
+
+    // Check the exit key to set close window
+    //if ((keyboardEvent->keyCode == CORE.Input.Keyboard.exitKey) && (eventType == EMSCRIPTEN_EVENT_KEYPRESS)) CORE.Window.shouldClose = true;
+    */
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on mouse input events
+static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+    switch (eventType)
+    {
+        case EMSCRIPTEN_EVENT_MOUSEENTER: CORE.Input.Mouse.cursorOnScreen = true; break;
+        case EMSCRIPTEN_EVENT_MOUSELEAVE: CORE.Input.Mouse.cursorOnScreen = false; break;
+        case EMSCRIPTEN_EVENT_MOUSEDOWN:
+        {
+            // NOTE: Emscripten and raylib buttons indices are not aligned
+            if (mouseEvent->button == 0) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 1;
+            else if (mouseEvent->button == 1) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 1;
+            else if (mouseEvent->button == 2) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 1;
+
+            //CORE.Input.Touch.currentTouchState[button] = action;
+        } break;
+        case EMSCRIPTEN_EVENT_MOUSEUP:
+        {
+            if (mouseEvent->button == 0) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] = 0;
+            else if (mouseEvent->button == 1) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_MIDDLE] = 0;
+            else if (mouseEvent->button == 2) CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_RIGHT] = 0;
+        } break;
+        default: break;
+    }
+
+#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+    // Process mouse events as touches to be able to use mouse-gestures
+    GestureEvent gestureEvent = { 0 };
+
+    // Register touch actions
+    if ((CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] == 1) && (CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
+    else if ((CORE.Input.Mouse.currentButtonState[MOUSE_BUTTON_LEFT] == 0) && (CORE.Input.Mouse.previousButtonState[MOUSE_BUTTON_LEFT] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP;
+
+    // NOTE: TOUCH_ACTION_MOVE event is registered in MouseMoveCallback()
+
+    // Assign a pointer ID
+    gestureEvent.pointId[0] = 0;
+
+    // Register touch points count
+    gestureEvent.pointCount = 1;
+
+    // Register touch points position, only one point registered
+    gestureEvent.position[0] = GetMousePosition();
+
+    // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
+    gestureEvent.position[0].x /= (float)GetScreenWidth();
+    gestureEvent.position[0].y /= (float)GetScreenHeight();
+
+    // Gesture data is sent to gestures-system for processing
+    // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself
+    if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent);
+#endif
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on mouse move events
+static EM_BOOL EmscriptenMouseMoveCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+    if (CORE.Input.Mouse.cursorLocked)
+    {
+        CORE.Input.Mouse.previousPosition.x = CORE.Input.Mouse.lockedPosition.x - mouseEvent->movementX;
+        CORE.Input.Mouse.previousPosition.y = CORE.Input.Mouse.lockedPosition.y - mouseEvent->movementY;
+    }
+    else
+    {
+        // Get mouse position in canvas CSS pixels
+        float mouseCssX = (float)mouseEvent->canvasX;
+        float mouseCssY = (float)mouseEvent->canvasY;
+
+        // Get canvas sizes
+        double cssWidth = 0.0;
+        double cssHeight = 0.0;
+        emscripten_get_element_css_size(platform.canvasId, &cssWidth, &cssHeight);
+
+        int fbWidth = 0;
+        int fbHeight = 0;
+        emscripten_get_canvas_element_size(platform.canvasId, &fbWidth, &fbHeight);
+
+        // Convert CSS to framebuffer coordinates
+        float scaleX = (float)fbWidth/(float)cssWidth;
+        float scaleY = (float)fbHeight/(float)cssHeight;
+
+        int mouseX = (int)(mouseCssX*scaleX);
+        int mouseY = (int)(mouseCssY*scaleY);
+
+        CORE.Input.Mouse.currentPosition.x = mouseX;//(float)mouseEvent->canvasX;
+        CORE.Input.Mouse.currentPosition.y = mouseY;//(float)mouseEvent->canvasY;
+
+        // Shorter alternative:
+        //double dpr = emscripten_get_device_pixel_ratio();
+        //int mouseX = (int)(e->canvasX*dpr);
+        //int mouseY = (int)(e->canvasY*dpr);
+
+        CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition;
+    }
+
+#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES)
+    // Process mouse events as touches to be able to use mouse-gestures
+    GestureEvent gestureEvent = { 0 };
+
+    gestureEvent.touchAction = TOUCH_ACTION_MOVE;
+
+    // Assign a pointer ID
+    gestureEvent.pointId[0] = 0;
+
+    // Register touch points count
+    gestureEvent.pointCount = 1;
+
+    // Register touch points position, only one point registered
+    gestureEvent.position[0] = CORE.Input.Touch.position[0];
+
+    // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height
+    gestureEvent.position[0].x /= (float)GetScreenWidth();
+    gestureEvent.position[0].y /= (float)GetScreenHeight();
+
+    // Gesture data is sent to gestures-system for processing
+    ProcessGestureEvent(gestureEvent);
+#endif
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on mouse wheel events
+static EM_BOOL EmscriptenMouseWheelCallback(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
+{
+    if (eventType == EMSCRIPTEN_EVENT_WHEEL)
+    {
+        CORE.Input.Mouse.currentWheelMove.x = (float)wheelEvent->deltaX;
+        CORE.Input.Mouse.currentWheelMove.y = (float)wheelEvent->deltaY;
+    }
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on pointer lock events
+static EM_BOOL EmscriptenPointerlockCallback(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)
+{
+    CORE.Input.Mouse.cursorLocked = EM_ASM_INT( { if (document.pointerLockElement) return 1; }, 0);
+
+    if (CORE.Input.Mouse.cursorLocked)
+    {
+        CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition;
+        CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.lockedPosition;
+    }
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on connect/disconnect gamepads events
+static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData)
+{
+    /*
+    TRACELOG(LOG_DEBUG, "%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"",
+           eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state",
+           gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping);
+
+    for (int i = 0; i < gamepadEvent->numAxes; i++) TRACELOG(LOG_DEBUG, "Axis %d: %g", i, gamepadEvent->axis[i]);
+    for (int i = 0; i < gamepadEvent->numButtons; i++) TRACELOG(LOG_DEBUG, "Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]);
+    */
+
+    if (gamepadEvent->connected && (gamepadEvent->index < MAX_GAMEPADS))
+    {
+        CORE.Input.Gamepad.ready[gamepadEvent->index] = true;
+        snprintf(CORE.Input.Gamepad.name[gamepadEvent->index], MAX_GAMEPAD_NAME_LENGTH, "%s", gamepadEvent->id);
+    }
+    else CORE.Input.Gamepad.ready[gamepadEvent->index] = false;
+
+    return 1; // The event was consumed by the callback handler
+}
+
+// Emscripten: Called on touch input events
+static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
+{
+    // Register touch points count
+    CORE.Input.Touch.pointCount = touchEvent->numTouches;
+
+    double canvasWidth = 0.0;
+    double canvasHeight = 0.0;
+    // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but
+    // we are looking for actual CSS size: canvas.style.width and canvas.style.height
+    // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight);
+    emscripten_get_element_css_size(platform.canvasId, &canvasWidth, &canvasHeight);
+
+    for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+    {
+        // Register touch points id
+        CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier;
+
+        // Register touch points position
+        CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY};
+
+        // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height
+        CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth);
+        CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight);
+
+        if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1;
+        else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0;
+    }
+
+    // Update mouse position if we detect a single touch
+    if (CORE.Input.Touch.pointCount == 1)
+    {
+        CORE.Input.Mouse.currentPosition.x = CORE.Input.Touch.position[0].x;
+        CORE.Input.Mouse.currentPosition.y = CORE.Input.Touch.position[0].y;
+    }
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    GestureEvent gestureEvent = { 0 };
+    gestureEvent.pointCount = CORE.Input.Touch.pointCount;
+
+    // Register touch actions
+    if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN;
+    else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP;
+    else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE;
+    else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL;
+
+    for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++)
+    {
+        gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i];
+        gestureEvent.position[i] = CORE.Input.Touch.position[i];
+
+        // Normalize gestureEvent.position[i]
+        gestureEvent.position[i].x /= (float)GetScreenWidth();
+        gestureEvent.position[i].y /= (float)GetScreenHeight();
+    }
+
+    // Gesture data is sent to gestures system for processing
+    ProcessGestureEvent(gestureEvent);
+#endif
+
+    if (eventType == EMSCRIPTEN_EVENT_TOUCHEND)
+    {
+        // Identify the EMSCRIPTEN_EVENT_TOUCHEND and remove it from the list
+        for (int i = 0; i < CORE.Input.Touch.pointCount; i++)
+        {
+            if (touchEvent->touches[i].isChanged)
+            {
+                // Move all touch points one position up
+                for (int j = i; j < CORE.Input.Touch.pointCount - 1; j++)
+                {
+                    CORE.Input.Touch.pointId[j] = CORE.Input.Touch.pointId[j + 1];
+                    CORE.Input.Touch.position[j] = CORE.Input.Touch.position[j + 1];
+                }
+                // Decrease touch points count to remove the last one
+                CORE.Input.Touch.pointCount--;
+                break;
+            }
+        }
+        // Clamp pointCount to avoid negative values
+        if (CORE.Input.Touch.pointCount < 0) CORE.Input.Touch.pointCount = 0;
+    }
+
+    return 1; // The event was consumed by the callback handler
+}
+//-------------------------------------------------------------------------------------------------------
+
+// EOF
diff --git a/raylib/src/raudio.c b/raylib/src/raudio.c
--- a/raylib/src/raudio.c
+++ b/raylib/src/raudio.c
@@ -15,8 +15,8 @@
 *           raudio module is included in the build
 *
 *       #define RAUDIO_STANDALONE
-*           Define to use the module as standalone library (independently of raylib).
-*           Required types and functions are defined in the same module.
+*           Define to use the module as standalone library (independently of raylib)
+*           Required types and functions are defined in the same module
 *
 *       #define SUPPORT_FILEFORMAT_WAV
 *       #define SUPPORT_FILEFORMAT_OGG
@@ -50,7 +50,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -74,14 +74,10 @@
 #else
     #include "raylib.h"         // Declares module functions
 
-    // Check if config flags have been externally provided on compilation line
-    #if !defined(EXTERNAL_CONFIG_FLAGS)
-        #include "config.h"     // Defines module configuration flags
-    #endif
-    #include "utils.h"          // Required for: fopen() Android mapping
+    #include "config.h"         // Defines module configuration flags
 #endif
 
-#if defined(SUPPORT_MODULE_RAUDIO)
+#if defined(SUPPORT_MODULE_RAUDIO) || defined(RAUDIO_STANDALONE)
 
 #if defined(_WIN32)
 // To avoid conflicting windows.h symbols with raylib, some flags are defined
@@ -89,7 +85,7 @@
 // by user at some point and won't be included...
 //-------------------------------------------------------------------------------------
 
-// If defined, the following flags inhibit definition of the indicated items.
+// If defined, the following flags inhibit definition of the indicated items
 #define NOGDICAPMASKS     // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
 #define NOVIRTUALKEYCODES // VK_*
 #define NOWINMESSAGES     // WM_*, EM_*, LB_*, CB_*
@@ -124,9 +120,9 @@
 #define NOWH              // SetWindowsHook and WH_*
 #define NOWINOFFSETS      // GWL_*, GCL_*, associated routines
 #define NOCOMM            // COMM driver routines
-#define NOKANJI           // Kanji support stuff.
-#define NOHELP            // Help engine interface.
-#define NOPROFILER        // Profiler interface.
+#define NOKANJI           // Kanji support stuff
+#define NOHELP            // Help engine interface
+#define NOPROFILER        // Profiler interface
 #define NODEFERWINDOWPOS  // DeferWindowPos routines
 #define NOMCX             // Modem Configuration Extensions
 
@@ -407,7 +403,7 @@
 };
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage);
 
@@ -451,10 +447,10 @@
 void TrackAudioBuffer(AudioBuffer *buffer);
 void UntrackAudioBuffer(AudioBuffer *buffer);
 
-
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Audio Device initialization and Closing
 //----------------------------------------------------------------------------------
+
 // Initialize audio device
 void InitAudioDevice(void)
 {
@@ -575,7 +571,7 @@
         return NULL;
     }
 
-    if (sizeInFrames > 0) audioBuffer->data = RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
+    if (sizeInFrames > 0) audioBuffer->data = (unsigned char *)RL_CALLOC(sizeInFrames*channels*ma_get_bytes_per_sample(format), 1);
 
     // Audio data runs through a format converter
     ma_data_converter_config converterConfig = ma_data_converter_config_init(format, AUDIO_DEVICE_FORMAT, channels, AUDIO_DEVICE_CHANNELS, sampleRate, AUDIO.System.device.sampleRate);
@@ -593,7 +589,7 @@
     // Init audio buffer values
     audioBuffer->volume = 1.0f;
     audioBuffer->pitch = 1.0f;
-    audioBuffer->pan = 0.5f;
+    audioBuffer->pan = 0.0f; // Center
 
     audioBuffer->callback = NULL;
     audioBuffer->processor = NULL;
@@ -604,6 +600,7 @@
 
     audioBuffer->usage = usage;
     audioBuffer->frameCursorPos = 0;
+    audioBuffer->framesProcessed = 0;
     audioBuffer->sizeInFrames = sizeInFrames;
 
     // Buffers should be marked as processed by default so that a call to
@@ -650,6 +647,9 @@
         buffer->playing = true;
         buffer->paused = false;
         buffer->frameCursorPos = 0;
+        buffer->framesProcessed = 0;
+        buffer->isSubBufferProcessed[0] = true;
+        buffer->isSubBufferProcessed[1] = true;
         ma_mutex_unlock(&AUDIO.System.lock);
     }
 }
@@ -716,7 +716,7 @@
 // Set pan for an audio buffer
 void SetAudioBufferPan(AudioBuffer *buffer, float pan)
 {
-    if (pan < 0.0f) pan = 0.0f;
+    if (pan < -1.0f) pan = -1.0f;
     else if (pan > 1.0f) pan = 1.0f;
 
     if (buffer != NULL)
@@ -801,10 +801,10 @@
             wave.sampleRate = wav.sampleRate;
             wave.sampleSize = 16;
             wave.channels = wav.channels;
-            wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short));
+            wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short));
 
             // NOTE: We are forcing conversion to 16bit sample size on reading
-            drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, wave.data);
+            drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data);
         }
         else TRACELOG(LOG_WARNING, "WAVE: Failed to load WAV data");
 
@@ -979,9 +979,13 @@
         }
 
         audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames;
-        audioBuffer->volume = source.stream.buffer->volume;
         audioBuffer->data = source.stream.buffer->data;
 
+        // Initalize the buffer as if it was new
+        audioBuffer->volume = 1.0f;
+        audioBuffer->pitch = 1.0f;
+        audioBuffer->pan = 0.0f; // Center
+
         sound.frameCount = source.frameCount;
         sound.stream.sampleRate = AUDIO.System.device.sampleRate;
         sound.stream.sampleSize = 32;
@@ -992,7 +996,6 @@
     return sound;
 }
 
-
 // Checks if a sound is valid (data loaded and buffers initialized)
 bool IsSoundValid(Sound sound)
 {
@@ -1033,6 +1036,8 @@
 }
 
 // Update sound buffer with new data
+// PARAMS: [data], format must match sound.stream.sampleSize, default 32 bit float - stereo
+// PARAMS: [frameCount] must not exceed sound.frameCount
 void UpdateSound(Sound sound, const void *data, int frameCount)
 {
     if (sound.stream.buffer != NULL)
@@ -1082,7 +1087,7 @@
             qoa.samplerate = wave.sampleRate;
             qoa.samples = wave.frameCount;
 
-            int bytesWritten = qoa_write(fileName, wave.data, &qoa);
+            int bytesWritten = qoa_write(fileName, (const short *)wave.data, &qoa);
             if (bytesWritten > 0) success = true;
         }
         else TRACELOG(LOG_WARNING, "AUDIO: Wave data must be 16 bit per sample for QOA format export");
@@ -1114,7 +1119,7 @@
 
     // NOTE: Text data buffer size is estimated considering wave data size in bytes
     // and requiring 12 char bytes for every byte; the actual size varies, but
-    // the longest possible char being appended is "%.4ff,\n    ", which is 12 bytes.
+    // the longest possible char being appended is "%.4ff,\n    ", which is 12 bytes
     char *txtData = (char *)RL_CALLOC(waveDataSize*12 + 2000, sizeof(char));
 
     int byteCount = 0;
@@ -1125,13 +1130,13 @@
     byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                        //\n");
     byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                              //\n");
-    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)                          //\n");
+    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5)                          //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                              //\n");
     byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n");
 
     // Get file name from path and convert variable name to uppercase
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
     for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
 
     // Add wave information
@@ -1240,6 +1245,7 @@
     frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate);
     if (frameCount == 0)
     {
+        RL_FREE(data);
         TRACELOG(LOG_WARNING, "WAVE: Failed format conversion");
         return;
     }
@@ -1332,7 +1338,7 @@
 #if defined(SUPPORT_FILEFORMAT_WAV)
     else if (IsFileExtension(fileName, ".wav"))
     {
-        drwav *ctxWav = RL_CALLOC(1, sizeof(drwav));
+        drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
         bool success = drwav_init_file(ctxWav, fileName, NULL);
 
         if (success)
@@ -1382,7 +1388,7 @@
 #if defined(SUPPORT_FILEFORMAT_MP3)
     else if (IsFileExtension(fileName, ".mp3"))
     {
-        drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3));
+        drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
         int result = drmp3_init_file(ctxMp3, fileName, NULL);
 
         if (result > 0)
@@ -1473,7 +1479,7 @@
 #if defined(SUPPORT_FILEFORMAT_MOD)
     else if (IsFileExtension(fileName, ".mod"))
     {
-        jar_mod_context_t *ctxMod = RL_CALLOC(1, sizeof(jar_mod_context_t));
+        jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_CALLOC(1, sizeof(jar_mod_context_t));
         jar_mod_init(ctxMod);
         int result = jar_mod_load_file(ctxMod, fileName);
 
@@ -1524,7 +1530,7 @@
 #if defined(SUPPORT_FILEFORMAT_WAV)
     else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0))
     {
-        drwav *ctxWav = RL_CALLOC(1, sizeof(drwav));
+        drwav *ctxWav = (drwav *)RL_CALLOC(1, sizeof(drwav));
 
         bool success = drwav_init_memory(ctxWav, (const void *)data, dataSize, NULL);
 
@@ -1540,7 +1546,8 @@
             music.looping = true;   // Looping enabled by default
             musicLoaded = true;
         }
-        else {
+        else
+        {
             drwav_uninit(ctxWav);
             RL_FREE(ctxWav);
         }
@@ -1550,7 +1557,7 @@
     else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0))
     {
         // Open ogg audio stream
-        stb_vorbis* ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL);
+        stb_vorbis *ctxOgg = stb_vorbis_open_memory((const unsigned char *)data, dataSize, NULL, NULL);
 
         if (ctxOgg != NULL)
         {
@@ -1575,8 +1582,8 @@
 #if defined(SUPPORT_FILEFORMAT_MP3)
     else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0))
     {
-        drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3));
-        int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL);
+        drmp3 *ctxMp3 = (drmp3 *)RL_CALLOC(1, sizeof(drmp3));
+        int success = drmp3_init_memory(ctxMp3, (const void *)data, dataSize, NULL);
 
         if (success)
         {
@@ -1620,7 +1627,7 @@
 #if defined(SUPPORT_FILEFORMAT_FLAC)
     else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0))
     {
-        drflac *ctxFlac = drflac_open_memory((const void*)data, dataSize, NULL);
+        drflac *ctxFlac = drflac_open_memory((const void *)data, dataSize, NULL);
 
         if (ctxFlac != NULL)
         {
@@ -1757,7 +1764,7 @@
         else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData);
 #endif
 #if defined(SUPPORT_FILEFORMAT_FLAC)
-        else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL);
+        else if (music.ctxType == MUSIC_AUDIO_FLAC) { drflac_close((drflac *)music.ctxData); drflac_free((drflac *)music.ctxData, NULL); }
 #endif
 #if defined(SUPPORT_FILEFORMAT_XM)
         else if (music.ctxType == MUSIC_MODULE_XM) jar_xm_free_context((jar_xm_context_t *)music.ctxData);
@@ -1855,6 +1862,8 @@
 
     ma_mutex_lock(&AUDIO.System.lock);
     music.stream.buffer->framesProcessed = positionInFrames;
+    music.stream.buffer->isSubBufferProcessed[0] = true;
+    music.stream.buffer->isSubBufferProcessed[1] = true;
     ma_mutex_unlock(&AUDIO.System.lock);
 }
 
@@ -1862,6 +1871,7 @@
 void UpdateMusicStream(Music music)
 {
     if (music.stream.buffer == NULL) return;
+    if (!music.stream.buffer->playing) return;
 
     ma_mutex_lock(&AUDIO.System.lock);
 
@@ -1881,14 +1891,28 @@
     // Check both sub-buffers to check if they require refilling
     for (int i = 0; i < 2; i++)
     {
-        if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer
-
         unsigned int framesLeft = music.frameCount - music.stream.buffer->framesProcessed;  // Frames left to be processed
         unsigned int framesToStream = 0;                 // Total frames to be streamed
 
         if ((framesLeft >= subBufferSizeInFrames) || music.looping) framesToStream = subBufferSizeInFrames;
         else framesToStream = framesLeft;
 
+        if (framesToStream == 0)
+        {
+            // Check if both buffers have been processed
+            if (music.stream.buffer->isSubBufferProcessed[0] && music.stream.buffer->isSubBufferProcessed[1])
+            {
+                ma_mutex_unlock(&AUDIO.System.lock);
+                StopMusicStream(music);
+                return;
+            }
+
+            ma_mutex_unlock(&AUDIO.System.lock);
+            return;
+        }
+
+        if (!music.stream.buffer->isSubBufferProcessed[i]) continue; // No refilling required, move to next sub-buffer
+
         int frameCountStillNeeded = framesToStream;
         int frameCountReadTotal = 0;
 
@@ -2001,19 +2025,6 @@
         }
 
         UpdateAudioStreamInLockedState(music.stream, AUDIO.System.pcmBuffer, framesToStream);
-
-        music.stream.buffer->framesProcessed = music.stream.buffer->framesProcessed%music.frameCount;
-
-        if (framesLeft <= subBufferSizeInFrames)
-        {
-            if (!music.looping)
-            {
-                ma_mutex_unlock(&AUDIO.System.lock);
-                // Streaming is ending, we filled latest frames from input
-                StopMusicStream(music);
-                return;
-            }
-        }
     }
 
     ma_mutex_unlock(&AUDIO.System.lock);
@@ -2064,7 +2075,7 @@
         {
             uint64_t framesPlayed = 0;
 
-            jar_xm_get_position(music.ctxData, NULL, NULL, NULL, &framesPlayed);
+            jar_xm_get_position((jar_xm_context_t *)music.ctxData, NULL, NULL, NULL, &framesPlayed);
             secondsPlayed = (float)framesPlayed/music.stream.sampleRate;
         }
         else
@@ -2076,8 +2087,10 @@
             int subBufferSize = (int)music.stream.buffer->sizeInFrames/2;
             int framesInFirstBuffer = music.stream.buffer->isSubBufferProcessed[0]? 0 : subBufferSize;
             int framesInSecondBuffer = music.stream.buffer->isSubBufferProcessed[1]? 0 : subBufferSize;
+            int framesInBuffers = framesInFirstBuffer + framesInSecondBuffer;
+            if (((unsigned int)framesInBuffers > music.frameCount) && !music.looping) framesInBuffers = music.frameCount;
             int framesSentToMix = music.stream.buffer->frameCursorPos%subBufferSize;
-            int framesPlayed = (framesProcessed - framesInFirstBuffer - framesInSecondBuffer + framesSentToMix)%(int)music.frameCount;
+            int framesPlayed = (framesProcessed - framesInBuffers + framesSentToMix)%(int)music.frameCount;
             if (framesPlayed < 0) framesPlayed += music.frameCount;
             secondsPlayed = (float)framesPlayed/music.stream.sampleRate;
             ma_mutex_unlock(&AUDIO.System.lock);
@@ -2101,9 +2114,13 @@
     // The size of a streaming buffer must be at least double the size of a period
     unsigned int periodSize = AUDIO.System.device.playback.internalPeriodSizeInFrames;
 
-    // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate
-    unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? AUDIO.System.device.sampleRate/30 : AUDIO.Buffer.defaultSize;
+    // If the buffer is not set, compute one that would give us a buffer good enough for a decent frame rate at the device bit size/rate
+    int deviceBitsPerSample = AUDIO.System.device.playback.format;
+    if (deviceBitsPerSample > 4)  deviceBitsPerSample = 4;
+    deviceBitsPerSample *= AUDIO.System.device.playback.channels;
 
+    unsigned int subBufferSize = (AUDIO.Buffer.defaultSize == 0)? (AUDIO.System.device.sampleRate/30*deviceBitsPerSample) : AUDIO.Buffer.defaultSize;
+
     if (subBufferSize < periodSize) subBufferSize = periodSize;
 
     // Create a double audio buffer of defined size
@@ -2329,11 +2346,9 @@
     ma_mutex_unlock(&AUDIO.System.lock);
 }
 
-
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
-
 // Log callback function
 static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage)
 {
@@ -2459,23 +2474,18 @@
 
         float *runningFramesOut = framesOut + (totalOutputFramesProcessed*audioBuffer->converter.channelsOut);
 
-        /* At this point we can convert the data to our mixing format. */
-        ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration);    /* Safe cast. */
+        // At this point we can convert the data to our mixing format
+        ma_uint64 inputFramesProcessedThisIteration = ReadAudioBufferFramesInInternalFormat(audioBuffer, inputBuffer, (ma_uint32)inputFramesToProcessThisIteration);
         ma_uint64 outputFramesProcessedThisIteration = outputFramesToProcessThisIteration;
         ma_data_converter_process_pcm_frames(&audioBuffer->converter, inputBuffer, &inputFramesProcessedThisIteration, runningFramesOut, &outputFramesProcessedThisIteration);
 
-        totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; /* Safe cast. */
+        totalOutputFramesProcessed += (ma_uint32)outputFramesProcessedThisIteration; // Safe cast
 
-        if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration)
-        {
-            break;  /* Ran out of input data. */
-        }
+        if (inputFramesProcessedThisIteration < inputFramesToProcessThisIteration) break;  // Ran out of input data
 
-        /* This should never be hit, but will add it here for safety. Ensures we get out of the loop when no input nor output frames are processed. */
-        if (inputFramesProcessedThisIteration == 0 && outputFramesProcessedThisIteration == 0)
-        {
-            break;
-        }
+        // This should never be hit, but added here for safety
+        // Ensures we get out of the loop when no input nor output frames are processed
+        if ((inputFramesProcessedThisIteration == 0) && (outputFramesProcessedThisIteration == 0)) break;
     }
 
     return totalOutputFramesProcessed;
@@ -2589,8 +2599,8 @@
 
     if (channels == 2)  // We consider panning
     {
-        const float left = buffer->pan;
-        const float right = 1.0f - left;
+        const float right = (buffer->pan + 1.0f)/2.0f; // Normalize: [-1..1] -> [0..1]
+        const float left = 1.0f - right;
 
         // Fast sine approximation in [0..1] for pan law: y = 0.5f*x*(3 - x*x);
         const float levels[2] = { localVolume*0.5f*left*(3.0f - left*left), localVolume*0.5f*right*(3.0f - right*right) };
@@ -2675,8 +2685,7 @@
             ma_uint32 subBufferSizeInFrames = stream.buffer->sizeInFrames/2;
             unsigned char *subBuffer = stream.buffer->data + ((subBufferSizeInFrames*stream.channels*(stream.sampleSize/8))*subBufferToUpdate);
 
-            // Total frames processed in buffer is always the complete size, filled with 0 if required
-            stream.buffer->framesProcessed += subBufferSizeInFrames;
+            stream.buffer->framesProcessed += frameCount;
 
             // Does this API expect a whole buffer to be updated in one go?
             // Assuming so, but if not will need to change this logic
@@ -2726,11 +2735,13 @@
     return dot;
 }
 
-// String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
+// String pointer reverse break: returns right-most occurrence of charset in text
+static const char *strprbrk(const char *text, const char *charset)
 {
     const char *latestMatch = NULL;
-    for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
+
+    for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
+
     return latestMatch;
 }
 
@@ -2753,11 +2764,11 @@
     static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
     memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
 
-    if (filePath != NULL) strcpy(fileName, GetFileName(filePath));   // Get filename with extension
+    if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
 
-    int size = (int)strlen(fileName);   // Get size in bytes
+    int fileNameLength = (int)strlen(fileName); // Get size in bytes
 
-    for (int i = 0; (i < size) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
+    for (int i = 0; (i < fileNameLength) && (i < MAX_FILENAMEWITHOUTEXT_LENGTH); i++)
     {
         if (fileName[i] == '.')
         {
@@ -2851,7 +2862,8 @@
 
         if (file != NULL)
         {
-            int count = fprintf(file, "%s", text);
+            int count = 0;
+            if (text != NULL) count = fprintf(file, "%s", text);
 
             if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
             else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
diff --git a/raylib/src/raylib.h b/raylib/src/raylib.h
--- a/raylib/src/raylib.h
+++ b/raylib/src/raylib.h
@@ -1,22 +1,22 @@
 /**********************************************************************************************
 *
-*   raylib v5.5-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+*   raylib v5.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
 *
 *   FEATURES:
 *       - NO external dependencies, all required libraries included with raylib
 *       - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
-*                        MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.
+*                        MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5
 *       - Written in plain C code (C99) in PascalCase/camelCase notation
-*       - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile)
+*       - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3, ES2, ES3 - choose at compile)
 *       - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
-*       - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts)
+*       - Multiple Fonts formats supported (TTF, OTF, FNT, BDF, Sprite fonts)
 *       - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
 *       - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
 *       - Flexible Materials system, supporting classic maps and PBR maps
-*       - Animated 3D models supported (skeletal bones animation) (IQM)
+*       - Animated 3D models supported (skeletal bones animation) (IQM, M3D, GLTF)
 *       - Shaders support, including Model shaders and Postprocessing shaders
 *       - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
-*       - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
+*       - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, QOA, XM, MOD)
 *       - VR stereo rendering with configurable HMD device parameters
 *       - Bindings to multiple programming languages available!
 *
@@ -27,29 +27,34 @@
 *       - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
 *
 *   DEPENDENCIES (included):
-*       [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP)
-*       [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP)
+*       [rcore][GLFW] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input
+*       [rcore][RGFW] rgfw (ColleagueRiley - github.com/ColleagueRiley/RGFW) for window/context management and input
+*       [rlgl] glad/glad_gles2 (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading
 *       [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
 *
 *   OPTIONAL DEPENDENCIES (included):
-*       [rcore] msf_gif (Miles Fogle) for GIF recording
 *       [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm
 *       [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm
+*       [rcore] rprand (Ramon Santamaria) for pseudo-random numbers generation
+*       [rtextures] qoi (Dominic Szablewski - https://phoboslab.org) for QOI image manage
 *       [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
 *       [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
-*       [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms
+*       [rtextures] stb_image_resize2 (Sean Barret) for image resizing algorithms
+*       [rtextures] stb_perlin (Sean Barret) for Perlin Noise image generation
 *       [rtext] stb_truetype (Sean Barret) for ttf fonts loading
 *       [rtext] stb_rect_pack (Sean Barret) for rectangles packing
 *       [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
 *       [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
 *       [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
-*       [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)
+*       [rmodels] m3d (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d)
+*       [rmodels] vox_loader (Johann Nadalutti) for models loading (VOX)
 *       [raudio] dr_wav (David Reid) for WAV audio file loading
 *       [raudio] dr_flac (David Reid) for FLAC audio file loading
 *       [raudio] dr_mp3 (David Reid) for MP3 audio file loading
 *       [raudio] stb_vorbis (Sean Barret) for OGG audio loading
 *       [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading
 *       [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading
+*       [raudio] qoa (Dominic Szablewski - https://phoboslab.org) for QOA audio manage
 *
 *
 *   LICENSE: zlib/libpng
@@ -57,7 +62,7 @@
 *   raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
 *   BSD-like license that allows static linking with closed source software:
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -82,9 +87,9 @@
 #include <stdarg.h>     // Required for: va_list - Only used by TraceLogCallback
 
 #define RAYLIB_VERSION_MAJOR 5
-#define RAYLIB_VERSION_MINOR 5
+#define RAYLIB_VERSION_MINOR 6
 #define RAYLIB_VERSION_PATCH 0
-#define RAYLIB_VERSION  "5.5-dev"
+#define RAYLIB_VERSION  "5.6-dev"
 
 // Function specifiers in case library is build/used as a shared library
 // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
@@ -94,13 +99,13 @@
         #define __declspec(x) __attribute__((x))
     #endif
     #if defined(BUILD_LIBTYPE_SHARED)
-        #define RLAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+        #define RLAPI __declspec(dllexport)     // Building the library as a Win32 shared library (.dll)
     #elif defined(USE_LIBTYPE_SHARED)
-        #define RLAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+        #define RLAPI __declspec(dllimport)     // Using the library as a Win32 shared library (.dll)
     #endif
 #else
     #if defined(BUILD_LIBTYPE_SHARED)
-        #define RLAPI __attribute__((visibility("default"))) // We are building as a Unix shared library (.so/.dylib)
+        #define RLAPI __attribute__((visibility("default"))) // Building as a Unix shared library (.so/.dylib)
     #endif
 #endif
 
@@ -109,7 +114,7 @@
 #endif
 
 //----------------------------------------------------------------------------------
-// Some basic Defines
+// Defines and Macros
 //----------------------------------------------------------------------------------
 #ifndef PI
     #define PI 3.14159265358979323846f
@@ -152,7 +157,7 @@
     #error "C++11 or later is required. Add -std=c++11"
 #endif
 
-// NOTE: We set some defines with some data types declared by raylib
+// NOTE: Set some defines with some data types declared by raylib
 // Other modules (raymath, rlgl) also require some of those types, so,
 // to be able to use those other modules as standalone (not depending on raylib)
 // this defines are very useful for internal check and avoid type (re)definitions
@@ -195,7 +200,7 @@
 #define RAYWHITE   CLITERAL(Color){ 245, 245, 245, 255 }   // My own White (raylib logo)
 
 //----------------------------------------------------------------------------------
-// Structures Definition
+// Types and Structures Definition
 //----------------------------------------------------------------------------------
 // Boolean type
 #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
@@ -321,7 +326,7 @@
     Vector3 position;       // Camera position
     Vector3 target;         // Camera target it looks-at
     Vector3 up;             // Camera up vector (rotation over its axis)
-    float fovy;             // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic
+    float fovy;             // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane height in world units in orthographic
     int projection;         // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
 } Camera3D;
 
@@ -329,10 +334,10 @@
 
 // Camera2D, defines position/orientation in 2d space
 typedef struct Camera2D {
-    Vector2 offset;         // Camera offset (displacement from target)
-    Vector2 target;         // Camera target (rotation and zoom origin)
-    float rotation;         // Camera rotation in degrees
-    float zoom;             // Camera zoom (scaling), should be 1.0f by default
+    Vector2 offset;         // Camera offset (screen space offset from window origin)
+    Vector2 target;         // Camera target (world space target point that is mapped to screen space offset)
+	float rotation;         // Camera rotation in degrees (pivots around target)
+    float zoom;             // Camera zoom (scaling around target), must not be set to 0, set to 1.0f for no scale
 } Camera2D;
 
 // Mesh, vertex data and vao/vbo
@@ -565,8 +570,7 @@
 } TraceLogLevel;
 
 // Keyboard keys (US keyboard layout)
-// NOTE: Use GetKeyPressed() to allow redefining
-// required keys for alternative layouts
+// NOTE: Use GetKeyPressed() to allow redefining required keys for alternative layouts
 typedef enum {
     KEY_NULL            = 0,        // Key: NULL, used for no key pressed
     // Alphanumeric keys
@@ -737,7 +741,7 @@
     GAMEPAD_BUTTON_RIGHT_THUMB          // Gamepad joystick pressed button right
 } GamepadButton;
 
-// Gamepad axis
+// Gamepad axes
 typedef enum {
     GAMEPAD_AXIS_LEFT_X        = 0,     // Gamepad left stick X axis
     GAMEPAD_AXIS_LEFT_Y        = 1,     // Gamepad left stick Y axis
@@ -795,7 +799,8 @@
     SHADER_LOC_MAP_BRDF,            // Shader location: sampler2d texture: brdf
     SHADER_LOC_VERTEX_BONEIDS,      // Shader location: vertex attribute: boneIds
     SHADER_LOC_VERTEX_BONEWEIGHTS,  // Shader location: vertex attribute: boneWeights
-    SHADER_LOC_BONE_MATRICES        // Shader location: array of matrices uniform: boneMatrices
+    SHADER_LOC_BONE_MATRICES,       // Shader location: array of matrices uniform: boneMatrices
+    SHADER_LOC_VERTEX_INSTANCE_TX   // Shader location: vertex attribute: instanceTransform
 } ShaderLocationIndex;
 
 #define SHADER_LOC_MAP_DIFFUSE      SHADER_LOC_MAP_ALBEDO
@@ -811,6 +816,10 @@
     SHADER_UNIFORM_IVEC2,           // Shader uniform type: ivec2 (2 int)
     SHADER_UNIFORM_IVEC3,           // Shader uniform type: ivec3 (3 int)
     SHADER_UNIFORM_IVEC4,           // Shader uniform type: ivec4 (4 int)
+    SHADER_UNIFORM_UINT,            // Shader uniform type: unsigned int
+    SHADER_UNIFORM_UIVEC2,          // Shader uniform type: uivec2 (2 unsigned int)
+    SHADER_UNIFORM_UIVEC3,          // Shader uniform type: uivec3 (3 unsigned int)
+    SHADER_UNIFORM_UIVEC4,          // Shader uniform type: uivec4 (4 unsigned int)
     SHADER_UNIFORM_SAMPLER2D        // Shader uniform type: sampler2d
 } ShaderUniformDataType;
 
@@ -877,8 +886,7 @@
     CUBEMAP_LAYOUT_LINE_VERTICAL,           // Layout is defined by a vertical line with faces
     CUBEMAP_LAYOUT_LINE_HORIZONTAL,         // Layout is defined by a horizontal line with faces
     CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR,     // Layout is defined by a 3x4 cross with cubemap faces
-    CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE,     // Layout is defined by a 4x3 cross with cubemap faces
-    CUBEMAP_LAYOUT_PANORAMA                 // Layout is defined by a panorama image (equirrectangular map)
+    CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE     // Layout is defined by a 4x3 cross with cubemap faces
 } CubemapLayout;
 
 // Font type, defines generation method
@@ -944,7 +952,7 @@
 typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize);    // FileIO: Load binary data
 typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize);   // FileIO: Save binary data
 typedef char *(*LoadFileTextCallback)(const char *fileName);            // FileIO: Load text data
-typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data
+typedef bool (*SaveFileTextCallback)(const char *fileName, const char *text); // FileIO: Save text data
 
 //------------------------------------------------------------------------------------
 // Global Variables Definition
@@ -965,36 +973,36 @@
 RLAPI bool WindowShouldClose(void);                               // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked)
 RLAPI bool IsWindowReady(void);                                   // Check if window has been initialized successfully
 RLAPI bool IsWindowFullscreen(void);                              // Check if window is currently fullscreen
-RLAPI bool IsWindowHidden(void);                                  // Check if window is currently hidden (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowMinimized(void);                               // Check if window is currently minimized (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowMaximized(void);                               // Check if window is currently maximized (only PLATFORM_DESKTOP)
-RLAPI bool IsWindowFocused(void);                                 // Check if window is currently focused (only PLATFORM_DESKTOP)
+RLAPI bool IsWindowHidden(void);                                  // Check if window is currently hidden
+RLAPI bool IsWindowMinimized(void);                               // Check if window is currently minimized
+RLAPI bool IsWindowMaximized(void);                               // Check if window is currently maximized
+RLAPI bool IsWindowFocused(void);                                 // Check if window is currently focused
 RLAPI bool IsWindowResized(void);                                 // Check if window has been resized last frame
 RLAPI bool IsWindowState(unsigned int flag);                      // Check if one specific window flag is enabled
-RLAPI void SetWindowState(unsigned int flags);                    // Set window configuration state using flags (only PLATFORM_DESKTOP)
+RLAPI void SetWindowState(unsigned int flags);                    // Set window configuration state using flags
 RLAPI void ClearWindowState(unsigned int flags);                  // Clear window configuration state flags
-RLAPI void ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed [resizes monitor to match window resolution] (only PLATFORM_DESKTOP)
-RLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed [resizes window to match monitor resolution] (only PLATFORM_DESKTOP)
-RLAPI void MaximizeWindow(void);                                  // Set window state: maximized, if resizable (only PLATFORM_DESKTOP)
-RLAPI void MinimizeWindow(void);                                  // Set window state: minimized, if resizable (only PLATFORM_DESKTOP)
-RLAPI void RestoreWindow(void);                                   // Set window state: not minimized/maximized (only PLATFORM_DESKTOP)
-RLAPI void SetWindowIcon(Image image);                            // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP)
-RLAPI void SetWindowIcons(Image *images, int count);              // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP)
-RLAPI void SetWindowTitle(const char *title);                     // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)
-RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen (only PLATFORM_DESKTOP)
+RLAPI void ToggleFullscreen(void);                                // Toggle window state: fullscreen/windowed, resizes monitor to match window resolution
+RLAPI void ToggleBorderlessWindowed(void);                        // Toggle window state: borderless windowed, resizes window to match monitor resolution
+RLAPI void MaximizeWindow(void);                                  // Set window state: maximized, if resizable
+RLAPI void MinimizeWindow(void);                                  // Set window state: minimized, if resizable
+RLAPI void RestoreWindow(void);                                   // Restore window from being minimized/maximized
+RLAPI void SetWindowIcon(Image image);                            // Set icon for window (single image, RGBA 32bit)
+RLAPI void SetWindowIcons(Image *images, int count);              // Set icon for window (multiple images, RGBA 32bit)
+RLAPI void SetWindowTitle(const char *title);                     // Set title for window
+RLAPI void SetWindowPosition(int x, int y);                       // Set window position on screen
 RLAPI void SetWindowMonitor(int monitor);                         // Set monitor for the current window
 RLAPI void SetWindowMinSize(int width, int height);               // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowMaxSize(int width, int height);               // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)
 RLAPI void SetWindowSize(int width, int height);                  // Set window dimensions
-RLAPI void SetWindowOpacity(float opacity);                       // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP)
-RLAPI void SetWindowFocused(void);                                // Set window focused (only PLATFORM_DESKTOP)
+RLAPI void SetWindowOpacity(float opacity);                       // Set window opacity [0.0f..1.0f]
+RLAPI void SetWindowFocused(void);                                // Set window focused
 RLAPI void *GetWindowHandle(void);                                // Get native window handle
 RLAPI int GetScreenWidth(void);                                   // Get current screen width
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 RLAPI int GetRenderWidth(void);                                   // Get current render width (it considers HiDPI)
 RLAPI int GetRenderHeight(void);                                  // Get current render height (it considers HiDPI)
 RLAPI int GetMonitorCount(void);                                  // Get number of connected monitors
-RLAPI int GetCurrentMonitor(void);                                // Get current connected monitor
+RLAPI int GetCurrentMonitor(void);                                // Get current monitor where window is placed
 RLAPI Vector2 GetMonitorPosition(int monitor);                    // Get specified monitor position
 RLAPI int GetMonitorWidth(int monitor);                           // Get specified monitor width (current video mode used by monitor)
 RLAPI int GetMonitorHeight(int monitor);                          // Get specified monitor height (current video mode used by monitor)
@@ -1006,6 +1014,7 @@
 RLAPI const char *GetMonitorName(int monitor);                    // Get the human-readable, UTF-8 encoded name of the specified monitor
 RLAPI void SetClipboardText(const char *text);                    // Set clipboard text content
 RLAPI const char *GetClipboardText(void);                         // Get clipboard text content
+RLAPI Image GetClipboardImage(void);                              // Get clipboard image content
 RLAPI void EnableEventWaiting(void);                              // Enable waiting for events on EndDrawing(), no automatic event polling
 RLAPI void DisableEventWaiting(void);                             // Disable waiting for events on EndDrawing(), automatic events polling
 
@@ -1050,7 +1059,7 @@
 RLAPI void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType);               // Set shader uniform value
 RLAPI void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count);   // Set shader uniform value vector
 RLAPI void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat);         // Set shader uniform value (matrix 4x4)
-RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value for texture (sampler2d)
+RLAPI void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture); // Set shader uniform value and bind the texture (sampler2d)
 RLAPI void UnloadShader(Shader shader);                                    // Unload shader from GPU memory (VRAM)
 
 // Screen-space-related functions
@@ -1065,99 +1074,104 @@
 RLAPI Matrix GetCameraMatrix2D(Camera2D camera);                        // Get camera 2d transform matrix
 
 // Timing-related functions
-RLAPI void SetTargetFPS(int fps);                                 // Set target FPS (maximum)
-RLAPI float GetFrameTime(void);                                   // Get time in seconds for last frame drawn (delta time)
-RLAPI double GetTime(void);                                       // Get elapsed time in seconds since InitWindow()
-RLAPI int GetFPS(void);                                           // Get current FPS
+RLAPI void SetTargetFPS(int fps);                       // Set target FPS (maximum)
+RLAPI float GetFrameTime(void);                         // Get time in seconds for last frame drawn (delta time)
+RLAPI double GetTime(void);                             // Get elapsed time in seconds since InitWindow()
+RLAPI int GetFPS(void);                                 // Get current FPS
 
 // Custom frame control functions
 // NOTE: Those functions are intended for advanced users that want full control over the frame processing
 // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
 // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
-RLAPI void SwapScreenBuffer(void);                                // Swap back buffer with front buffer (screen drawing)
-RLAPI void PollInputEvents(void);                                 // Register all input events
-RLAPI void WaitTime(double seconds);                              // Wait for some time (halt program execution)
+RLAPI void SwapScreenBuffer(void);                      // Swap back buffer with front buffer (screen drawing)
+RLAPI void PollInputEvents(void);                       // Register all input events
+RLAPI void WaitTime(double seconds);                    // Wait for some time (halt program execution)
 
 // Random values generation functions
-RLAPI void SetRandomSeed(unsigned int seed);                      // Set the seed for the random number generator
-RLAPI int GetRandomValue(int min, int max);                       // Get a random value between min and max (both included)
+RLAPI void SetRandomSeed(unsigned int seed);            // Set the seed for the random number generator
+RLAPI int GetRandomValue(int min, int max);             // Get a random value between min and max (both included)
 RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated
-RLAPI void UnloadRandomSequence(int *sequence);                   // Unload random values sequence
+RLAPI void UnloadRandomSequence(int *sequence);         // Unload random values sequence
 
 // Misc. functions
-RLAPI void TakeScreenshot(const char *fileName);                  // Takes a screenshot of current screen (filename extension defines format)
-RLAPI void SetConfigFlags(unsigned int flags);                    // Setup init configuration flags (view FLAGS)
-RLAPI void OpenURL(const char *url);                              // Open URL with default system browser (if available)
+RLAPI void TakeScreenshot(const char *fileName);                // Takes a screenshot of current screen (filename extension defines format)
+RLAPI void SetConfigFlags(unsigned int flags);                  // Setup init configuration flags (view FLAGS)
+RLAPI void OpenURL(const char *url);                            // Open URL with default system browser (if available)
 
-// NOTE: Following functions implemented in module [utils]
-//------------------------------------------------------------------
-RLAPI void TraceLog(int logLevel, const char *text, ...);         // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
-RLAPI void SetTraceLogLevel(int logLevel);                        // Set the current threshold (minimum) log level
-RLAPI void *MemAlloc(unsigned int size);                          // Internal memory allocator
-RLAPI void *MemRealloc(void *ptr, unsigned int size);             // Internal memory reallocator
-RLAPI void MemFree(void *ptr);                                    // Internal memory free
+// Logging system
+RLAPI void SetTraceLogLevel(int logLevel);                      // Set the current threshold (minimum) log level
+RLAPI void TraceLog(int logLevel, const char *text, ...);       // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...)
+RLAPI void SetTraceLogCallback(TraceLogCallback callback);      // Set custom trace log
 
-// Set custom callbacks
-// WARNING: Callbacks setup is intended for advanced users
-RLAPI void SetTraceLogCallback(TraceLogCallback callback);         // Set custom trace log
-RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback); // Set custom file binary data loader
-RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback); // Set custom file binary data saver
-RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom file text data loader
-RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver
+// Memory management, using internal allocators 
+RLAPI void *MemAlloc(unsigned int size);                        // Internal memory allocator
+RLAPI void *MemRealloc(void *ptr, unsigned int size);           // Internal memory reallocator
+RLAPI void MemFree(void *ptr);                                  // Internal memory free
 
-// Files management functions
+// File system management functions
 RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read)
-RLAPI void UnloadFileData(unsigned char *data);                   // Unload file data allocated by LoadFileData()
+RLAPI void UnloadFileData(unsigned char *data);                     // Unload file data allocated by LoadFileData()
 RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success
 RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success
-RLAPI char *LoadFileText(const char *fileName);                   // Load text data from file (read), returns a '\0' terminated string
-RLAPI void UnloadFileText(char *text);                            // Unload file text data allocated by LoadFileText()
-RLAPI bool SaveFileText(const char *fileName, char *text);        // Save text data to file (write), string must be '\0' terminated, returns true on success
-//------------------------------------------------------------------
+RLAPI char *LoadFileText(const char *fileName);                     // Load text data from file (read), returns a '\0' terminated string
+RLAPI void UnloadFileText(char *text);                              // Unload file text data allocated by LoadFileText()
+RLAPI bool SaveFileText(const char *fileName, const char *text);    // Save text data to file (write), string must be '\0' terminated, returns true on success
 
-// File system functions
-RLAPI bool FileExists(const char *fileName);                      // Check if file exists
-RLAPI bool DirectoryExists(const char *dirPath);                  // Check if a directory path exists
-RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav)
-RLAPI int GetFileLength(const char *fileName);                    // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)
-RLAPI const char *GetFileExtension(const char *fileName);         // Get pointer to extension for a filename string (includes dot: '.png')
-RLAPI const char *GetFileName(const char *filePath);              // Get pointer to filename for a path string
-RLAPI const char *GetFileNameWithoutExt(const char *filePath);    // Get filename string without extension (uses static string)
-RLAPI const char *GetDirectoryPath(const char *filePath);         // Get full path for a given fileName with path (uses static string)
-RLAPI const char *GetPrevDirectoryPath(const char *dirPath);      // Get previous directory path for a given path (uses static string)
-RLAPI const char *GetWorkingDirectory(void);                      // Get current working directory (uses static string)
-RLAPI const char *GetApplicationDirectory(void);                  // Get the directory of the running application (uses static string)
-RLAPI int MakeDirectory(const char *dirPath);                     // Create directories (including full path requested), returns 0 on success
-RLAPI bool ChangeDirectory(const char *dir);                      // Change working directory, return true on success
-RLAPI bool IsPathFile(const char *path);                          // Check if a given path is a file or a directory
-RLAPI bool IsFileNameValid(const char *fileName);                 // Check if fileName is valid for the platform/OS
-RLAPI FilePathList LoadDirectoryFiles(const char *dirPath);       // Load directory filepaths
+// File access custom callbacks
+// WARNING: Callbacks setup is intended for advanced users
+RLAPI void SetLoadFileDataCallback(LoadFileDataCallback callback);  // Set custom file binary data loader
+RLAPI void SetSaveFileDataCallback(SaveFileDataCallback callback);  // Set custom file binary data saver
+RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback);  // Set custom file text data loader
+RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback);  // Set custom file text data saver
+
+RLAPI int FileRename(const char *fileName, const char *fileRename); // Rename file (if exists)
+RLAPI int FileRemove(const char *fileName);                         // Remove file (if exists)
+RLAPI int FileCopy(const char *srcPath, const char *dstPath);       // Copy file from one path to another, dstPath created if it doesn't exist
+RLAPI int FileMove(const char *srcPath, const char *dstPath);       // Move file from one directory to another, dstPath created if it doesn't exist
+RLAPI int FileTextReplace(const char *fileName, const char *search, const char *replacement); // Replace text in an existing file
+RLAPI int FileTextFindIndex(const char *fileName, const char *search); // Find text in existing file
+RLAPI bool FileExists(const char *fileName);                        // Check if file exists
+RLAPI bool DirectoryExists(const char *dirPath);                    // Check if a directory path exists
+RLAPI bool IsFileExtension(const char *fileName, const char *ext);  // Check file extension (recommended include point: .png, .wav)
+RLAPI int GetFileLength(const char *fileName);                      // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h)
+RLAPI long GetFileModTime(const char *fileName);                    // Get file modification time (last write time)
+RLAPI const char *GetFileExtension(const char *fileName);           // Get pointer to extension for a filename string (includes dot: '.png')
+RLAPI const char *GetFileName(const char *filePath);                // Get pointer to filename for a path string
+RLAPI const char *GetFileNameWithoutExt(const char *filePath);      // Get filename string without extension (uses static string)
+RLAPI const char *GetDirectoryPath(const char *filePath);           // Get full path for a given fileName with path (uses static string)
+RLAPI const char *GetPrevDirectoryPath(const char *dirPath);        // Get previous directory path for a given path (uses static string)
+RLAPI const char *GetWorkingDirectory(void);                        // Get current working directory (uses static string)
+RLAPI const char *GetApplicationDirectory(void);                    // Get the directory of the running application (uses static string)
+RLAPI int MakeDirectory(const char *dirPath);                       // Create directories (including full path requested), returns 0 on success
+RLAPI bool ChangeDirectory(const char *dirPath);                    // Change working directory, return true on success
+RLAPI bool IsPathFile(const char *path);                            // Check if a given path is a file or a directory
+RLAPI bool IsFileNameValid(const char *fileName);                   // Check if fileName is valid for the platform/OS
+RLAPI FilePathList LoadDirectoryFiles(const char *dirPath);         // Load directory filepaths
 RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and recursive directory scan. Use 'DIR' in the filter string to include directories in the result
-RLAPI void UnloadDirectoryFiles(FilePathList files);              // Unload filepaths
-RLAPI bool IsFileDropped(void);                                   // Check if a file has been dropped into window
-RLAPI FilePathList LoadDroppedFiles(void);                        // Load dropped filepaths
-RLAPI void UnloadDroppedFiles(FilePathList files);                // Unload dropped filepaths
-RLAPI long GetFileModTime(const char *fileName);                  // Get file modification time (last write time)
+RLAPI void UnloadDirectoryFiles(FilePathList files);                // Unload filepaths
+RLAPI bool IsFileDropped(void);                                     // Check if a file has been dropped into window
+RLAPI FilePathList LoadDroppedFiles(void);                          // Load dropped filepaths
+RLAPI void UnloadDroppedFiles(FilePathList files);                  // Unload dropped filepaths
 
 // Compression/Encoding functionality
 RLAPI unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize);        // Compress data (DEFLATE algorithm), memory must be MemFree()
 RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize);  // Decompress data (DEFLATE algorithm), memory must be MemFree()
-RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize);               // Encode data to Base64 string, memory must be MemFree()
-RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize);                    // Decode Base64 string data, memory must be MemFree()
-RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize);     // Compute CRC32 hash code
-RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize);      // Compute MD5 hash code, returns static int[4] (16 bytes)
-RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize);      // Compute SHA1 hash code, returns static int[5] (20 bytes)
-
+RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize);               // Encode data to Base64 string (includes NULL terminator), memory must be MemFree()
+RLAPI unsigned char *DecodeDataBase64(const char *text, int *outputSize);                             // Decode Base64 string (expected NULL terminated), memory must be MemFree()
+RLAPI unsigned int ComputeCRC32(unsigned char *data, int dataSize);       // Compute CRC32 hash code
+RLAPI unsigned int *ComputeMD5(unsigned char *data, int dataSize);        // Compute MD5 hash code, returns static int[4] (16 bytes)
+RLAPI unsigned int *ComputeSHA1(unsigned char *data, int dataSize);       // Compute SHA1 hash code, returns static int[5] (20 bytes)
+RLAPI unsigned int *ComputeSHA256(unsigned char *data, int dataSize);     // Compute SHA256 hash code, returns static int[8] (32 bytes)
 
 // Automation events functionality
-RLAPI AutomationEventList LoadAutomationEventList(const char *fileName);                // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
-RLAPI void UnloadAutomationEventList(AutomationEventList list);                         // Unload automation events list from file
-RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName);   // Export automation events list as text file
-RLAPI void SetAutomationEventList(AutomationEventList *list);                           // Set automation event list to record to
-RLAPI void SetAutomationEventBaseFrame(int frame);                                      // Set automation event internal base frame to start recording
-RLAPI void StartAutomationEventRecording(void);                                         // Start recording automation events (AutomationEventList must be set)
-RLAPI void StopAutomationEventRecording(void);                                          // Stop recording automation events
-RLAPI void PlayAutomationEvent(AutomationEvent event);                                  // Play a recorded automation event
+RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+RLAPI void UnloadAutomationEventList(AutomationEventList list);   // Unload automation events list from file
+RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
+RLAPI void SetAutomationEventList(AutomationEventList *list);     // Set automation event list to record to
+RLAPI void SetAutomationEventBaseFrame(int frame);                // Set automation event internal base frame to start recording
+RLAPI void StartAutomationEventRecording(void);                   // Start recording automation events (AutomationEventList must be set)
+RLAPI void StopAutomationEventRecording(void);                    // Stop recording automation events
+RLAPI void PlayAutomationEvent(AutomationEvent event);            // Play a recorded automation event
 
 //------------------------------------------------------------------------------------
 // Input Handling Functions (Module: core)
@@ -1165,25 +1179,26 @@
 
 // Input-related functions: keyboard
 RLAPI bool IsKeyPressed(int key);                             // Check if a key has been pressed once
-RLAPI bool IsKeyPressedRepeat(int key);                       // Check if a key has been pressed again (Only PLATFORM_DESKTOP)
+RLAPI bool IsKeyPressedRepeat(int key);                       // Check if a key has been pressed again
 RLAPI bool IsKeyDown(int key);                                // Check if a key is being pressed
 RLAPI bool IsKeyReleased(int key);                            // Check if a key has been released once
 RLAPI bool IsKeyUp(int key);                                  // Check if a key is NOT being pressed
 RLAPI int GetKeyPressed(void);                                // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty
 RLAPI int GetCharPressed(void);                               // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty
+RLAPI const char *GetKeyName(int key);                        // Get name of a QWERTY key on the current keyboard layout (eg returns string 'q' for KEY_A on an AZERTY keyboard)
 RLAPI void SetExitKey(int key);                               // Set a custom key to exit program (default is ESC)
 
 // Input-related functions: gamepads
-RLAPI bool IsGamepadAvailable(int gamepad);                                        // Check if a gamepad is available
-RLAPI const char *GetGamepadName(int gamepad);                                     // Get gamepad internal name id
-RLAPI bool IsGamepadButtonPressed(int gamepad, int button);                        // Check if a gamepad button has been pressed once
-RLAPI bool IsGamepadButtonDown(int gamepad, int button);                           // Check if a gamepad button is being pressed
-RLAPI bool IsGamepadButtonReleased(int gamepad, int button);                       // Check if a gamepad button has been released once
-RLAPI bool IsGamepadButtonUp(int gamepad, int button);                             // Check if a gamepad button is NOT being pressed
-RLAPI int GetGamepadButtonPressed(void);                                           // Get the last gamepad button pressed
-RLAPI int GetGamepadAxisCount(int gamepad);                                        // Get gamepad axis count for a gamepad
-RLAPI float GetGamepadAxisMovement(int gamepad, int axis);                         // Get axis movement value for a gamepad axis
-RLAPI int SetGamepadMappings(const char *mappings);                                // Set internal gamepad mappings (SDL_GameControllerDB)
+RLAPI bool IsGamepadAvailable(int gamepad);                   // Check if a gamepad is available
+RLAPI const char *GetGamepadName(int gamepad);                // Get gamepad internal name id
+RLAPI bool IsGamepadButtonPressed(int gamepad, int button);   // Check if a gamepad button has been pressed once
+RLAPI bool IsGamepadButtonDown(int gamepad, int button);      // Check if a gamepad button is being pressed
+RLAPI bool IsGamepadButtonReleased(int gamepad, int button);  // Check if a gamepad button has been released once
+RLAPI bool IsGamepadButtonUp(int gamepad, int button);        // Check if a gamepad button is NOT being pressed
+RLAPI int GetGamepadButtonPressed(void);                      // Get the last gamepad button pressed
+RLAPI int GetGamepadAxisCount(int gamepad);                   // Get axis count for a gamepad
+RLAPI float GetGamepadAxisMovement(int gamepad, int axis);    // Get movement value for a gamepad axis
+RLAPI int SetGamepadMappings(const char *mappings);           // Set internal gamepad mappings (SDL_GameControllerDB)
 RLAPI void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float duration); // Set gamepad vibration for both motors (duration in seconds)
 
 // Input-related functions: mouse
@@ -1212,19 +1227,19 @@
 //------------------------------------------------------------------------------------
 // Gestures and Touch Handling Functions (Module: rgestures)
 //------------------------------------------------------------------------------------
-RLAPI void SetGesturesEnabled(unsigned int flags);      // Enable a set of gestures using flags
-RLAPI bool IsGestureDetected(unsigned int gesture);     // Check if a gesture have been detected
-RLAPI int GetGestureDetected(void);                     // Get latest detected gesture
-RLAPI float GetGestureHoldDuration(void);               // Get gesture hold time in seconds
-RLAPI Vector2 GetGestureDragVector(void);               // Get gesture drag vector
-RLAPI float GetGestureDragAngle(void);                  // Get gesture drag angle
-RLAPI Vector2 GetGesturePinchVector(void);              // Get gesture pinch delta
-RLAPI float GetGesturePinchAngle(void);                 // Get gesture pinch angle
+RLAPI void SetGesturesEnabled(unsigned int flags);            // Enable a set of gestures using flags
+RLAPI bool IsGestureDetected(unsigned int gesture);           // Check if a gesture have been detected
+RLAPI int GetGestureDetected(void);                           // Get latest detected gesture
+RLAPI float GetGestureHoldDuration(void);                     // Get gesture hold time in seconds
+RLAPI Vector2 GetGestureDragVector(void);                     // Get gesture drag vector
+RLAPI float GetGestureDragAngle(void);                        // Get gesture drag angle
+RLAPI Vector2 GetGesturePinchVector(void);                    // Get gesture pinch delta
+RLAPI float GetGesturePinchAngle(void);                       // Get gesture pinch angle
 
 //------------------------------------------------------------------------------------
 // Camera System Functions (Module: rcamera)
 //------------------------------------------------------------------------------------
-RLAPI void UpdateCamera(Camera *camera, int mode);      // Update camera position for selected mode
+RLAPI void UpdateCamera(Camera *camera, int mode);            // Update camera position for selected mode
 RLAPI void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom); // Update camera movement/rotation
 
 //------------------------------------------------------------------------------------
@@ -1233,9 +1248,9 @@
 // Set texture and rectangle to be used on shapes drawing
 // NOTE: It can be useful when using basic shapes and one single font,
 // defining a font char white rectangle would allow drawing everything in a single draw call
-RLAPI void SetShapesTexture(Texture2D texture, Rectangle source);       // Set texture and rectangle to be used on shapes drawing
-RLAPI Texture2D GetShapesTexture(void);                                 // Get texture that is used for shapes drawing
-RLAPI Rectangle GetShapesTextureRectangle(void);                        // Get texture source rectangle that is used for shapes drawing
+RLAPI void SetShapesTexture(Texture2D texture, Rectangle source); // Set texture and rectangle to be used on shapes drawing
+RLAPI Texture2D GetShapesTexture(void);                 // Get texture that is used for shapes drawing
+RLAPI Rectangle GetShapesTextureRectangle(void);        // Get texture source rectangle that is used for shapes drawing
 
 // Basic shapes drawing functions
 RLAPI void DrawPixel(int posX, int posY, Color color);                                                   // Draw a pixel using geometry [Can be slow, use with care]
@@ -1245,6 +1260,7 @@
 RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color);                       // Draw a line (using triangles/quads)
 RLAPI void DrawLineStrip(const Vector2 *points, int pointCount, Color color);                            // Draw lines sequence (using gl lines)
 RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color);                   // Draw line segment cubic-bezier in-out interpolation
+RLAPI void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int spaceSize, Color color);   // Draw a dashed line
 RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color);                              // Draw a color-filled circle
 RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color);      // Draw a piece of a circle
 RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw circle sector outline
@@ -1253,7 +1269,9 @@
 RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color);                         // Draw circle outline
 RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color);                                  // Draw circle outline (Vector version)
 RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color);             // Draw ellipse
+RLAPI void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color);                      // Draw ellipse (Vector version)
 RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color);        // Draw ellipse outline
+RLAPI void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color);                 // Draw ellipse outline (Vector version)
 RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring
 RLAPI void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color);    // Draw ring outline
 RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color);                        // Draw a color-filled rectangle
@@ -1262,7 +1280,7 @@
 RLAPI void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color);                 // Draw a color-filled rectangle with pro parameters
 RLAPI void DrawRectangleGradientV(int posX, int posY, int width, int height, Color top, Color bottom);   // Draw a vertical-gradient-filled rectangle
 RLAPI void DrawRectangleGradientH(int posX, int posY, int width, int height, Color left, Color right);   // Draw a horizontal-gradient-filled rectangle
-RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight); // Draw a gradient-filled rectangle with custom vertex colors
+RLAPI void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight); // Draw a gradient-filled rectangle with custom vertex colors
 RLAPI void DrawRectangleLines(int posX, int posY, int width, int height, Color color);                   // Draw rectangle outline
 RLAPI void DrawRectangleLinesEx(Rectangle rec, float lineThick, Color color);                            // Draw rectangle outline with extended parameters
 RLAPI void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color);              // Draw rectangle with rounded edges
@@ -1277,11 +1295,11 @@
 RLAPI void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color); // Draw a polygon outline of n sides with extended parameters
 
 // Splines drawing functions
-RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color);                  // Draw spline: Linear, minimum 2 points
-RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color);                   // Draw spline: B-Spline, minimum 4 points
-RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color);              // Draw spline: Catmull-Rom, minimum 4 points
-RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color);         // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
-RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
+RLAPI void DrawSplineLinear(const Vector2 *points, int pointCount, float thick, Color color);            // Draw spline: Linear, minimum 2 points
+RLAPI void DrawSplineBasis(const Vector2 *points, int pointCount, float thick, Color color);             // Draw spline: B-Spline, minimum 4 points
+RLAPI void DrawSplineCatmullRom(const Vector2 *points, int pointCount, float thick, Color color);        // Draw spline: Catmull-Rom, minimum 4 points
+RLAPI void DrawSplineBezierQuadratic(const Vector2 *points, int pointCount, float thick, Color color);   // Draw spline: Quadratic Bezier, minimum 3 points (1 control point): [p1, c2, p3, c4...]
+RLAPI void DrawSplineBezierCubic(const Vector2 *points, int pointCount, float thick, Color color);       // Draw spline: Cubic Bezier, minimum 4 points (2 control points): [p1, c2, c3, p4, c5, c6...]
 RLAPI void DrawSplineSegmentLinear(Vector2 p1, Vector2 p2, float thick, Color color);                    // Draw spline segment: Linear, 2 points
 RLAPI void DrawSplineSegmentBasis(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: B-Spline, 4 points
 RLAPI void DrawSplineSegmentCatmullRom(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, float thick, Color color); // Draw spline segment: Catmull-Rom, 4 points
@@ -1299,13 +1317,13 @@
 RLAPI bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2);                                           // Check collision between two rectangles
 RLAPI bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, float radius2);        // Check collision between two circles
 RLAPI bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec);                         // Check collision between circle and rectangle
+RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2);               // Check if circle collides with a line created betweeen two points [p1] and [p2]
 RLAPI bool CheckCollisionPointRec(Vector2 point, Rectangle rec);                                         // Check if point is inside rectangle
 RLAPI bool CheckCollisionPointCircle(Vector2 point, Vector2 center, float radius);                       // Check if point is inside circle
 RLAPI bool CheckCollisionPointTriangle(Vector2 point, Vector2 p1, Vector2 p2, Vector2 p3);               // Check if point is inside a triangle
+RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold);                // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
 RLAPI bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount);                // Check if point is within a polygon described by array of vertices
 RLAPI bool CheckCollisionLines(Vector2 startPos1, Vector2 endPos1, Vector2 startPos2, Vector2 endPos2, Vector2 *collisionPoint); // Check the collision between two lines defined by two points each, returns collision point by reference
-RLAPI bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshold);                // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
-RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2);               // Check if circle collides with a line created betweeen two points [p1] and [p2]
 RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2);                                         // Get collision rectangle for two rectangles collision
 
 //------------------------------------------------------------------------------------
@@ -1354,7 +1372,7 @@
 RLAPI void ImageBlurGaussian(Image *image, int blurSize);                                                // Apply Gaussian blur using a box blur approximation
 RLAPI void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize);                    // Apply custom square convolution kernel to image
 RLAPI void ImageResize(Image *image, int newWidth, int newHeight);                                       // Resize image (Bicubic scaling algorithm)
-RLAPI void ImageResizeNN(Image *image, int newWidth,int newHeight);                                      // Resize image (Nearest-Neighbor scaling algorithm)
+RLAPI void ImageResizeNN(Image *image, int newWidth, int newHeight);                                     // Resize image (Nearest-Neighbor scaling algorithm)
 RLAPI void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill); // Resize canvas and fill with color
 RLAPI void ImageMipmaps(Image *image);                                                                   // Compute all mipmap levels for a provided image
 RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp);                            // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
@@ -1395,8 +1413,8 @@
 RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);               // Draw triangle within an image
 RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
 RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color);          // Draw triangle outline within an image
-RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color);               // Draw a triangle fan defined by points within an image (first vertex is the center)
-RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color);             // Draw a triangle strip defined by points within an image
+RLAPI void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color);               // Draw a triangle fan defined by points within an image (first vertex is the center)
+RLAPI void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color);             // Draw a triangle strip defined by points within an image
 RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint);             // Draw a source image within a destination image (tint applied to source)
 RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color);   // Draw text (using default font) within an image (destination)
 RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
@@ -1411,8 +1429,8 @@
 RLAPI void UnloadTexture(Texture2D texture);                                                             // Unload texture from GPU memory (VRAM)
 RLAPI bool IsRenderTextureValid(RenderTexture2D target);                                                 // Check if a render texture is valid (loaded in GPU)
 RLAPI void UnloadRenderTexture(RenderTexture2D target);                                                  // Unload render texture from GPU memory (VRAM)
-RLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data
-RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels);                       // Update GPU texture rectangle with new data
+RLAPI void UpdateTexture(Texture2D texture, const void *pixels);                                         // Update GPU texture with new data (pixels should be able to fill texture)
+RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels);                       // Update GPU texture rectangle with new data (pixels and rec should fit in texture)
 
 // Texture configuration functions
 RLAPI void GenTextureMipmaps(Texture2D *texture);                                                        // Generate GPU mipmaps for a texture
@@ -1453,11 +1471,11 @@
 // Font loading/unloading functions
 RLAPI Font GetFontDefault(void);                                                            // Get the default Font
 RLAPI Font LoadFont(const char *fileName);                                                  // Load font from file into GPU memory (VRAM)
-RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height
+RLAPI Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height
 RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar);                        // Load font from Image (XNA style)
-RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
+RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf'
 RLAPI bool IsFontValid(Font font);                                                          // Check if a font is valid (font data loaded, WARNING: GPU texture not checked)
-RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use
+RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount, int type, int *glyphCount); // Load font data for further use
 RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info
 RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount);                               // Unload font chars info data (RAM)
 RLAPI void UnloadFont(Font font);                                                           // Unload font from GPU memory (VRAM)
@@ -1480,37 +1498,42 @@
 RLAPI Rectangle GetGlyphAtlasRec(Font font, int codepoint);                                 // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
 
 // Text codepoints management functions (unicode characters)
-RLAPI char *LoadUTF8(const int *codepoints, int length);                // Load UTF-8 text encoded from codepoints array
-RLAPI void UnloadUTF8(char *text);                                      // Unload UTF-8 text encoded from codepoints array
-RLAPI int *LoadCodepoints(const char *text, int *count);                // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
-RLAPI void UnloadCodepoints(int *codepoints);                           // Unload codepoints data from memory
-RLAPI int GetCodepointCount(const char *text);                          // Get total number of codepoints in a UTF-8 encoded string
-RLAPI int GetCodepoint(const char *text, int *codepointSize);           // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
-RLAPI int GetCodepointNext(const char *text, int *codepointSize);       // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
-RLAPI int GetCodepointPrevious(const char *text, int *codepointSize);   // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
-RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size);        // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
+RLAPI char *LoadUTF8(const int *codepoints, int length);                                    // Load UTF-8 text encoded from codepoints array
+RLAPI void UnloadUTF8(char *text);                                                          // Unload UTF-8 text encoded from codepoints array
+RLAPI int *LoadCodepoints(const char *text, int *count);                                    // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
+RLAPI void UnloadCodepoints(int *codepoints);                                               // Unload codepoints data from memory
+RLAPI int GetCodepointCount(const char *text);                                              // Get total number of codepoints in a UTF-8 encoded string
+RLAPI int GetCodepoint(const char *text, int *codepointSize);                               // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI int GetCodepointNext(const char *text, int *codepointSize);                           // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI int GetCodepointPrevious(const char *text, int *codepointSize);                       // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+RLAPI const char *CodepointToUTF8(int codepoint, int *utf8Size);                            // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
 
 // Text strings management functions (no UTF-8 strings, only byte chars)
-// NOTE: Some strings allocate memory internally for returned strings, just be careful!
+// WARNING 1: Most of these functions use internal static buffers[], it's recommended to store returned data on user-side for re-use
+// WARNING 2: Some strings allocate memory internally for the returned strings, those strings must be free by user using MemFree()
+RLAPI char **LoadTextLines(const char *text, int *count);                                   // Load text as separate lines ('\n')
+RLAPI void UnloadTextLines(char **text, int lineCount);                                     // Unload text lines
 RLAPI int TextCopy(char *dst, const char *src);                                             // Copy one string to another, returns bytes copied
 RLAPI bool TextIsEqual(const char *text1, const char *text2);                               // Check if two text string are equal
 RLAPI unsigned int TextLength(const char *text);                                            // Get text length, checks for '\0' ending
 RLAPI const char *TextFormat(const char *text, ...);                                        // Text formatting with variables (sprintf() style)
 RLAPI const char *TextSubtext(const char *text, int position, int length);                  // Get a piece of a text string
-RLAPI char *TextReplace(const char *text, const char *replace, const char *by);             // Replace text string (WARNING: memory must be freed!)
+RLAPI const char *TextRemoveSpaces(const char *text);                                       // Remove text spaces, concat words
+RLAPI char *GetTextBetween(const char *text, const char *begin, const char *end);           // Get text between two strings
+RLAPI char *TextReplace(const char *text, const char *search, const char *replacement);     // Replace text string (WARNING: memory must be freed!)
+RLAPI char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement); // Replace text between two specific strings (WARNING: memory must be freed!)
 RLAPI char *TextInsert(const char *text, const char *insert, int position);                 // Insert text in a position (WARNING: memory must be freed!)
-RLAPI const char *TextJoin(const char **textList, int count, const char *delimiter);        // Join text strings with delimiter
-RLAPI const char **TextSplit(const char *text, char delimiter, int *count);                 // Split text into multiple strings
-RLAPI void TextAppend(char *text, const char *append, int *position);                       // Append text at specific position and move cursor!
-RLAPI int TextFindIndex(const char *text, const char *find);                                // Find first text occurrence within a string
-RLAPI const char *TextToUpper(const char *text);                      // Get upper case version of provided string
-RLAPI const char *TextToLower(const char *text);                      // Get lower case version of provided string
-RLAPI const char *TextToPascal(const char *text);                     // Get Pascal case notation version of provided string
-RLAPI const char *TextToSnake(const char *text);                      // Get Snake case notation version of provided string
-RLAPI const char *TextToCamel(const char *text);                      // Get Camel case notation version of provided string
-
-RLAPI int TextToInteger(const char *text);                            // Get integer value from text (negative values not supported)
-RLAPI float TextToFloat(const char *text);                            // Get float value from text (negative values not supported)
+RLAPI char *TextJoin(char **textList, int count, const char *delimiter);                    // Join text strings with delimiter
+RLAPI char **TextSplit(const char *text, char delimiter, int *count);                       // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings
+RLAPI void TextAppend(char *text, const char *append, int *position);                       // Append text at specific position and move cursor
+RLAPI int TextFindIndex(const char *text, const char *search);                              // Find first text occurrence within a string, -1 if not found
+RLAPI char *TextToUpper(const char *text);                                                  // Get upper case version of provided string
+RLAPI char *TextToLower(const char *text);                                                  // Get lower case version of provided string
+RLAPI char *TextToPascal(const char *text);                                                 // Get Pascal case notation version of provided string
+RLAPI char *TextToSnake(const char *text);                                                  // Get Snake case notation version of provided string
+RLAPI char *TextToCamel(const char *text);                                                  // Get Camel case notation version of provided string
+RLAPI int TextToInteger(const char *text);                                                  // Get integer value from text
+RLAPI float TextToFloat(const char *text);                                                  // Get float value from text
 
 //------------------------------------------------------------------------------------
 // Basic 3d Shapes Drawing Functions (Module: models)
@@ -1596,21 +1619,21 @@
 
 // Model animations loading/unloading functions
 RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount);            // Load model animations from file
-RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);               // Update model animation pose
+RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame);               // Update model animation pose (CPU)
+RLAPI void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame);          // Update model animation mesh bone matrices (GPU skinning)
 RLAPI void UnloadModelAnimation(ModelAnimation anim);                                       // Unload animation data
 RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount);                // Unload animation array data
 RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim);                         // Check model animation skeleton match
-RLAPI void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame);   // Update model animation mesh bone matrices (Note GPU skinning does not work on Mac)
 
 // Collision detection functions
-RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2);   // Check collision between two spheres
-RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);                                 // Check collision between two bounding boxes
-RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius);                  // Check collision between box and sphere
-RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius);                    // Get collision info between ray and sphere
-RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box);                                    // Get collision info between ray and box
-RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform);                       // Get collision info between ray and mesh
-RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3);            // Get collision info between ray and triangle
-RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4);    // Get collision info between ray and quad
+RLAPI bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2); // Check collision between two spheres
+RLAPI bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2);                         // Check collision between two bounding boxes
+RLAPI bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius);          // Check collision between box and sphere
+RLAPI RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius);            // Get collision info between ray and sphere
+RLAPI RayCollision GetRayCollisionBox(Ray ray, BoundingBox box);                            // Get collision info between ray and box
+RLAPI RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform);               // Get collision info between ray and mesh
+RLAPI RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3);    // Get collision info between ray and triangle
+RLAPI RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4); // Get collision info between ray and quad
 
 //------------------------------------------------------------------------------------
 // Audio Loading and Playing Functions (Module: audio)
@@ -1632,7 +1655,7 @@
 RLAPI Sound LoadSoundFromWave(Wave wave);                             // Load sound from wave data
 RLAPI Sound LoadSoundAlias(Sound source);                             // Create a new sound that shares the same sample data as the source sound, does not own the sound data
 RLAPI bool IsSoundValid(Sound sound);                                 // Checks if a sound is valid (data loaded and buffers initialized)
-RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data
+RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data (default data format: 32 bit float, stereo)
 RLAPI void UnloadWave(Wave wave);                                     // Unload wave data
 RLAPI void UnloadSound(Sound sound);                                  // Unload sound
 RLAPI void UnloadSoundAlias(Sound alias);                             // Unload a sound alias (does not deallocate sample data)
@@ -1647,7 +1670,7 @@
 RLAPI bool IsSoundPlaying(Sound sound);                               // Check if a sound is currently playing
 RLAPI void SetSoundVolume(Sound sound, float volume);                 // Set volume for a sound (1.0 is max level)
 RLAPI void SetSoundPitch(Sound sound, float pitch);                   // Set pitch for a sound (1.0 is base level)
-RLAPI void SetSoundPan(Sound sound, float pan);                       // Set pan for a sound (0.5 is center)
+RLAPI void SetSoundPan(Sound sound, float pan);                       // Set pan for a sound (-1.0 left, 0.0 center, 1.0 right)
 RLAPI Wave WaveCopy(Wave wave);                                       // Copy a wave to a new wave
 RLAPI void WaveCrop(Wave *wave, int initFrame, int finalFrame);       // Crop a wave to defined frames range
 RLAPI void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels); // Convert wave data to desired format
@@ -1668,7 +1691,7 @@
 RLAPI void SeekMusicStream(Music music, float position);              // Seek music to a position (in seconds)
 RLAPI void SetMusicVolume(Music music, float volume);                 // Set volume for music (1.0 is max level)
 RLAPI void SetMusicPitch(Music music, float pitch);                   // Set pitch for a music (1.0 is base level)
-RLAPI void SetMusicPan(Music music, float pan);                       // Set pan for a music (0.5 is center)
+RLAPI void SetMusicPan(Music music, float pan);                       // Set pan for a music (-1.0 left, 0.0 center, 1.0 right)
 RLAPI float GetMusicTimeLength(Music music);                          // Get music time length (in seconds)
 RLAPI float GetMusicTimePlayed(Music music);                          // Get current music time played (in seconds)
 
@@ -1689,10 +1712,10 @@
 RLAPI void SetAudioStreamBufferSizeDefault(int size);                 // Default size for new audio streams
 RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data
 
-RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as 'float'
+RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives frames x 2 samples as 'float' (stereo)
 RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream
 
-RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as 'float'
+RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives frames x 2 samples as 'float' (stereo)
 RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline
 
 #if defined(__cplusplus)
diff --git a/raylib/src/raymath.h b/raylib/src/raymath.h
--- a/raylib/src/raymath.h
+++ b/raylib/src/raymath.h
@@ -19,20 +19,25 @@
 *
 *   CONFIGURATION:
 *       #define RAYMATH_IMPLEMENTATION
-*           Generates the implementation of the library into the included file.
+*           Generates the implementation of the library into the included file
 *           If not defined, the library is in header only mode and can be included in other headers
-*           or source files without problems. But only ONE file should hold the implementation.
+*           or source files without problems. But only ONE file should hold the implementation
 *
 *       #define RAYMATH_STATIC_INLINE
-*           Define static inline functions code, so #include header suffices for use.
-*           This may use up lots of memory.
+*           Define static inline functions code, so #include header suffices for use
+*           This may use up lots of memory
 *
 *       #define RAYMATH_DISABLE_CPP_OPERATORS
 *           Disables C++ operator overloads for raymath types.
 *
+*       #define RAYMATH_USE_SIMD_INTRINSICS
+*           Try to enable SIMD intrinsics for MatrixMultiply()
+*           Note that users enabling it must be aware of the target platform where application will
+*           run to support the selected SIMD intrinsic, for now, only SSE is supported
+*
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2015-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2015-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -79,7 +84,6 @@
     #endif
 #endif
 
-
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
@@ -170,6 +174,35 @@
 
 #include <math.h>       // Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabsf()
 
+#if defined(RAYMATH_USE_SIMD_INTRINSICS)
+    // SIMD is used on the most costly raymath function MatrixMultiply()
+    // NOTE: Only SSE intrinsics support implemented
+    // TODO: Consider support for other SIMD instrinsics:
+    //  - SSEx, AVX, AVX2, FMA, NEON, RVV
+    /*
+    #if defined(__SSE4_2__)
+        #include <nmmintrin.h>
+        #define RAYMATH_SSE42_ENABLED
+    #elif defined(__SSE4_1__)
+        #include <smmintrin.h>
+        #define RAYMATH_SSE41_ENABLED
+    #elif defined(__SSSE3__)
+        #include <tmmintrin.h>
+        #define RAYMATH_SSSE3_ENABLED
+    #elif defined(__SSE3__)
+        #include <pmmintrin.h>
+        #define RAYMATH_SSE3_ENABLED
+    #elif defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64)) // SSE2 x64
+        #include <emmintrin.h>
+        #define RAYMATH_SSE2_ENABLED
+    #endif
+    */
+    #if defined(__SSE__) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))
+        #include <xmmintrin.h>
+        #define RAYMATH_SSE_ENABLED
+    #endif
+#endif
+
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Utils math
 //----------------------------------------------------------------------------------
@@ -304,6 +337,14 @@
     return result;
 }
 
+// Calculate two vectors cross product
+RMAPI float Vector2CrossProduct(Vector2 v1, Vector2 v2)
+{
+    float result = (v1.x*v2.y - v1.y*v2.x);
+
+    return result;
+}
+
 // Calculate distance between two vectors
 RMAPI float Vector2Distance(Vector2 v1, Vector2 v2)
 {
@@ -320,8 +361,9 @@
     return result;
 }
 
-// Calculate angle between two vectors
-// NOTE: Angle is calculated from origin point (0, 0)
+// Calculate the signed angle from v1 to v2, relative to the origin (0, 0)
+// NOTE: Coordinate system convention: positive X right, positive Y down
+// positive angles appear clockwise, and negative angles appear counterclockwise
 RMAPI float Vector2Angle(Vector2 v1, Vector2 v2)
 {
     float result = 0.0f;
@@ -1459,20 +1501,36 @@
 RMAPI float MatrixDeterminant(Matrix mat)
 {
     float result = 0.0f;
-
+/*
     // Cache the matrix values (speed optimization)
     float a00 = mat.m0, a01 = mat.m1, a02 = mat.m2, a03 = mat.m3;
     float a10 = mat.m4, a11 = mat.m5, a12 = mat.m6, a13 = mat.m7;
     float a20 = mat.m8, a21 = mat.m9, a22 = mat.m10, a23 = mat.m11;
     float a30 = mat.m12, a31 = mat.m13, a32 = mat.m14, a33 = mat.m15;
 
+    // NOTE: It takes 72 multiplication to calculate 4x4 matrix determinant
     result = a30*a21*a12*a03 - a20*a31*a12*a03 - a30*a11*a22*a03 + a10*a31*a22*a03 +
              a20*a11*a32*a03 - a10*a21*a32*a03 - a30*a21*a02*a13 + a20*a31*a02*a13 +
              a30*a01*a22*a13 - a00*a31*a22*a13 - a20*a01*a32*a13 + a00*a21*a32*a13 +
              a30*a11*a02*a23 - a10*a31*a02*a23 - a30*a01*a12*a23 + a00*a31*a12*a23 +
              a10*a01*a32*a23 - a00*a11*a32*a23 - a20*a11*a02*a33 + a10*a21*a02*a33 +
              a20*a01*a12*a33 - a00*a21*a12*a33 - a10*a01*a22*a33 + a00*a11*a22*a33;
+*/
+    // Using Laplace expansion (https://en.wikipedia.org/wiki/Laplace_expansion),
+    // previous operation can be simplified to 40 multiplications, decreasing matrix 
+    // size from 4x4 to 2x2 using minors
 
+    // Cache the matrix values (speed optimization)
+    float m0 = mat.m0, m1 = mat.m1, m2 = mat.m2, m3 = mat.m3;
+    float m4 = mat.m4, m5 = mat.m5, m6 = mat.m6, m7 = mat.m7;
+    float m8 = mat.m8, m9 = mat.m9, m10 = mat.m10, m11 = mat.m11;
+    float m12 = mat.m12, m13 = mat.m13, m14 = mat.m14, m15 = mat.m15;
+
+    result = (m0*((m5*(m10*m15 - m11*m14) - m9*(m6*m15 - m7*m14) + m13*(m6*m11 - m7*m10))) -
+        m4*((m1*(m10*m15 - m11*m14) - m9*(m2*m15 - m3*m14) + m13*(m2*m11 - m3*m10))) +
+        m8*((m1*(m6*m15 - m7*m14) - m5*(m2*m15 - m3*m14) + m13*(m2*m7 - m3*m6))) -
+        m12*((m1*(m6*m11 - m7*m10) - m5*(m2*m11 - m3*m10) + m9*(m2*m7 - m3*m6))));
+
     return result;
 }
 
@@ -1622,7 +1680,64 @@
 RMAPI Matrix MatrixMultiply(Matrix left, Matrix right)
 {
     Matrix result = { 0 };
+    
+#if defined(RAYMATH_SSE_ENABLED)
+    // Load left side and right side
+    __m128 c0 = _mm_set_ps(right.m12, right.m8,  right.m4,  right.m0);
+    __m128 c1 = _mm_set_ps(right.m13, right.m9,  right.m5,  right.m1);
+    __m128 c2 = _mm_set_ps(right.m14, right.m10, right.m6,  right.m2);
+    __m128 c3 = _mm_set_ps(right.m15, right.m11, right.m7,  right.m3);
+    
+    // Transpose so c0..c3 become *rows* of the right matrix in semantic order
+    _MM_TRANSPOSE4_PS(c0, c1, c2, c3);
 
+    float tmp[4] = { 0 };
+    __m128 row;
+    
+    // Row 0 of result: [m0, m1, m2, m3]
+    row  = _mm_mul_ps(_mm_set1_ps(left.m0),  c0);
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m1),  c1));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m2),  c2));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m3),  c3));
+    _mm_storeu_ps(tmp, row);
+    result.m0 = tmp[0];
+    result.m1 = tmp[1];
+    result.m2 = tmp[2];
+    result.m3 = tmp[3];
+
+    // Row 1 of result: [m4, m5, m6, m7]
+    row  = _mm_mul_ps(_mm_set1_ps(left.m4),  c0);
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m5),  c1));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m6),  c2));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m7),  c3));
+    _mm_storeu_ps(tmp, row);
+    result.m4 = tmp[0];
+    result.m5 = tmp[1];
+    result.m6 = tmp[2];
+    result.m7 = tmp[3];
+
+    // Row 2 of result: [m8, m9, m10, m11]
+    row  = _mm_mul_ps(_mm_set1_ps(left.m8),  c0);
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m9),  c1));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m10), c2));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m11), c3));
+    _mm_storeu_ps(tmp, row);
+    result.m8  = tmp[0];
+    result.m9  = tmp[1];
+    result.m10 = tmp[2];
+    result.m11 = tmp[3];
+
+    // Row 3 of result: [m12, m13, m14, m15]
+    row  = _mm_mul_ps(_mm_set1_ps(left.m12), c0);
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m13), c1));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m14), c2));
+    row  = _mm_add_ps(row, _mm_mul_ps(_mm_set1_ps(left.m15), c3));
+    _mm_storeu_ps(tmp, row);
+    result.m12 = tmp[0];
+    result.m13 = tmp[1];
+    result.m14 = tmp[2];
+    result.m15 = tmp[3];
+#else
     result.m0 = left.m0*right.m0 + left.m1*right.m4 + left.m2*right.m8 + left.m3*right.m12;
     result.m1 = left.m0*right.m1 + left.m1*right.m5 + left.m2*right.m9 + left.m3*right.m13;
     result.m2 = left.m0*right.m2 + left.m1*right.m6 + left.m2*right.m10 + left.m3*right.m14;
@@ -1639,6 +1754,7 @@
     result.m13 = left.m12*right.m1 + left.m13*right.m5 + left.m14*right.m9 + left.m15*right.m13;
     result.m14 = left.m12*right.m2 + left.m13*right.m6 + left.m14*right.m10 + left.m15*right.m14;
     result.m15 = left.m12*right.m3 + left.m13*right.m7 + left.m14*right.m11 + left.m15*right.m15;
+#endif
 
     return result;
 }
@@ -2527,59 +2643,119 @@
     return result;
 }
 
-// Decompose a transformation matrix into its rotational, translational and scaling components
+// Compose a transformation matrix from rotational, translational and scaling components
+// TODO: This function is not following raymath conventions defined in header: NOT self-contained
+RMAPI Matrix MatrixCompose(Vector3 translation, Quaternion rotation, Vector3 scale)
+{
+    // Initialize vectors
+    Vector3 right = { 1.0f, 0.0f, 0.0f };
+    Vector3 up = { 0.0f, 1.0f, 0.0f };
+    Vector3 forward = { 0.0f, 0.0f, 1.0f };
+
+    // Scale vectors
+    right = Vector3Scale(right, scale.x);
+    up = Vector3Scale(up, scale.y);
+    forward = Vector3Scale(forward , scale.z);
+
+    // Rotate vectors
+    right = Vector3RotateByQuaternion(right, rotation);
+    up = Vector3RotateByQuaternion(up, rotation);
+    forward = Vector3RotateByQuaternion(forward, rotation);
+    
+    // Set result matrix output
+	Matrix result = {
+		right.x, up.x, forward.x, translation.x,
+		right.y, up.y, forward.y, translation.y,
+		right.z, up.z, forward.z, translation.z,
+		0.0f, 0.0f, 0.0f, 1.0f
+	};
+
+	return result;
+}
+
+// Decompose a transformation matrix into its rotational, translational and scaling components and remove shear
+// TODO: This function is not following raymath conventions defined in header: NOT self-contained
 RMAPI void MatrixDecompose(Matrix mat, Vector3 *translation, Quaternion *rotation, Vector3 *scale)
 {
-    // Extract translation.
+    float eps = (float)1e-9;
+
+    // Extract Translation
     translation->x = mat.m12;
     translation->y = mat.m13;
     translation->z = mat.m14;
 
-    // Extract upper-left for determinant computation
-    const float a = mat.m0;
-    const float b = mat.m4;
-    const float c = mat.m8;
-    const float d = mat.m1;
-    const float e = mat.m5;
-    const float f = mat.m9;
-    const float g = mat.m2;
-    const float h = mat.m6;
-    const float i = mat.m10;
-    const float A = e*i - f*h;
-    const float B = f*g - d*i;
-    const float C = d*h - e*g;
+    // Matrix Columns - Rotation will be extracted into here.
+    Vector3 matColumns[3] = { { mat.m0, mat.m4, mat.m8 },
+                             { mat.m1, mat.m5, mat.m9 },
+                             { mat.m2, mat.m6, mat.m10 } };
 
-    // Extract scale
-    const float det = a*A + b*B + c*C;
-    Vector3 abc = { a, b, c };
-    Vector3 def = { d, e, f };
-    Vector3 ghi = { g, h, i };
+    // Shear Parameters XY, XZ, and YZ (extract and ignored)
+    float shear[3] = { 0 };
 
-    float scalex = Vector3Length(abc);
-    float scaley = Vector3Length(def);
-    float scalez = Vector3Length(ghi);
-    Vector3 s = { scalex, scaley, scalez };
+    // Normalized Scale Parameters
+    Vector3 scl = { 0 };
 
-    if (det < 0) s = Vector3Negate(s);
+    // Max-Normalizing helps numerical stability
+    float stabilizer = eps;
+    for (int i = 0; i < 3; i++)
+    {
+        stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].x));
+        stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].y));
+        stabilizer = fmaxf(stabilizer, fabsf(matColumns[i].z));
+    };
+    matColumns[0] = Vector3Scale(matColumns[0], 1.0f / stabilizer);
+    matColumns[1] = Vector3Scale(matColumns[1], 1.0f / stabilizer);
+    matColumns[2] = Vector3Scale(matColumns[2], 1.0f / stabilizer);
 
-    *scale = s;
+    // X Scale
+    scl.x = Vector3Length(matColumns[0]);
+    if (scl.x > eps) matColumns[0] = Vector3Scale(matColumns[0], 1.0f / scl.x);
 
-    // Remove scale from the matrix if it is not close to zero
-    Matrix clone = mat;
-    if (!FloatEquals(det, 0))
+    // Compute XY shear and make col2 orthogonal
+    shear[0] = Vector3DotProduct(matColumns[0], matColumns[1]);
+    matColumns[1] = Vector3Subtract(matColumns[1], Vector3Scale(matColumns[0], shear[0]));
+
+    // Y Scale
+    scl.y = Vector3Length(matColumns[1]);
+    if (scl.y > eps)
     {
-        clone.m0 /= s.x;
-        clone.m5 /= s.y;
-        clone.m10 /= s.z;
+        matColumns[1] = Vector3Scale(matColumns[1], 1.0f / scl.y);
+        shear[0] /= scl.y; // Correct XY shear
+    }
 
-        // Extract rotation
-        *rotation = QuaternionFromMatrix(clone);
+    // Compute XZ and YZ shears and make col3 orthogonal
+    shear[1] = Vector3DotProduct(matColumns[0], matColumns[2]);
+    matColumns[2] = Vector3Subtract(matColumns[2], Vector3Scale(matColumns[0], shear[1]));
+    shear[2] = Vector3DotProduct(matColumns[1], matColumns[2]);
+    matColumns[2] = Vector3Subtract(matColumns[2], Vector3Scale(matColumns[1], shear[2]));
+
+    // Z Scale
+    scl.z = Vector3Length(matColumns[2]);
+    if (scl.z > eps)
+    {
+        matColumns[2] = Vector3Scale(matColumns[2], 1.0f / scl.z);
+        shear[1] /= scl.z; // Correct XZ shear
+        shear[2] /= scl.z; // Correct YZ shear
     }
-    else
+
+    // matColumns are now orthonormal in O(3). Now ensure its in SO(3) by enforcing det = 1.
+    if (Vector3DotProduct(matColumns[0], Vector3CrossProduct(matColumns[1], matColumns[2])) < 0)
     {
-        // Set to identity if close to zero
-        *rotation = QuaternionIdentity();
+        scl = Vector3Negate(scl);
+        matColumns[0] = Vector3Negate(matColumns[0]);
+        matColumns[1] = Vector3Negate(matColumns[1]);
+        matColumns[2] = Vector3Negate(matColumns[2]);
     }
+
+    // Set Scale
+    *scale = Vector3Scale(scl, stabilizer);
+
+    // Extract Rotation
+    Matrix rotationMatrix = { matColumns[0].x, matColumns[0].y, matColumns[0].z, 0,
+                             matColumns[1].x, matColumns[1].y, matColumns[1].z, 0,
+                             matColumns[2].x, matColumns[2].y, matColumns[2].z, 0,
+                             0, 0, 0, 1 };
+    *rotation = QuaternionFromMatrix(rotationMatrix);
 }
 
 #if defined(__cplusplus) && !defined(RAYMATH_DISABLE_CPP_OPERATORS)
@@ -2642,7 +2818,7 @@
     return Vector2Transform(lhs, rhs);
 }
 
-inline const Vector2& operator -= (Vector2& lhs, const Matrix& rhs)
+inline const Vector2& operator *= (Vector2& lhs, const Matrix& rhs)
 {
     lhs = Vector2Transform(lhs, rhs);
     return lhs;
@@ -2650,12 +2826,12 @@
 
 inline Vector2 operator / (const Vector2& lhs, const float& rhs)
 {
-    return Vector2Scale(lhs, 1.0f / rhs);
+    return Vector2Scale(lhs, 1.0f/rhs);
 }
 
 inline const Vector2& operator /= (Vector2& lhs, const float& rhs)
 {
-    lhs = Vector2Scale(lhs, rhs);
+    lhs = Vector2Scale(lhs, 1.0f/rhs);
     return lhs;
 }
 
@@ -2736,7 +2912,7 @@
     return Vector3Transform(lhs, rhs);
 }
 
-inline const Vector3& operator -= (Vector3& lhs, const Matrix& rhs)
+inline const Vector3& operator *= (Vector3& lhs, const Matrix& rhs)
 {
     lhs = Vector3Transform(lhs, rhs);
     return lhs;
@@ -2744,12 +2920,12 @@
 
 inline Vector3 operator / (const Vector3& lhs, const float& rhs)
 {
-    return Vector3Scale(lhs, 1.0f / rhs);
+    return Vector3Scale(lhs, 1.0f/rhs);
 }
 
 inline const Vector3& operator /= (Vector3& lhs, const float& rhs)
 {
-    lhs = Vector3Scale(lhs, rhs);
+    lhs = Vector3Scale(lhs, 1.0f/rhs);
     return lhs;
 }
 
@@ -2828,12 +3004,12 @@
 
 inline Vector4 operator / (const Vector4& lhs, const float& rhs)
 {
-    return Vector4Scale(lhs, 1.0f / rhs);
+    return Vector4Scale(lhs, 1.0f/rhs);
 }
 
 inline const Vector4& operator /= (Vector4& lhs, const float& rhs)
 {
-    lhs = Vector4Scale(lhs, rhs);
+    lhs = Vector4Scale(lhs, 1.0f/rhs);
     return lhs;
 }
 
diff --git a/raylib/src/rcamera.h b/raylib/src/rcamera.h
--- a/raylib/src/rcamera.h
+++ b/raylib/src/rcamera.h
@@ -20,7 +20,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2022-2024 Christoph Wagner (@Crydsch) & Ramon Santamaria (@raysan5)
+*   Copyright (c) 2022-2026 Christoph Wagner (@Crydsch) and Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -50,14 +50,14 @@
 // Function specifiers in case library is build/used as a shared library (Windows)
 // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll
 #if defined(_WIN32)
-#if defined(BUILD_LIBTYPE_SHARED)
-#if defined(__TINYC__)
-#define __declspec(x) __attribute__((x))
-#endif
-#define RLAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
-#elif defined(USE_LIBTYPE_SHARED)
-#define RLAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
-#endif
+    #if defined(BUILD_LIBTYPE_SHARED)
+        #if defined(__TINYC__)
+            #define __declspec(x) __attribute__((x))
+        #endif
+        #define RLAPI __declspec(dllexport)     // We are building the library as a Win32 shared library (.dll)
+    #elif defined(USE_LIBTYPE_SHARED)
+        #define RLAPI __declspec(dllimport)     // We are using the library as a Win32 shared library (.dll)
+    #endif
 #endif
 
 #ifndef RLAPI
@@ -65,8 +65,8 @@
 #endif
 
 #if defined(RCAMERA_STANDALONE)
-    #define CAMERA_CULL_DISTANCE_NEAR      0.01
-    #define CAMERA_CULL_DISTANCE_FAR    1000.0
+    #define CAMERA_CULL_DISTANCE_NEAR      0.05
+    #define CAMERA_CULL_DISTANCE_FAR    4000.0
 #else
     #define CAMERA_CULL_DISTANCE_NEAR   RL_CULL_DISTANCE_NEAR
     #define CAMERA_CULL_DISTANCE_FAR    RL_CULL_DISTANCE_FAR
@@ -154,7 +154,7 @@
 RLAPI void CameraRoll(Camera *camera, float angle);
 
 RLAPI Matrix GetCameraViewMatrix(Camera *camera);
-RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect);
+RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect);
 
 #if defined(__cplusplus)
 }
@@ -191,19 +191,21 @@
                             // IsKeyDown()
                             // IsKeyPressed()
                             // GetFrameTime()
+                            
+#include <math.h>           // Required for: fabsf()
 
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
-#define CAMERA_MOVE_SPEED                               5.4f       // Units per second
-#define CAMERA_ROTATION_SPEED                           0.03f
-#define CAMERA_PAN_SPEED                                0.2f
+#define CAMERA_MOVE_SPEED                           5.4f       // Units per second
+#define CAMERA_ROTATION_SPEED                       0.03f
+#define CAMERA_PAN_SPEED                            0.2f
 
 // Camera mouse movement sensitivity
-#define CAMERA_MOUSE_MOVE_SENSITIVITY                   0.003f
+#define CAMERA_MOUSE_MOVE_SENSITIVITY               0.003f
 
 // Camera orbital speed in CAMERA_ORBITAL mode
-#define CAMERA_ORBITAL_SPEED                            0.5f       // Radians per second
+#define CAMERA_ORBITAL_SPEED                        0.5f       // Radians per second
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -216,7 +218,7 @@
 //...
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 //...
 
@@ -252,8 +254,11 @@
 
     if (moveInWorldPlane)
     {
-        // Project vector onto world plane
-        forward.y = 0;
+        // Project vector onto world plane (the plane defined by the up vector)
+        if (fabsf(camera->up.z) > 0.7071f) forward.z = 0;
+        else if (fabsf(camera->up.x) > 0.7071f) forward.x = 0;
+        else forward.y = 0;
+
         forward = Vector3Normalize(forward);
     }
 
@@ -285,8 +290,11 @@
 
     if (moveInWorldPlane)
     {
-        // Project vector onto world plane
-        right.y = 0;
+        // Project vector onto world plane (the plane defined by the up vector)
+        if (fabsf(camera->up.z) > 0.7071f) right.z = 0;
+        else if (fabsf(camera->up.x) > 0.7071f) right.x = 0;
+        else right.y = 0;
+
         right = Vector3Normalize(right);
     }
 
@@ -345,7 +353,7 @@
 //  - lockView prevents camera overrotation (aka "somersaults")
 //  - rotateAroundTarget defines if rotation is around target or around its position
 //  - rotateUp rotates the up direction as well (typically only usefull in CAMERA_FREE)
-// NOTE: angle must be provided in radians
+// NOTE: [angle] must be provided in radians
 void CameraPitch(Camera *camera, float angle, bool lockView, bool rotateAroundTarget, bool rotateUp)
 {
     // Up direction
@@ -382,7 +390,7 @@
         // Move position relative to target
         camera->position = Vector3Subtract(camera->target, targetPosition);
     }
-    else // rotate around camera.position
+    else // Rotate around camera.position
     {
         // Move target relative to position
         camera->target = Vector3Add(camera->position, targetPosition);
diff --git a/raylib/src/rcore.c b/raylib/src/rcore.c
--- a/raylib/src/rcore.c
+++ b/raylib/src/rcore.c
@@ -12,4035 +12,4654 @@
 *           - Windows (Win32, Win64)
 *           - Linux (X11/Wayland desktop mode)
 *           - Others (not tested)
-*       > PLATFORM_WEB:
-*           - HTML5 (WebAssembly)
-*       > PLATFORM_DRM:
-*           - Raspberry Pi 0-5 (DRM/KMS)
-*           - Linux DRM subsystem (KMS mode)
-*       > PLATFORM_ANDROID:
-*           - Android (ARM, ARM64)
-*
-*   CONFIGURATION:
-*       #define SUPPORT_DEFAULT_FONT (default)
-*           Default font is loaded on window initialization to be available for the user to render simple text.
-*           NOTE: If enabled, uses external module functions to load default raylib font (module: text)
-*
-*       #define SUPPORT_CAMERA_SYSTEM
-*           Camera module is included (rcamera.h) and multiple predefined cameras are available:
-*               free, 1st/3rd person, orbital, custom
-*
-*       #define SUPPORT_GESTURES_SYSTEM
-*           Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
-*
-*       #define SUPPORT_MOUSE_GESTURES
-*           Mouse gestures are directly mapped like touches and processed by gestures system.
-*
-*       #define SUPPORT_BUSY_WAIT_LOOP
-*           Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
-*
-*       #define SUPPORT_PARTIALBUSY_WAIT_LOOP
-*           Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end
-*
-*       #define SUPPORT_SCREEN_CAPTURE
-*           Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
-*
-*       #define SUPPORT_GIF_RECORDING
-*           Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()
-*
-*       #define SUPPORT_COMPRESSION_API
-*           Support CompressData() and DecompressData() functions, those functions use zlib implementation
-*           provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
-*           for linkage
-*
-*       #define SUPPORT_AUTOMATION_EVENTS
-*           Support automatic events recording and playing, useful for automated testing systems or AI based game playing
-*
-*   DEPENDENCIES:
-*       raymath  - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
-*       camera   - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
-*       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) and contributors
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-//----------------------------------------------------------------------------------
-// Feature Test Macros required for this module
-//----------------------------------------------------------------------------------
-#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_XOPEN_SOURCE < 500)
-    #undef _XOPEN_SOURCE
-    #define _XOPEN_SOURCE 500 // Required for: readlink if compiled with c99 without gnu ext.
-#endif
-
-#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L)
-    #undef _POSIX_C_SOURCE
-    #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext.
-#endif
-
-#include "raylib.h"                 // Declares module functions
-
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"             // Defines module configuration flags
-#endif
-
-#include "utils.h"                  // Required for: TRACELOG() macros
-
-#include <stdlib.h>                 // Required for: srand(), rand(), atexit()
-#include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
-#include <string.h>                 // Required for: strrchr(), strcmp(), strlen(), memset()
-#include <time.h>                   // Required for: time() [Used in InitTimer()]
-#include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
-
-#define RLGL_IMPLEMENTATION
-#include "rlgl.h"                   // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
-
-#define RAYMATH_IMPLEMENTATION
-#include "raymath.h"                // Vector2, Vector3, Quaternion and Matrix functionality
-
-#if defined(SUPPORT_GESTURES_SYSTEM)
-    #define RGESTURES_IMPLEMENTATION
-    #include "rgestures.h"          // Gestures detection functionality
-#endif
-
-#if defined(SUPPORT_CAMERA_SYSTEM)
-    #define RCAMERA_IMPLEMENTATION
-    #include "rcamera.h"            // Camera system functionality
-#endif
-
-#if defined(SUPPORT_GIF_RECORDING)
-    #define MSF_GIF_MALLOC(contextPointer, newSize) RL_MALLOC(newSize)
-    #define MSF_GIF_REALLOC(contextPointer, oldMemory, oldSize, newSize) RL_REALLOC(oldMemory, newSize)
-    #define MSF_GIF_FREE(contextPointer, oldMemory, oldSize) RL_FREE(oldMemory)
-
-    #define MSF_GIF_IMPL
-    #include "external/msf_gif.h"   // GIF recording functionality
-#endif
-
-#if defined(SUPPORT_COMPRESSION_API)
-    #define SINFL_IMPLEMENTATION
-    #define SINFL_NO_SIMD
-    #include "external/sinfl.h"     // Deflate (RFC 1951) decompressor
-
-    #define SDEFL_IMPLEMENTATION
-    #include "external/sdefl.h"     // Deflate (RFC 1951) compressor
-#endif
-
-#if defined(SUPPORT_RPRAND_GENERATOR)
-    #define RPRAND_IMPLEMENTATION
-    #include "external/rprand.h"
-#endif
-
-#if defined(__linux__) && !defined(_GNU_SOURCE)
-    #define _GNU_SOURCE
-#endif
-
-// Platform specific defines to handle GetApplicationDirectory()
-#if (defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)) || (defined(_MSC_VER) && defined(PLATFORM_DESKTOP_RGFW))
-    #ifndef MAX_PATH
-        #define MAX_PATH 1025
-    #endif
-__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize);
-__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize);
-__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default);
-unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
-unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
-#elif defined(__linux__)
-    #include <unistd.h>
-#elif defined(__FreeBSD__)
-    #include <sys/types.h>
-    #include <sys/sysctl.h>
-    #include <unistd.h>
-#elif defined(__APPLE__)
-    #include <sys/syslimits.h>
-    #include <mach-o/dyld.h>
-#endif // OSs
-
-#define _CRT_INTERNAL_NONSTDC_NAMES  1
-#include <sys/stat.h>               // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
-
-#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
-    #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-#endif
-
-#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
-    #define DIRENT_MALLOC RL_MALLOC
-    #define DIRENT_FREE RL_FREE
-
-    #include "external/dirent.h"    // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
-#else
-    #include <dirent.h>             // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
-#endif
-
-#if defined(_WIN32)
-    #include <io.h>                 // Required for: _access() [Used in FileExists()]
-    #include <direct.h>             // Required for: _getch(), _chdir(), _mkdir()
-    #define GETCWD _getcwd          // NOTE: MSDN recommends not to use getcwd(), chdir()
-    #define CHDIR _chdir
-    #define MKDIR(dir) _mkdir(dir)
-#else
-    #include <unistd.h>             // Required for: getch(), chdir(), mkdir(), access()
-    #define GETCWD getcwd
-    #define CHDIR chdir
-    #define MKDIR(dir) mkdir(dir, 0777)
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#ifndef MAX_FILEPATH_CAPACITY
-    #define MAX_FILEPATH_CAPACITY       8192        // Maximum capacity for filepath
-#endif
-#ifndef MAX_FILEPATH_LENGTH
-    #if defined(_WIN32)
-        #define MAX_FILEPATH_LENGTH      256        // On Win32, MAX_PATH = 260 (limits.h) but Windows 10, Version 1607 enables long paths...
-    #else
-        #define MAX_FILEPATH_LENGTH     4096        // On Linux, PATH_MAX = 4096 by default (limits.h)
-    #endif
-#endif
-
-#ifndef MAX_KEYBOARD_KEYS
-    #define MAX_KEYBOARD_KEYS            512        // Maximum number of keyboard keys supported
-#endif
-#ifndef MAX_MOUSE_BUTTONS
-    #define MAX_MOUSE_BUTTONS              8        // Maximum number of mouse buttons supported
-#endif
-#ifndef MAX_GAMEPADS
-    #define MAX_GAMEPADS                   4        // Maximum number of gamepads supported
-#endif
-#ifndef MAX_GAMEPAD_AXIS
-    #define MAX_GAMEPAD_AXIS               8        // Maximum number of axis supported (per gamepad)
-#endif
-#ifndef MAX_GAMEPAD_BUTTONS
-    #define MAX_GAMEPAD_BUTTONS           32        // Maximum number of buttons supported (per gamepad)
-#endif
-#ifndef MAX_GAMEPAD_VIBRATION_TIME
-    #define MAX_GAMEPAD_VIBRATION_TIME     2.0f     // Maximum vibration time in seconds
-#endif
-#ifndef MAX_TOUCH_POINTS
-    #define MAX_TOUCH_POINTS               8        // Maximum number of touch points supported
-#endif
-#ifndef MAX_KEY_PRESSED_QUEUE
-    #define MAX_KEY_PRESSED_QUEUE         16        // Maximum number of keys in the key input queue
-#endif
-#ifndef MAX_CHAR_PRESSED_QUEUE
-    #define MAX_CHAR_PRESSED_QUEUE        16        // Maximum number of characters in the char input queue
-#endif
-
-#ifndef MAX_DECOMPRESSION_SIZE
-    #define MAX_DECOMPRESSION_SIZE        64        // Maximum size allocated for decompression in MB
-#endif
-
-#ifndef MAX_AUTOMATION_EVENTS
-    #define MAX_AUTOMATION_EVENTS      16384        // Maximum number of automation events to record
-#endif
-
-#ifndef DIRECTORY_FILTER_TAG
-    #define DIRECTORY_FILTER_TAG       "DIR"        // Name tag used to request directory inclusion on directory scan
-#endif                                              // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
-
-// Flags operation macros
-#define FLAG_SET(n, f) ((n) |= (f))
-#define FLAG_CLEAR(n, f) ((n) &= ~(f))
-#define FLAG_TOGGLE(n, f) ((n) ^= (f))
-#define FLAG_CHECK(n, f) ((n) & (f))
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-typedef struct { int x; int y; } Point;
-typedef struct { unsigned int width; unsigned int height; } Size;
-
-// Core global state context data
-typedef struct CoreData {
-    struct {
-        const char *title;                  // Window text title const pointer
-        unsigned int flags;                 // Configuration flags (bit based), keeps window state
-        bool ready;                         // Check if window has been initialized successfully
-        bool fullscreen;                    // Check if fullscreen mode is enabled
-        bool shouldClose;                   // Check if window set for closing
-        bool resizedLastFrame;              // Check if window has been resized last frame
-        bool eventWaiting;                  // Wait for events before ending frame
-        bool usingFbo;                      // Using FBO (RenderTexture) for rendering instead of default framebuffer
-
-        Point position;                     // Window position (required on fullscreen toggle)
-        Point previousPosition;             // Window previous position (required on borderless windowed toggle)
-        Size display;                       // Display width and height (monitor, device-screen, LCD, ...)
-        Size screen;                        // Screen width and height (used render area)
-        Size previousScreen;                // Screen previous width and height (required on borderless windowed toggle)
-        Size currentFbo;                    // Current render width and height (depends on active fbo)
-        Size render;                        // Framebuffer width and height (render area, including black bars if required)
-        Point renderOffset;                 // Offset from render area (must be divided by 2)
-        Size screenMin;                     // Screen minimum width and height (for resizable window)
-        Size screenMax;                     // Screen maximum width and height (for resizable window)
-        Matrix screenScale;                 // Matrix to scale screen (framebuffer rendering)
-
-        char **dropFilepaths;               // Store dropped files paths pointers (provided by GLFW)
-        unsigned int dropFileCount;         // Count dropped files strings
-
-    } Window;
-    struct {
-        const char *basePath;               // Base path for data storage
-
-    } Storage;
-    struct {
-        struct {
-            int exitKey;                    // Default exit key
-            char currentKeyState[MAX_KEYBOARD_KEYS];        // Registers current frame key state
-            char previousKeyState[MAX_KEYBOARD_KEYS];       // Registers previous frame key state
-
-            // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially
-            char keyRepeatInFrame[MAX_KEYBOARD_KEYS];       // Registers key repeats for current frame
-
-            int keyPressedQueue[MAX_KEY_PRESSED_QUEUE];     // Input keys queue
-            int keyPressedQueueCount;       // Input keys queue count
-
-            int charPressedQueue[MAX_CHAR_PRESSED_QUEUE];   // Input characters queue (unicode)
-            int charPressedQueueCount;      // Input characters queue count
-
-        } Keyboard;
-        struct {
-            Vector2 offset;                 // Mouse offset
-            Vector2 scale;                  // Mouse scaling
-            Vector2 currentPosition;        // Mouse position on screen
-            Vector2 previousPosition;       // Previous mouse position
-
-            int cursor;                     // Tracks current mouse cursor
-            bool cursorHidden;              // Track if cursor is hidden
-            bool cursorOnScreen;            // Tracks if cursor is inside client area
-
-            char currentButtonState[MAX_MOUSE_BUTTONS];     // Registers current mouse button state
-            char previousButtonState[MAX_MOUSE_BUTTONS];    // Registers previous mouse button state
-            Vector2 currentWheelMove;       // Registers current mouse wheel variation
-            Vector2 previousWheelMove;      // Registers previous mouse wheel variation
-
-        } Mouse;
-        struct {
-            int pointCount;                             // Number of touch points active
-            int pointId[MAX_TOUCH_POINTS];              // Point identifiers
-            Vector2 position[MAX_TOUCH_POINTS];         // Touch position on screen
-            char currentTouchState[MAX_TOUCH_POINTS];   // Registers current touch state
-            char previousTouchState[MAX_TOUCH_POINTS];  // Registers previous touch state
-
-        } Touch;
-        struct {
-            int lastButtonPressed;          // Register last gamepad button pressed
-            int axisCount[MAX_GAMEPADS];    // Register number of available gamepad axis
-            bool ready[MAX_GAMEPADS];       // Flag to know if gamepad is ready
-            char name[MAX_GAMEPADS][64];    // Gamepad name holder
-            char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];     // Current gamepad buttons state
-            char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];    // Previous gamepad buttons state
-            float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS];                // Gamepad axis state
-
-        } Gamepad;
-    } Input;
-    struct {
-        double current;                     // Current time measure
-        double previous;                    // Previous time measure
-        double update;                      // Time measure for frame update
-        double draw;                        // Time measure for frame draw
-        double frame;                       // Time measure for one frame
-        double target;                      // Desired time for one frame, if 0 not applied
-        unsigned long long int base;        // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
-        unsigned int frameCounter;          // Frame counter
-
-    } Time;
-} CoreData;
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-RLAPI const char *raylib_version = RAYLIB_VERSION;  // raylib version exported symbol, required for some bindings
-
-CoreData CORE = { 0 };                      // Global CORE state context
-
-// Flag to note GPU acceleration is available,
-// referenced from other modules to support GPU data loading
-// NOTE: Useful to allow Texture, RenderTexture, Font.texture, Mesh.vaoId/vboId, Shader loading
-bool isGpuReady = false;
-
-#if defined(SUPPORT_SCREEN_CAPTURE)
-static int screenshotCounter = 0;           // Screenshots counter
-#endif
-
-#if defined(SUPPORT_GIF_RECORDING)
-static unsigned int gifFrameCounter = 0;    // GIF frames counter
-static bool gifRecording = false;           // GIF recording state
-static MsfGifState gifState = { 0 };        // MSGIF context state
-#endif
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-// Automation events type
-typedef enum AutomationEventType {
-    EVENT_NONE = 0,
-    // Input events
-    INPUT_KEY_UP,                   // param[0]: key
-    INPUT_KEY_DOWN,                 // param[0]: key
-    INPUT_KEY_PRESSED,              // param[0]: key
-    INPUT_KEY_RELEASED,             // param[0]: key
-    INPUT_MOUSE_BUTTON_UP,          // param[0]: button
-    INPUT_MOUSE_BUTTON_DOWN,        // param[0]: button
-    INPUT_MOUSE_POSITION,           // param[0]: x, param[1]: y
-    INPUT_MOUSE_WHEEL_MOTION,       // param[0]: x delta, param[1]: y delta
-    INPUT_GAMEPAD_CONNECT,          // param[0]: gamepad
-    INPUT_GAMEPAD_DISCONNECT,       // param[0]: gamepad
-    INPUT_GAMEPAD_BUTTON_UP,        // param[0]: button
-    INPUT_GAMEPAD_BUTTON_DOWN,      // param[0]: button
-    INPUT_GAMEPAD_AXIS_MOTION,      // param[0]: axis, param[1]: delta
-    INPUT_TOUCH_UP,                 // param[0]: id
-    INPUT_TOUCH_DOWN,               // param[0]: id
-    INPUT_TOUCH_POSITION,           // param[0]: x, param[1]: y
-    INPUT_GESTURE,                  // param[0]: gesture
-    // Window events
-    WINDOW_CLOSE,                   // no params
-    WINDOW_MAXIMIZE,                // no params
-    WINDOW_MINIMIZE,                // no params
-    WINDOW_RESIZE,                  // param[0]: width, param[1]: height
-    // Custom events
-    ACTION_TAKE_SCREENSHOT,         // no params
-    ACTION_SETTARGETFPS             // param[0]: fps
-} AutomationEventType;
-
-// Event type to config events flags
-// TODO: Not used at the moment
-typedef enum {
-    EVENT_INPUT_KEYBOARD    = 0,
-    EVENT_INPUT_MOUSE       = 1,
-    EVENT_INPUT_GAMEPAD     = 2,
-    EVENT_INPUT_TOUCH       = 4,
-    EVENT_INPUT_GESTURE     = 8,
-    EVENT_WINDOW            = 16,
-    EVENT_CUSTOM            = 32
-} EventType;
-
-// Event type name strings, required for export
-static const char *autoEventTypeName[] = {
-    "EVENT_NONE",
-    "INPUT_KEY_UP",
-    "INPUT_KEY_DOWN",
-    "INPUT_KEY_PRESSED",
-    "INPUT_KEY_RELEASED",
-    "INPUT_MOUSE_BUTTON_UP",
-    "INPUT_MOUSE_BUTTON_DOWN",
-    "INPUT_MOUSE_POSITION",
-    "INPUT_MOUSE_WHEEL_MOTION",
-    "INPUT_GAMEPAD_CONNECT",
-    "INPUT_GAMEPAD_DISCONNECT",
-    "INPUT_GAMEPAD_BUTTON_UP",
-    "INPUT_GAMEPAD_BUTTON_DOWN",
-    "INPUT_GAMEPAD_AXIS_MOTION",
-    "INPUT_TOUCH_UP",
-    "INPUT_TOUCH_DOWN",
-    "INPUT_TOUCH_POSITION",
-    "INPUT_GESTURE",
-    "WINDOW_CLOSE",
-    "WINDOW_MAXIMIZE",
-    "WINDOW_MINIMIZE",
-    "WINDOW_RESIZE",
-    "ACTION_TAKE_SCREENSHOT",
-    "ACTION_SETTARGETFPS"
-};
-
-/*
-// Automation event (24 bytes)
-// NOTE: Opaque struct, internal to raylib
-struct AutomationEvent {
-    unsigned int frame;                 // Event frame
-    unsigned int type;                  // Event type (AutomationEventType)
-    int params[4];                      // Event parameters (if required)
-};
-*/
-
-static AutomationEventList *currentEventList = NULL;        // Current automation events list, set by user, keep internal pointer
-static bool automationEventRecording = false;               // Recording automation events flag
-//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
-#endif
-//-----------------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-// NOTE: Those functions are common for all platforms!
-//----------------------------------------------------------------------------------
-
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
-extern void LoadFontDefault(void);      // [Module: text] Loads default font on InitWindow()
-extern void UnloadFontDefault(void);    // [Module: text] Unloads default font from GPU memory
-#endif
-
-extern int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
-extern void ClosePlatform(void);        // Close platform
-
-static void InitTimer(void);                                // Initialize timer, hi-resolution if available (required by InitPlatform())
-static void SetupFramebuffer(int width, int height);        // Setup main framebuffer (required by InitPlatform())
-static void SetupViewport(int width, int height);           // Set viewport for a provided width and height
-
-static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter);   // Scan all files and directories in a base path
-static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter);  // Scan all files and directories recursively from a base path
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-static void RecordAutomationEvent(void); // Record frame events (to internal events array)
-#endif
-
-#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
-// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
-void __stdcall Sleep(unsigned long msTimeout);              // Required for: WaitTime()
-#endif
-
-#if !defined(SUPPORT_MODULE_RTEXT)
-const char *TextFormat(const char *text, ...);              // Formatting of text with variables to 'embed'
-#endif // !SUPPORT_MODULE_RTEXT
-
-#if defined(PLATFORM_DESKTOP)
-    #define PLATFORM_DESKTOP_GLFW
-#endif
-
-// Include platform-specific submodules
-#if defined(PLATFORM_DESKTOP_GLFW)
-    #include "platforms/rcore_desktop_glfw.c"
-#elif defined(PLATFORM_DESKTOP_SDL)
-    #include "platforms/rcore_desktop_sdl.c"
-#elif defined(PLATFORM_DESKTOP_RGFW)
-    #include "platforms/rcore_desktop_rgfw.c"
-#elif defined(PLATFORM_WEB)
-    #include "platforms/rcore_web.c"
-#elif defined(PLATFORM_DRM)
-    #include "platforms/rcore_drm.c"
-#elif defined(PLATFORM_ANDROID)
-    #include "platforms/rcore_android.c"
-#else
-    // TODO: Include your custom platform backend!
-    // i.e software rendering backend or console backend!
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Window and Graphics Device
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//bool WindowShouldClose(void)
-//void ToggleFullscreen(void)
-//void ToggleBorderlessWindowed(void)
-//void MaximizeWindow(void)
-//void MinimizeWindow(void)
-//void RestoreWindow(void)
-
-//void SetWindowState(unsigned int flags)
-//void ClearWindowState(unsigned int flags)
-
-//void SetWindowIcon(Image image)
-//void SetWindowIcons(Image *images, int count)
-//void SetWindowTitle(const char *title)
-//void SetWindowPosition(int x, int y)
-//void SetWindowMonitor(int monitor)
-//void SetWindowMinSize(int width, int height)
-//void SetWindowMaxSize(int width, int height)
-//void SetWindowSize(int width, int height)
-//void SetWindowOpacity(float opacity)
-//void SetWindowFocused(void)
-//void *GetWindowHandle(void)
-//Vector2 GetWindowPosition(void)
-//Vector2 GetWindowScaleDPI(void)
-
-//int GetMonitorCount(void)
-//int GetCurrentMonitor(void)
-//int GetMonitorWidth(int monitor)
-//int GetMonitorHeight(int monitor)
-//int GetMonitorPhysicalWidth(int monitor)
-//int GetMonitorPhysicalHeight(int monitor)
-//int GetMonitorRefreshRate(int monitor)
-//Vector2 GetMonitorPosition(int monitor)
-//const char *GetMonitorName(int monitor)
-
-//void SetClipboardText(const char *text)
-//const char *GetClipboardText(void)
-
-//void ShowCursor(void)
-//void HideCursor(void)
-//void EnableCursor(void)
-//void DisableCursor(void)
-
-// Initialize window and OpenGL context
-void InitWindow(int width, int height, const char *title)
-{
-    TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
-
-#if defined(PLATFORM_DESKTOP_GLFW)
-    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)");
-#elif defined(PLATFORM_DESKTOP_SDL)
-    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)");
-#elif defined(PLATFORM_DESKTOP_RGFW)
-    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)");
-#elif defined(PLATFORM_WEB)
-    TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)");
-#elif defined(PLATFORM_DRM)
-    TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
-#elif defined(PLATFORM_ANDROID)
-    TRACELOG(LOG_INFO, "Platform backend: ANDROID");
-#else
-    // TODO: Include your custom platform backend!
-    // i.e software rendering backend or console backend!
-    TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
-#endif
-
-    TRACELOG(LOG_INFO, "Supported raylib modules:");
-    TRACELOG(LOG_INFO, "    > rcore:..... loaded (mandatory)");
-    TRACELOG(LOG_INFO, "    > rlgl:...... loaded (mandatory)");
-#if defined(SUPPORT_MODULE_RSHAPES)
-    TRACELOG(LOG_INFO, "    > rshapes:... loaded (optional)");
-#else
-    TRACELOG(LOG_INFO, "    > rshapes:... not loaded (optional)");
-#endif
-#if defined(SUPPORT_MODULE_RTEXTURES)
-    TRACELOG(LOG_INFO, "    > rtextures:. loaded (optional)");
-#else
-    TRACELOG(LOG_INFO, "    > rtextures:. not loaded (optional)");
-#endif
-#if defined(SUPPORT_MODULE_RTEXT)
-    TRACELOG(LOG_INFO, "    > rtext:..... loaded (optional)");
-#else
-    TRACELOG(LOG_INFO, "    > rtext:..... not loaded (optional)");
-#endif
-#if defined(SUPPORT_MODULE_RMODELS)
-    TRACELOG(LOG_INFO, "    > rmodels:... loaded (optional)");
-#else
-    TRACELOG(LOG_INFO, "    > rmodels:... not loaded (optional)");
-#endif
-#if defined(SUPPORT_MODULE_RAUDIO)
-    TRACELOG(LOG_INFO, "    > raudio:.... loaded (optional)");
-#else
-    TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
-#endif
-
-    // Initialize window data
-    CORE.Window.screen.width = width;
-    CORE.Window.screen.height = height;
-    CORE.Window.eventWaiting = false;
-    CORE.Window.screenScale = MatrixIdentity();     // No draw scaling required by default
-    if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
-
-    // Initialize global input state
-    memset(&CORE.Input, 0, sizeof(CORE.Input));     // Reset CORE.Input structure to 0
-    CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
-    CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
-    CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
-    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
-
-    // Initialize platform
-    //--------------------------------------------------------------
-    InitPlatform();
-    //--------------------------------------------------------------
-
-    // Initialize rlgl default data (buffers and shaders)
-    // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
-    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-    isGpuReady = true; // Flag to note GPU has been initialized successfully
-
-    // Setup default viewport
-    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
-
-#if defined(SUPPORT_MODULE_RTEXT)
-    #if defined(SUPPORT_DEFAULT_FONT)
-        // Load default font
-        // WARNING: External function: Module required: rtext
-        LoadFontDefault();
-        #if defined(SUPPORT_MODULE_RSHAPES)
-        // Set font white rectangle for shapes drawing, so shapes and text can be batched together
-        // WARNING: rshapes module is required, if not available, default internal white rectangle is used
-        Rectangle rec = GetFontDefault().recs[95];
-        if (CORE.Window.flags & FLAG_MSAA_4X_HINT)
-        {
-            // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
-            SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
-        }
-        else
-        {
-            // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
-            SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
-        }
-        #endif
-    #endif
-#else
-    #if defined(SUPPORT_MODULE_RSHAPES)
-    // Set default texture and rectangle to be used for shapes drawing
-    // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
-    Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
-    SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });    // WARNING: Module required: rshapes
-    #endif
-#endif
-
-    CORE.Time.frameCounter = 0;
-    CORE.Window.shouldClose = false;
-
-    // Initialize random seed
-    SetRandomSeed((unsigned int)time(NULL));
-
-    TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
-}
-
-// Close window and unload OpenGL context
-void CloseWindow(void)
-{
-#if defined(SUPPORT_GIF_RECORDING)
-    if (gifRecording)
-    {
-        MsfGifResult result = msf_gif_end(&gifState);
-        msf_gif_free(result);
-        gifRecording = false;
-    }
-#endif
-
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
-    UnloadFontDefault();        // WARNING: Module required: rtext
-#endif
-
-    rlglClose();                // De-init rlgl
-
-    // De-initialize platform
-    //--------------------------------------------------------------
-    ClosePlatform();
-    //--------------------------------------------------------------
-
-    CORE.Window.ready = false;
-    TRACELOG(LOG_INFO, "Window closed successfully");
-}
-
-// Check if window has been initialized successfully
-bool IsWindowReady(void)
-{
-    return CORE.Window.ready;
-}
-
-// Check if window is currently fullscreen
-bool IsWindowFullscreen(void)
-{
-    return CORE.Window.fullscreen;
-}
-
-// Check if window is currently hidden
-bool IsWindowHidden(void)
-{
-    return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0);
-}
-
-// Check if window has been minimized
-bool IsWindowMinimized(void)
-{
-    return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0);
-}
-
-// Check if window has been maximized
-bool IsWindowMaximized(void)
-{
-    return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0);
-}
-
-// Check if window has the focus
-bool IsWindowFocused(void)
-{
-    return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0);
-}
-
-// Check if window has been resizedLastFrame
-bool IsWindowResized(void)
-{
-    return CORE.Window.resizedLastFrame;
-}
-
-// Check if one specific window flag is enabled
-bool IsWindowState(unsigned int flag)
-{
-    return ((CORE.Window.flags & flag) > 0);
-}
-
-// Get current screen width
-int GetScreenWidth(void)
-{
-    return CORE.Window.screen.width;
-}
-
-// Get current screen height
-int GetScreenHeight(void)
-{
-    return CORE.Window.screen.height;
-}
-
-// Get current render width which is equal to screen width*dpi scale
-int GetRenderWidth(void)
-{
-    int width = 0;
-#if defined(__APPLE__)
-    Vector2 scale = GetWindowScaleDPI();
-    width = (int)((float)CORE.Window.render.width*scale.x);
-#else
-    width = CORE.Window.render.width;
-#endif
-    return width;
-}
-
-// Get current screen height which is equal to screen height*dpi scale
-int GetRenderHeight(void)
-{
-    int height = 0;
-#if defined(__APPLE__)
-    Vector2 scale = GetWindowScaleDPI();
-    height = (int)((float)CORE.Window.render.height*scale.y);
-#else
-    height = CORE.Window.render.height;
-#endif
-    return height;
-}
-
-// Enable waiting for events on EndDrawing(), no automatic event polling
-void EnableEventWaiting(void)
-{
-    CORE.Window.eventWaiting = true;
-}
-
-// Disable waiting for events on EndDrawing(), automatic events polling
-void DisableEventWaiting(void)
-{
-    CORE.Window.eventWaiting = false;
-}
-
-// Check if cursor is not visible
-bool IsCursorHidden(void)
-{
-    return CORE.Input.Mouse.cursorHidden;
-}
-
-// Check if cursor is on the current screen
-bool IsCursorOnScreen(void)
-{
-    return CORE.Input.Mouse.cursorOnScreen;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Screen Drawing
-//----------------------------------------------------------------------------------
-
-// Set background color (framebuffer clear color)
-void ClearBackground(Color color)
-{
-    rlClearColor(color.r, color.g, color.b, color.a);   // Set clear color
-    rlClearScreenBuffers();                             // Clear current framebuffers
-}
-
-// Setup canvas (framebuffer) to start drawing
-void BeginDrawing(void)
-{
-    // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
-    // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
-
-    CORE.Time.current = GetTime();      // Number of elapsed seconds since InitTimer()
-    CORE.Time.update = CORE.Time.current - CORE.Time.previous;
-    CORE.Time.previous = CORE.Time.current;
-
-    rlLoadIdentity();                   // Reset current matrix (modelview)
-    rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
-
-    //rlTranslatef(0.375, 0.375, 0);    // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
-                                        // NOTE: Not required with OpenGL 3.3+
-}
-
-// End canvas drawing and swap buffers (double buffering)
-void EndDrawing(void)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-#if defined(SUPPORT_GIF_RECORDING)
-    // Draw record indicator
-    if (gifRecording)
-    {
-        #ifndef GIF_RECORD_FRAMERATE
-        #define GIF_RECORD_FRAMERATE    10
-        #endif
-        gifFrameCounter += (unsigned int)(GetFrameTime()*1000);
-
-        // NOTE: We record one gif frame depending on the desired gif framerate
-        if (gifFrameCounter > 1000/GIF_RECORD_FRAMERATE)
-        {
-            // Get image data for the current frame (from backbuffer)
-            // NOTE: This process is quite slow... :(
-            Vector2 scale = GetWindowScaleDPI();
-            unsigned char *screenData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
-
-            #ifndef GIF_RECORD_BITRATE
-            #define GIF_RECORD_BITRATE 16
-            #endif
-
-            // Add the frame to the gif recording, given how many frames have passed in centiseconds
-            msf_gif_frame(&gifState, screenData, gifFrameCounter/10, GIF_RECORD_BITRATE, (int)((float)CORE.Window.render.width*scale.x)*4);
-            gifFrameCounter -= 1000/GIF_RECORD_FRAMERATE;
-
-            RL_FREE(screenData);    // Free image data
-        }
-
-    #if defined(SUPPORT_MODULE_RSHAPES) && defined(SUPPORT_MODULE_RTEXT)
-        // Display the recording indicator every half-second
-        if ((int)(GetTime()/0.5)%2 == 1)
-        {
-            DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);                 // WARNING: Module required: rshapes
-            DrawText("GIF RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);     // WARNING: Module required: rtext
-        }
-    #endif
-
-        rlDrawRenderBatchActive();  // Update and draw internal render batch
-    }
-#endif
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    if (automationEventRecording) RecordAutomationEvent();    // Event recording
-#endif
-
-#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
-    SwapScreenBuffer();                  // Copy back buffer to front buffer (screen)
-
-    // Frame time control system
-    CORE.Time.current = GetTime();
-    CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
-    CORE.Time.previous = CORE.Time.current;
-
-    CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
-
-    // Wait for some milliseconds...
-    if (CORE.Time.frame < CORE.Time.target)
-    {
-        WaitTime(CORE.Time.target - CORE.Time.frame);
-
-        CORE.Time.current = GetTime();
-        double waitTime = CORE.Time.current - CORE.Time.previous;
-        CORE.Time.previous = CORE.Time.current;
-
-        CORE.Time.frame += waitTime;    // Total frame time: update + draw + wait
-    }
-
-    PollInputEvents();      // Poll user events (before next frame update)
-#endif
-
-#if defined(SUPPORT_SCREEN_CAPTURE)
-    if (IsKeyPressed(KEY_F12))
-    {
-#if defined(SUPPORT_GIF_RECORDING)
-        if (IsKeyDown(KEY_LEFT_CONTROL))
-        {
-            if (gifRecording)
-            {
-                gifRecording = false;
-
-                MsfGifResult result = msf_gif_end(&gifState);
-
-                SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize);
-                msf_gif_free(result);
-
-                TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording");
-            }
-            else
-            {
-                gifRecording = true;
-                gifFrameCounter = 0;
-
-                Vector2 scale = GetWindowScaleDPI();
-                msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
-                screenshotCounter++;
-
-                TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter));
-            }
-        }
-        else
-#endif  // SUPPORT_GIF_RECORDING
-        {
-            TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
-            screenshotCounter++;
-        }
-    }
-#endif  // SUPPORT_SCREEN_CAPTURE
-
-    CORE.Time.frameCounter++;
-}
-
-// Initialize 2D mode with custom camera (2D)
-void BeginMode2D(Camera2D camera)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlLoadIdentity();               // Reset current matrix (modelview)
-
-    // Apply 2d camera transformation to modelview
-    rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
-}
-
-// Ends 2D mode with custom camera
-void EndMode2D(void)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlLoadIdentity();               // Reset current matrix (modelview)
-
-    if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
-}
-
-// Initializes 3D mode with custom camera (3D)
-void BeginMode3D(Camera camera)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
-    rlPushMatrix();                 // Save previous matrix, which contains the settings for the 2d ortho projection
-    rlLoadIdentity();               // Reset current matrix (projection)
-
-    float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
-
-    // NOTE: zNear and zFar values are important when computing depth buffer values
-    if (camera.projection == CAMERA_PERSPECTIVE)
-    {
-        // Setup perspective projection
-        double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
-        double right = top*aspect;
-
-        rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    }
-    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
-    {
-        // Setup orthographic projection
-        double top = camera.fovy/2.0;
-        double right = top*aspect;
-
-        rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    }
-
-    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
-    rlLoadIdentity();               // Reset current matrix (modelview)
-
-    // Setup Camera view
-    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
-    rlMultMatrixf(MatrixToFloat(matView));      // Multiply modelview matrix by view matrix (camera)
-
-    rlEnableDepthTest();            // Enable DEPTH_TEST for 3D
-}
-
-// Ends 3D mode and returns to default 2D orthographic mode
-void EndMode3D(void)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
-    rlPopMatrix();                  // Restore previous matrix (projection) from matrix stack
-
-    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
-    rlLoadIdentity();               // Reset current matrix (modelview)
-
-    if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
-
-    rlDisableDepthTest();           // Disable DEPTH_TEST for 2D
-}
-
-// Initializes render texture for drawing
-void BeginTextureMode(RenderTexture2D target)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlEnableFramebuffer(target.id); // Enable render target
-
-    // Set viewport and RLGL internal framebuffer size
-    rlViewport(0, 0, target.texture.width, target.texture.height);
-    rlSetFramebufferWidth(target.texture.width);
-    rlSetFramebufferHeight(target.texture.height);
-
-    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
-    rlLoadIdentity();               // Reset current matrix (projection)
-
-    // Set orthographic projection to current framebuffer size
-    // NOTE: Configured top-left corner as (0, 0)
-    rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
-
-    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
-    rlLoadIdentity();               // Reset current matrix (modelview)
-
-    //rlScalef(0.0f, -1.0f, 0.0f);  // Flip Y-drawing (?)
-
-    // Setup current width/height for proper aspect ratio
-    // calculation when using BeginMode3D()
-    CORE.Window.currentFbo.width = target.texture.width;
-    CORE.Window.currentFbo.height = target.texture.height;
-    CORE.Window.usingFbo = true;
-}
-
-// Ends drawing to render texture
-void EndTextureMode(void)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlDisableFramebuffer();         // Disable render target (fbo)
-
-    // Set viewport to default framebuffer size
-    SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
-
-    // Go back to the modelview state from BeginDrawing since we are back to the default FBO
-    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
-    rlLoadIdentity();               // Reset current matrix (modelview)
-    rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
-
-    // Reset current fbo to screen size
-    CORE.Window.currentFbo.width = CORE.Window.render.width;
-    CORE.Window.currentFbo.height = CORE.Window.render.height;
-    CORE.Window.usingFbo = false;
-}
-
-// Begin custom shader mode
-void BeginShaderMode(Shader shader)
-{
-    rlSetShader(shader.id, shader.locs);
-}
-
-// End custom shader mode (returns to default shader)
-void EndShaderMode(void)
-{
-    rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
-}
-
-// Begin blending mode (alpha, additive, multiplied, subtract, custom)
-// NOTE: Blend modes supported are enumerated in BlendMode enum
-void BeginBlendMode(int mode)
-{
-    rlSetBlendMode(mode);
-}
-
-// End blending mode (reset to default: alpha blending)
-void EndBlendMode(void)
-{
-    rlSetBlendMode(BLEND_ALPHA);
-}
-
-// Begin scissor mode (define screen area for following drawing)
-// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
-void BeginScissorMode(int x, int y, int width, int height)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-
-    rlEnableScissorTest();
-
-#if defined(__APPLE__)
-    if (!CORE.Window.usingFbo)
-    {
-        Vector2 scale = GetWindowScaleDPI();
-        rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
-    }
-#else
-    if (!CORE.Window.usingFbo && ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0))
-    {
-        Vector2 scale = GetWindowScaleDPI();
-        rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
-    }
-#endif
-    else
-    {
-        rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
-    }
-}
-
-// End scissor mode
-void EndScissorMode(void)
-{
-    rlDrawRenderBatchActive();      // Update and draw internal render batch
-    rlDisableScissorTest();
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: VR Stereo Rendering
-//----------------------------------------------------------------------------------
-
-// Begin VR drawing configuration
-void BeginVrStereoMode(VrStereoConfig config)
-{
-    rlEnableStereoRender();
-
-    // Set stereo render matrices
-    rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
-    rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
-}
-
-// End VR drawing process (and desktop mirror)
-void EndVrStereoMode(void)
-{
-    rlDisableStereoRender();
-}
-
-// Load VR stereo config for VR simulator device parameters
-VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
-{
-    VrStereoConfig config = { 0 };
-
-    if (rlGetVersion() != RL_OPENGL_11)
-    {
-        // Compute aspect ratio
-        float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
-
-        // Compute lens parameters
-        float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
-        config.leftLensCenter[0] = 0.25f + lensShift;
-        config.leftLensCenter[1] = 0.5f;
-        config.rightLensCenter[0] = 0.75f - lensShift;
-        config.rightLensCenter[1] = 0.5f;
-        config.leftScreenCenter[0] = 0.25f;
-        config.leftScreenCenter[1] = 0.5f;
-        config.rightScreenCenter[0] = 0.75f;
-        config.rightScreenCenter[1] = 0.5f;
-
-        // Compute distortion scale parameters
-        // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
-        float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
-        float lensRadiusSq = lensRadius*lensRadius;
-        float distortionScale = device.lensDistortionValues[0] +
-                                device.lensDistortionValues[1]*lensRadiusSq +
-                                device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
-                                device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
-
-        float normScreenWidth = 0.5f;
-        float normScreenHeight = 1.0f;
-        config.scaleIn[0] = 2.0f/normScreenWidth;
-        config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
-        config.scale[0] = normScreenWidth*0.5f/distortionScale;
-        config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
-
-        // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
-        // ...but with lens distortion it is increased (see Oculus SDK Documentation)
-        float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance);     // Really need distortionScale?
-       // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
-
-        // Compute camera projection matrices
-        float projOffset = 4.0f*lensShift;      // Scaled to projection space coordinates [-1..1]
-        Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
-
-        config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
-        config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
-
-        // Compute camera transformation matrices
-        // NOTE: Camera movement might seem more natural if we model the head
-        // Our axis of rotation is the base of our head, so we might want to add
-        // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
-        config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
-        config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
-
-        // Compute eyes Viewports
-        /*
-        config.eyeViewportRight[0] = 0;
-        config.eyeViewportRight[1] = 0;
-        config.eyeViewportRight[2] = device.hResolution/2;
-        config.eyeViewportRight[3] = device.vResolution;
-
-        config.eyeViewportLeft[0] = device.hResolution/2;
-        config.eyeViewportLeft[1] = 0;
-        config.eyeViewportLeft[2] = device.hResolution/2;
-        config.eyeViewportLeft[3] = device.vResolution;
-        */
-    }
-    else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
-
-    return config;
-}
-
-// Unload VR stereo config properties
-void UnloadVrStereoConfig(VrStereoConfig config)
-{
-    TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c");
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Shaders Management
-//----------------------------------------------------------------------------------
-
-// Load shader from files and bind default locations
-// NOTE: If shader string is NULL, using default vertex/fragment shaders
-Shader LoadShader(const char *vsFileName, const char *fsFileName)
-{
-    Shader shader = { 0 };
-
-    char *vShaderStr = NULL;
-    char *fShaderStr = NULL;
-
-    if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
-    if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
-
-    shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
-
-    UnloadFileText(vShaderStr);
-    UnloadFileText(fShaderStr);
-
-    return shader;
-}
-
-// Load shader from code strings and bind default locations
-Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
-{
-    Shader shader = { 0 };
-
-    shader.id = rlLoadShaderCode(vsCode, fsCode);
-
-    // After shader loading, we TRY to set default location names
-    if (shader.id > 0)
-    {
-        // Default shader attribute locations have been binded before linking:
-        //          vertex position location    = 0
-        //          vertex texcoord location    = 1
-        //          vertex normal location      = 2
-        //          vertex color location       = 3
-        //          vertex tangent location     = 4
-        //          vertex texcoord2 location   = 5
-        //          vertex boneIds location     = 6
-        //          vertex boneWeights location = 7
-
-        // NOTE: If any location is not found, loc point becomes -1
-
-        shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
-
-        // All locations reset to -1 (no location)
-        for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
-
-        // Get handles to GLSL input attribute locations
-        shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
-        shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
-        shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
-        shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
-        shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
-        shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
-        shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
-        shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
-
-        // Get handles to GLSL uniform locations (vertex shader)
-        shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
-        shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
-        shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
-        shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
-        shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
-        shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
-
-        // Get handles to GLSL uniform locations (fragment shader)
-        shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
-        shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);  // SHADER_LOC_MAP_ALBEDO
-        shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
-        shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
-    }
-
-    return shader;
-}
-
-// Check if a shader is valid (loaded on GPU)
-bool IsShaderValid(Shader shader)
-{
-    return ((shader.id > 0) &&          // Validate shader id (GPU loaded successfully)
-            (shader.locs != NULL));     // Validate memory has been allocated for default shader locations
-
-    // The following locations are tried to be set automatically (locs[i] >= 0),
-    // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
-    // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
-
-    // Vertex shader attribute locations (default)
-    // shader.locs[SHADER_LOC_VERTEX_POSITION]      // Set by default internal shader
-    // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]    // Set by default internal shader
-    // shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
-    // shader.locs[SHADER_LOC_VERTEX_NORMAL]
-    // shader.locs[SHADER_LOC_VERTEX_TANGENT]
-    // shader.locs[SHADER_LOC_VERTEX_COLOR]         // Set by default internal shader
-
-    // Vertex shader uniform locations (default)
-    // shader.locs[SHADER_LOC_MATRIX_MVP]           // Set by default internal shader
-    // shader.locs[SHADER_LOC_MATRIX_VIEW]
-    // shader.locs[SHADER_LOC_MATRIX_PROJECTION]
-    // shader.locs[SHADER_LOC_MATRIX_MODEL]
-    // shader.locs[SHADER_LOC_MATRIX_NORMAL]
-
-    // Fragment shader uniform locations (default)
-    // shader.locs[SHADER_LOC_COLOR_DIFFUSE]        // Set by default internal shader
-    // shader.locs[SHADER_LOC_MAP_DIFFUSE]          // Set by default internal shader
-    // shader.locs[SHADER_LOC_MAP_SPECULAR]
-    // shader.locs[SHADER_LOC_MAP_NORMAL]
-}
-
-// Unload shader from GPU memory (VRAM)
-void UnloadShader(Shader shader)
-{
-    if (shader.id != rlGetShaderIdDefault())
-    {
-        rlUnloadShaderProgram(shader.id);
-
-        // NOTE: If shader loading failed, it should be 0
-        RL_FREE(shader.locs);
-    }
-}
-
-// Get shader uniform location
-int GetShaderLocation(Shader shader, const char *uniformName)
-{
-    return rlGetLocationUniform(shader.id, uniformName);
-}
-
-// Get shader attribute location
-int GetShaderLocationAttrib(Shader shader, const char *attribName)
-{
-    return rlGetLocationAttrib(shader.id, attribName);
-}
-
-// Set shader uniform value
-void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
-{
-    SetShaderValueV(shader, locIndex, value, uniformType, 1);
-}
-
-// Set shader uniform value vector
-void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
-{
-    if (locIndex > -1)
-    {
-        rlEnableShader(shader.id);
-        rlSetUniform(locIndex, value, uniformType, count);
-        //rlDisableShader();      // Avoid resetting current shader program, in case other uniforms are set
-    }
-}
-
-// Set shader uniform value (matrix 4x4)
-void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
-{
-    if (locIndex > -1)
-    {
-        rlEnableShader(shader.id);
-        rlSetUniformMatrix(locIndex, mat);
-        //rlDisableShader();
-    }
-}
-
-// Set shader uniform value for texture
-void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
-{
-    if (locIndex > -1)
-    {
-        rlEnableShader(shader.id);
-        rlSetUniformSampler(locIndex, texture.id);
-        //rlDisableShader();
-    }
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Screen-space Queries
-//----------------------------------------------------------------------------------
-
-// Get a ray trace from screen position (i.e mouse)
-Ray GetScreenToWorldRay(Vector2 position, Camera camera)
-{
-    Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
-
-    return ray;
-}
-
-// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
-Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
-{
-    Ray ray = { 0 };
-
-    // Calculate normalized device coordinates
-    // NOTE: y value is negative
-    float x = (2.0f*position.x)/(float)width - 1.0f;
-    float y = 1.0f - (2.0f*position.y)/(float)height;
-    float z = 1.0f;
-
-    // Store values in a vector
-    Vector3 deviceCoords = { x, y, z };
-
-    // Calculate view matrix from camera look at
-    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
-
-    Matrix matProj = MatrixIdentity();
-
-    if (camera.projection == CAMERA_PERSPECTIVE)
-    {
-        // Calculate projection matrix from perspective
-        matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    }
-    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
-    {
-        double aspect = (double)width/(double)height;
-        double top = camera.fovy/2.0;
-        double right = top*aspect;
-
-        // Calculate projection matrix from orthographic
-        matProj = MatrixOrtho(-right, right, -top, top, 0.01, 1000.0);
-    }
-
-    // Unproject far/near points
-    Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
-    Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
-
-    // Unproject the mouse cursor in the near plane
-    // We need this as the source position because orthographic projects,
-    // compared to perspective doesn't have a convergence point,
-    // meaning that the "eye" of the camera is more like a plane than a point
-    Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
-
-    // Calculate normalized direction vector
-    Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
-
-    if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
-    else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
-
-    // Apply calculated vectors to ray
-    ray.direction = direction;
-
-    return ray;
-}
-
-// Get transform matrix for camera
-Matrix GetCameraMatrix(Camera camera)
-{
-    Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
-
-    return mat;
-}
-
-// Get camera 2d transform matrix
-Matrix GetCameraMatrix2D(Camera2D camera)
-{
-    Matrix matTransform = { 0 };
-    // The camera in world-space is set by
-    //   1. Move it to target
-    //   2. Rotate by -rotation and scale by (1/zoom)
-    //      When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
-    //      not for the camera getting bigger, hence the invert. Same deal with rotation
-    //   3. Move it by (-offset);
-    //      Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
-    //      we need to do it into opposite direction (inverse transform)
-
-    // Having camera transform in world-space, inverse of it gives the modelview transform
-    // Since (A*B*C)' = C'*B'*A', the modelview is
-    //   1. Move to offset
-    //   2. Rotate and Scale
-    //   3. Move by -target
-    Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
-    Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
-    Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
-    Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
-
-    matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
-
-    return matTransform;
-}
-
-// Get the screen space position from a 3d world space position
-Vector2 GetWorldToScreen(Vector3 position, Camera camera)
-{
-    Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
-
-    return screenPosition;
-}
-
-// Get size position for a 3d world space position (useful for texture drawing)
-Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
-{
-    // Calculate projection matrix (from perspective instead of frustum
-    Matrix matProj = MatrixIdentity();
-
-    if (camera.projection == CAMERA_PERSPECTIVE)
-    {
-        // Calculate projection matrix from perspective
-        matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    }
-    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
-    {
-        double aspect = (double)width/(double)height;
-        double top = camera.fovy/2.0;
-        double right = top*aspect;
-
-        // Calculate projection matrix from orthographic
-        matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
-    }
-
-    // Calculate view matrix from camera look at (and transpose it)
-    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
-
-    // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)?
-
-    // Convert world position vector to quaternion
-    Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
-
-    // Transform world position to view
-    worldPos = QuaternionTransform(worldPos, matView);
-
-    // Transform result to projection (clip space position)
-    worldPos = QuaternionTransform(worldPos, matProj);
-
-    // Calculate normalized device coordinates (inverted y)
-    Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
-
-    // Calculate 2d screen position vector
-    Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
-
-    return screenPosition;
-}
-
-// Get the screen space position for a 2d camera world space position
-Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
-{
-    Matrix matCamera = GetCameraMatrix2D(camera);
-    Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
-
-    return (Vector2){ transform.x, transform.y };
-}
-
-// Get the world space position for a 2d camera screen space position
-Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
-{
-    Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
-    Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
-
-    return (Vector2){ transform.x, transform.y };
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Timming
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//double GetTime(void)
-
-// Set target FPS (maximum)
-void SetTargetFPS(int fps)
-{
-    if (fps < 1) CORE.Time.target = 0.0;
-    else CORE.Time.target = 1.0/(double)fps;
-
-    TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
-}
-
-// Get current FPS
-// NOTE: We calculate an average framerate
-int GetFPS(void)
-{
-    int fps = 0;
-
-#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
-    #define FPS_CAPTURE_FRAMES_COUNT    30      // 30 captures
-    #define FPS_AVERAGE_TIME_SECONDS   0.5f     // 500 milliseconds
-    #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
-
-    static int index = 0;
-    static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
-    static float average = 0, last = 0;
-    float fpsFrame = GetFrameTime();
-
-    // if we reset the window, reset the FPS info
-    if (CORE.Time.frameCounter == 0)
-    {
-        average = 0;
-        last = 0;
-        index = 0;
-
-        for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
-    }
-
-    if (fpsFrame == 0) return 0;
-
-    if ((GetTime() - last) > FPS_STEP)
-    {
-        last = (float)GetTime();
-        index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
-        average -= history[index];
-        history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
-        average += history[index];
-    }
-
-    fps = (int)roundf(1.0f/average);
-#endif
-
-    return fps;
-}
-
-// Get time in seconds for last frame drawn (delta time)
-float GetFrameTime(void)
-{
-    return (float)CORE.Time.frame;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Custom frame control
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//void SwapScreenBuffer(void);
-//void PollInputEvents(void);
-
-// Wait for some time (stop program execution)
-// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
-// take longer than expected... for that reason we use the busy wait loop
-// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
-// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
-void WaitTime(double seconds)
-{
-    if (seconds < 0) return;    // Security check
-
-#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
-    double destinationTime = GetTime() + seconds;
-#endif
-
-#if defined(SUPPORT_BUSY_WAIT_LOOP)
-    while (GetTime() < destinationTime) { }
-#else
-    #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
-        double sleepSeconds = seconds - seconds*0.05;  // NOTE: We reserve a percentage of the time for busy waiting
-    #else
-        double sleepSeconds = seconds;
-    #endif
-
-    // System halt functions
-    #if defined(_WIN32)
-        Sleep((unsigned long)(sleepSeconds*1000.0));
-    #endif
-    #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
-        struct timespec req = { 0 };
-        time_t sec = sleepSeconds;
-        long nsec = (sleepSeconds - sec)*1000000000L;
-        req.tv_sec = sec;
-        req.tv_nsec = nsec;
-
-        // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
-        while (nanosleep(&req, &req) == -1) continue;
-    #endif
-    #if defined(__APPLE__)
-        usleep(sleepSeconds*1000000.0);
-    #endif
-
-    #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
-        while (GetTime() < destinationTime) { }
-    #endif
-#endif
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Misc
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//void OpenURL(const char *url)
-
-// Set the seed for the random number generator
-void SetRandomSeed(unsigned int seed)
-{
-#if defined(SUPPORT_RPRAND_GENERATOR)
-    rprand_set_seed(seed);
-#else
-    srand(seed);
-#endif
-}
-
-// Get a random value between min and max included
-int GetRandomValue(int min, int max)
-{
-    int value = 0;
-
-    if (min > max)
-    {
-        int tmp = max;
-        max = min;
-        min = tmp;
-    }
-
-#if defined(SUPPORT_RPRAND_GENERATOR)
-    value = rprand_get_value(min, max);
-#else
-    // WARNING: Ranges higher than RAND_MAX will return invalid results
-    // More specifically, if (max - min) > INT_MAX there will be an overflow,
-    // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
-    // NOTE: Depending on the library it can be as low as 32767
-    if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
-    {
-        TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
-    }
-
-    value = (rand()%(abs(max - min) + 1) + min);
-#endif
-    return value;
-}
-
-// Load random values sequence, no values repeated, min and max included
-int *LoadRandomSequence(unsigned int count, int min, int max)
-{
-    int *values = NULL;
-
-#if defined(SUPPORT_RPRAND_GENERATOR)
-    values = rprand_load_sequence(count, min, max);
-#else
-    if (count > ((unsigned int)abs(max - min) + 1)) return values;  // Security check
-
-    values = (int *)RL_CALLOC(count, sizeof(int));
-
-    int value = 0;
-    bool dupValue = false;
-
-    for (int i = 0; i < (int)count;)
-    {
-        value = (rand()%(abs(max - min) + 1) + min);
-        dupValue = false;
-
-        for (int j = 0; j < i; j++)
-        {
-            if (values[j] == value)
-            {
-                dupValue = true;
-                break;
-            }
-        }
-
-        if (!dupValue)
-        {
-            values[i] = value;
-            i++;
-        }
-    }
-#endif
-    return values;
-}
-
-// Unload random values sequence
-void UnloadRandomSequence(int *sequence)
-{
-#if defined(SUPPORT_RPRAND_GENERATOR)
-    rprand_unload_sequence(sequence);
-#else
-    RL_FREE(sequence);
-#endif
-}
-
-// Takes a screenshot of current screen
-// NOTE: Provided fileName should not contain paths, saving to working directory
-void TakeScreenshot(const char *fileName)
-{
-#if defined(SUPPORT_MODULE_RTEXTURES)
-    // Security check to (partially) avoid malicious code
-    if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
-
-    Vector2 scale = GetWindowScaleDPI();
-    unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
-    Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
-
-    char path[512] = { 0 };
-    strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, GetFileName(fileName)));
-
-    ExportImage(image, path);           // WARNING: Module required: rtextures
-    RL_FREE(imgData);
-
-    if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
-    else TRACELOG(LOG_WARNING, "SYSTEM: [%s] Screenshot could not be saved", path);
-#else
-    TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
-#endif
-}
-
-// Setup window configuration flags (view FLAGS)
-// NOTE: This function is expected to be called before window creation,
-// because it sets up some flags for the window creation process
-// To configure window states after creation, just use SetWindowState()
-void SetConfigFlags(unsigned int flags)
-{
-    // Selected flags are set but not evaluated at this point,
-    // flag evaluation happens at InitWindow() or SetWindowState()
-    CORE.Window.flags |= flags;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: File system
-//----------------------------------------------------------------------------------
-
-// Check if the file exists
-bool FileExists(const char *fileName)
-{
-    bool result = false;
-
-#if defined(_WIN32)
-    if (_access(fileName, 0) != -1) result = true;
-#else
-    if (access(fileName, F_OK) != -1) result = true;
-#endif
-
-    // NOTE: Alternatively, stat() can be used instead of access()
-    //#include <sys/stat.h>
-    //struct stat statbuf;
-    //if (stat(filename, &statbuf) == 0) result = true;
-
-    return result;
-}
-
-// Check file extension
-// NOTE: Extensions checking is not case-sensitive
-bool IsFileExtension(const char *fileName, const char *ext)
-{
-    #define MAX_FILE_EXTENSION_LENGTH  16
-
-    bool result = false;
-    const char *fileExt = GetFileExtension(fileName);
-
-    if (fileExt != NULL)
-    {
-#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_TEXT_MANIPULATION)
-        int extCount = 0;
-        const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext
-
-        char fileExtLower[MAX_FILE_EXTENSION_LENGTH + 1] = { 0 };
-        strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_LENGTH); // WARNING: Module required: rtext
-
-        for (int i = 0; i < extCount; i++)
-        {
-            if (strcmp(fileExtLower, TextToLower(checkExts[i])) == 0)
-            {
-                result = true;
-                break;
-            }
-        }
-#else
-        if (strcmp(fileExt, ext) == 0) result = true;
-#endif
-    }
-
-    return result;
-}
-
-// Check if a directory path exists
-bool DirectoryExists(const char *dirPath)
-{
-    bool result = false;
-    DIR *dir = opendir(dirPath);
-
-    if (dir != NULL)
-    {
-        result = true;
-        closedir(dir);
-    }
-
-    return result;
-}
-
-// Get file length in bytes
-// NOTE: GetFileSize() conflicts with windows.h
-int GetFileLength(const char *fileName)
-{
-    int size = 0;
-
-    // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(),
-    // but depending on the platform that call could not be available
-    //struct stat result = { 0 };
-    //stat(fileName, &result);
-    //return result.st_size;
-
-    FILE *file = fopen(fileName, "rb");
-
-    if (file != NULL)
-    {
-        fseek(file, 0L, SEEK_END);
-        long int fileSize = ftell(file);
-
-        // Check for size overflow (INT_MAX)
-        if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName);
-        else size = (int)fileSize;
-
-        fclose(file);
-    }
-
-    return size;
-}
-
-// Get pointer to extension for a filename string (includes the dot: .png)
-const char *GetFileExtension(const char *fileName)
-{
-    const char *dot = strrchr(fileName, '.');
-
-    if (!dot || dot == fileName) return NULL;
-
-    return dot;
-}
-
-// String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
-{
-    const char *latestMatch = NULL;
-
-    for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
-
-    return latestMatch;
-}
-
-// Get pointer to filename for a path string
-const char *GetFileName(const char *filePath)
-{
-    const char *fileName = NULL;
-
-    if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
-
-    if (fileName == NULL) return filePath;
-
-    return fileName + 1;
-}
-
-// Get filename string without extension (uses static string)
-const char *GetFileNameWithoutExt(const char *filePath)
-{
-    #define MAX_FILENAME_LENGTH     256
-
-    static char fileName[MAX_FILENAME_LENGTH] = { 0 };
-    memset(fileName, 0, MAX_FILENAME_LENGTH);
-
-    if (filePath != NULL)
-    {
-        strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
-        int size = (int)strlen(fileName); // Get size in bytes
-
-        for (int i = size; i > 0; i--) // Reverse search '.'
-        {
-            if (fileName[i] == '.')
-            {
-                // NOTE: We break on first '.' found
-                fileName[i] = '\0';
-                break;
-            }
-        }
-    }
-
-    return fileName;
-}
-
-// Get directory for a given filePath
-const char *GetDirectoryPath(const char *filePath)
-{
-    /*
-    // NOTE: Directory separator is different in Windows and other platforms,
-    // fortunately, Windows also support the '/' separator, that's the one should be used
-    #if defined(_WIN32)
-        char separator = '\\';
-    #else
-        char separator = '/';
-    #endif
-    */
-    const char *lastSlash = NULL;
-    static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(dirPath, 0, MAX_FILEPATH_LENGTH);
-
-    // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
-    // we add the current directory path to dirPath
-    if (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/')
-    {
-        // For security, we set starting path to current directory,
-        // obtained path will be concatenated to this
-        dirPath[0] = '.';
-        dirPath[1] = '/';
-    }
-
-    lastSlash = strprbrk(filePath, "\\/");
-    if (lastSlash)
-    {
-        if (lastSlash == filePath)
-        {
-            // The last and only slash is the leading one: path is in a root directory
-            dirPath[0] = filePath[0];
-            dirPath[1] = '\0';
-        }
-        else
-        {
-            // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
-            char *dirPathPtr = dirPath;
-            if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2;     // Skip drive letter, "C:"
-            memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
-            dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0';  // Add '\0' manually
-        }
-    }
-
-    return dirPath;
-}
-
-// Get previous directory path for a given path
-const char *GetPrevDirectoryPath(const char *dirPath)
-{
-    static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
-    int pathLen = (int)strlen(dirPath);
-
-    if (pathLen <= 3) strcpy(prevDirPath, dirPath);
-
-    for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
-    {
-        if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
-        {
-            // Check for root: "C:\" or "/"
-            if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
-
-            strncpy(prevDirPath, dirPath, i);
-            break;
-        }
-    }
-
-    return prevDirPath;
-}
-
-// Get current working directory
-const char *GetWorkingDirectory(void)
-{
-    static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(currentDir, 0, MAX_FILEPATH_LENGTH);
-
-    char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
-
-    return path;
-}
-
-const char *GetApplicationDirectory(void)
-{
-    static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(appDir, 0, MAX_FILEPATH_LENGTH);
-
-#if defined(_WIN32)
-    int len = 0;
-#if defined(UNICODE)
-    unsigned short widePath[MAX_PATH];
-    len = GetModuleFileNameW(NULL, widePath, MAX_PATH);
-    len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL);
-#else
-    len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
-#endif
-    if (len > 0)
-    {
-        for (int i = len; i >= 0; --i)
-        {
-            if (appDir[i] == '\\')
-            {
-                appDir[i + 1] = '\0';
-                break;
-            }
-        }
-    }
-    else
-    {
-        appDir[0] = '.';
-        appDir[1] = '\\';
-    }
-
-#elif defined(__linux__)
-    unsigned int size = sizeof(appDir);
-    ssize_t len = readlink("/proc/self/exe", appDir, size);
-
-    if (len > 0)
-    {
-        for (int i = len; i >= 0; --i)
-        {
-            if (appDir[i] == '/')
-            {
-                appDir[i + 1] = '\0';
-                break;
-            }
-        }
-    }
-    else
-    {
-        appDir[0] = '.';
-        appDir[1] = '/';
-    }
-#elif defined(__APPLE__)
-    uint32_t size = sizeof(appDir);
-
-    if (_NSGetExecutablePath(appDir, &size) == 0)
-    {
-        int len = strlen(appDir);
-        for (int i = len; i >= 0; --i)
-        {
-            if (appDir[i] == '/')
-            {
-                appDir[i + 1] = '\0';
-                break;
-            }
-        }
-    }
-    else
-    {
-        appDir[0] = '.';
-        appDir[1] = '/';
-    }
-#elif defined(__FreeBSD__)
-    size_t size = sizeof(appDir);
-    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
-
-    if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
-    {
-        int len = strlen(appDir);
-        for (int i = len; i >= 0; --i)
-        {
-            if (appDir[i] == '/')
-            {
-                appDir[i + 1] = '\0';
-                break;
-            }
-        }
-    }
-    else
-    {
-        appDir[0] = '.';
-        appDir[1] = '/';
-    }
-
-#endif
-
-    return appDir;
-}
-
-// Load directory filepaths
-// NOTE: Base path is prepended to the scanned filepaths
-// WARNING: Directory is scanned twice, first time to get files count
-// No recursive scanning is done!
-FilePathList LoadDirectoryFiles(const char *dirPath)
-{
-    FilePathList files = { 0 };
-    unsigned int fileCounter = 0;
-
-    struct dirent *entity;
-    DIR *dir = opendir(dirPath);
-
-    if (dir != NULL) // It's a directory
-    {
-        // SCAN 1: Count files
-        while ((entity = readdir(dir)) != NULL)
-        {
-            // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
-            if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
-        }
-
-        // Memory allocation for dirFileCount
-        files.capacity = fileCounter;
-        files.paths = (char **)RL_MALLOC(files.capacity*sizeof(char *));
-        for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_MALLOC(MAX_FILEPATH_LENGTH*sizeof(char));
-
-        closedir(dir);
-
-        // SCAN 2: Read filepaths
-        // NOTE: Directory paths are also registered
-        ScanDirectoryFiles(dirPath, &files, NULL);
-
-        // Security check: read files.count should match fileCounter
-        if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory");  // Maybe it's a file...
-
-    return files;
-}
-
-// Load directory filepaths with extension filtering and recursive directory scan
-// NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
-FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
-{
-    FilePathList files = { 0 };
-
-    files.capacity = MAX_FILEPATH_CAPACITY;
-    files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
-    for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
-
-    // WARNING: basePath is always prepended to scanned paths
-    if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
-    else ScanDirectoryFiles(basePath, &files, filter);
-
-    return files;
-}
-
-// Unload directory filepaths
-// WARNING: files.count is not reseted to 0 after unloading
-void UnloadDirectoryFiles(FilePathList files)
-{
-    for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
-
-    RL_FREE(files.paths);
-}
-
-// Create directories (including full path requested), returns 0 on success
-int MakeDirectory(const char *dirPath)
-{
-    if ((dirPath == NULL) || (dirPath[0] == '\0')) return 1; // Path is not valid
-    if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
-
-    // Copy path string to avoid modifying original
-    int len = (int)strlen(dirPath) + 1;
-    char *pathcpy = (char *)RL_CALLOC(len, 1);
-    memcpy(pathcpy, dirPath, len);
-
-    // Iterate over pathcpy, create each subdirectory as needed
-    for (int i = 0; (i < len) && (pathcpy[i] != '\0'); i++)
-    {
-        if (pathcpy[i] == ':') i++;
-        else
-        {
-            if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
-            {
-                pathcpy[i] = '\0';
-                if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
-                pathcpy[i] = '/';
-            }
-        }
-    }
-
-    // Create final directory
-    if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
-
-    RL_FREE(pathcpy);
-
-    return 0;
-}
-
-// Change working directory, returns true on success
-bool ChangeDirectory(const char *dir)
-{
-    bool result = CHDIR(dir);
-
-    if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
-
-    return (result == 0);
-}
-
-// Check if a given path point to a file
-bool IsPathFile(const char *path)
-{
-    struct stat result = { 0 };
-    stat(path, &result);
-
-    return S_ISREG(result.st_mode);
-}
-
-// Check if fileName is valid for the platform/OS
-bool IsFileNameValid(const char *fileName)
-{
-    bool valid = true;
-
-    if ((fileName != NULL) && (fileName[0] != '\0'))
-    {
-        int length = (int)strlen(fileName);
-        bool allPeriods = true;
-
-        for (int i = 0; i < length; i++)
-        {
-            // Check invalid characters
-            if ((fileName[i] == '<') ||
-                (fileName[i] == '>') ||
-                (fileName[i] == ':') ||
-                (fileName[i] == '\"') ||
-                (fileName[i] == '/') ||
-                (fileName[i] == '\\') ||
-                (fileName[i] == '|') ||
-                (fileName[i] == '?') ||
-                (fileName[i] == '*')) { valid = false; break; }
-
-            // Check non-glyph characters
-            if ((unsigned char)fileName[i] < 32) { valid = false; break; }
-
-            // TODO: Check trailing periods/spaces?
-
-            // Check if filename is not all periods
-            if (fileName[i] != '.') allPeriods = false;
-        }
-
-        if (allPeriods) valid = false;
-
-/*
-        if (valid)
-        {
-            // Check invalid DOS names
-            if (length >= 3)
-            {
-                if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) ||   // CON
-                    ((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) ||   // PRN
-                    ((fileName[0] == 'A') && (fileName[1] == 'U') && (fileName[2] == 'X')) ||   // AUX
-                    ((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
-            }
-
-            if (length >= 4)
-            {
-                if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) ||  // COM0-9
-                    ((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
-            }
-        }
-*/
-    }
-
-    return valid;
-}
-
-// Check if a file has been dropped into window
-bool IsFileDropped(void)
-{
-    bool result = false;
-
-    if (CORE.Window.dropFileCount > 0) result = true;
-
-    return result;
-}
-
-// Load dropped filepaths
-FilePathList LoadDroppedFiles(void)
-{
-    FilePathList files = { 0 };
-
-    files.count = CORE.Window.dropFileCount;
-    files.paths = CORE.Window.dropFilepaths;
-
-    return files;
-}
-
-// Unload dropped filepaths
-void UnloadDroppedFiles(FilePathList files)
-{
-    // WARNING: files pointers are the same as internal ones
-
-    if (files.count > 0)
-    {
-        for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
-
-        RL_FREE(files.paths);
-
-        CORE.Window.dropFileCount = 0;
-        CORE.Window.dropFilepaths = NULL;
-    }
-}
-
-// Get file modification time (last write time)
-long GetFileModTime(const char *fileName)
-{
-    struct stat result = { 0 };
-    long modTime = 0;
-
-    if (stat(fileName, &result) == 0)
-    {
-        time_t mod = result.st_mtime;
-
-        modTime = (long)mod;
-    }
-
-    return modTime;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Compression and Encoding
-//----------------------------------------------------------------------------------
-
-// Compress data (DEFLATE algorithm)
-unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
-{
-    #define COMPRESSION_QUALITY_DEFLATE  8
-
-    unsigned char *compData = NULL;
-
-#if defined(SUPPORT_COMPRESSION_API)
-    // Compress data and generate a valid DEFLATE stream
-    struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl));   // WARNING: Possible stack overflow, struct sdefl is almost 1MB
-    int bounds = sdefl_bound(dataSize);
-    compData = (unsigned char *)RL_CALLOC(bounds, 1);
-
-    *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE);   // Compression level 8, same as stbiw
-    RL_FREE(sdefl);
-
-    TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
-#endif
-
-    return compData;
-}
-
-// Decompress data (DEFLATE algorithm)
-unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
-{
-    unsigned char *data = NULL;
-
-#if defined(SUPPORT_COMPRESSION_API)
-    // Decompress data from a valid DEFLATE stream
-    data = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
-    int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
-
-    // WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data!
-    // TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory
-    unsigned char *temp = (unsigned char *)RL_REALLOC(data, length);
-
-    if (temp != NULL) data = temp;
-    else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory");
-
-    *dataSize = length;
-
-    TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize);
-#endif
-
-    return data;
-}
-
-// Encode data to Base64 string
-char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
-{
-    static const unsigned char base64encodeTable[] = {
-        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
-        'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
-        'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
-    };
-
-    static const int modTable[] = { 0, 2, 1 };
-
-    *outputSize = 4*((dataSize + 2)/3);
-
-    char *encodedData = (char *)RL_MALLOC(*outputSize);
-
-    if (encodedData == NULL) return NULL;   // Security check
-
-    for (int i = 0, j = 0; i < dataSize;)
-    {
-        unsigned int octetA = (i < dataSize)? (unsigned char)data[i++] : 0;
-        unsigned int octetB = (i < dataSize)? (unsigned char)data[i++] : 0;
-        unsigned int octetC = (i < dataSize)? (unsigned char)data[i++] : 0;
-
-        unsigned int triple = (octetA << 0x10) + (octetB << 0x08) + octetC;
-
-        encodedData[j++] = base64encodeTable[(triple >> 3*6) & 0x3F];
-        encodedData[j++] = base64encodeTable[(triple >> 2*6) & 0x3F];
-        encodedData[j++] = base64encodeTable[(triple >> 1*6) & 0x3F];
-        encodedData[j++] = base64encodeTable[(triple >> 0*6) & 0x3F];
-    }
-
-    for (int i = 0; i < modTable[dataSize%3]; i++) encodedData[*outputSize - 1 - i] = '=';  // Padding character
-
-    return encodedData;
-}
-
-// Decode Base64 string data
-unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
-{
-    static const unsigned char base64decodeTable[] = {
-        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
-        11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
-        37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
-    };
-
-    // Get output size of Base64 input data
-    int outSize = 0;
-    for (int i = 0; data[4*i] != 0; i++)
-    {
-        if (data[4*i + 3] == '=')
-        {
-            if (data[4*i + 2] == '=') outSize += 1;
-            else outSize += 2;
-        }
-        else outSize += 3;
-    }
-
-    // Allocate memory to store decoded Base64 data
-    unsigned char *decodedData = (unsigned char *)RL_MALLOC(outSize);
-
-    for (int i = 0; i < outSize/3; i++)
-    {
-        unsigned char a = base64decodeTable[(int)data[4*i]];
-        unsigned char b = base64decodeTable[(int)data[4*i + 1]];
-        unsigned char c = base64decodeTable[(int)data[4*i + 2]];
-        unsigned char d = base64decodeTable[(int)data[4*i + 3]];
-
-        decodedData[3*i] = (a << 2) | (b >> 4);
-        decodedData[3*i + 1] = (b << 4) | (c >> 2);
-        decodedData[3*i + 2] = (c << 6) | d;
-    }
-
-    if (outSize%3 == 1)
-    {
-        int n = outSize/3;
-        unsigned char a = base64decodeTable[(int)data[4*n]];
-        unsigned char b = base64decodeTable[(int)data[4*n + 1]];
-        decodedData[outSize - 1] = (a << 2) | (b >> 4);
-    }
-    else if (outSize%3 == 2)
-    {
-        int n = outSize/3;
-        unsigned char a = base64decodeTable[(int)data[4*n]];
-        unsigned char b = base64decodeTable[(int)data[4*n + 1]];
-        unsigned char c = base64decodeTable[(int)data[4*n + 2]];
-        decodedData[outSize - 2] = (a << 2) | (b >> 4);
-        decodedData[outSize - 1] = (b << 4) | (c >> 2);
-    }
-
-    *outputSize = outSize;
-    return decodedData;
-}
-
-// Compute CRC32 hash code
-unsigned int ComputeCRC32(unsigned char *data, int dataSize)
-{
-    static unsigned int crcTable[256] = {
-        0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
-        0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
-        0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
-        0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
-        0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
-        0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
-        0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
-        0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
-        0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
-        0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
-        0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
-        0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
-        0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
-        0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
-        0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
-        0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
-        0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
-        0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
-        0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
-        0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
-        0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
-        0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
-        0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
-        0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
-        0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
-        0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
-        0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
-        0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
-        0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
-        0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
-        0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
-        0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
-    };
-
-    unsigned int crc = ~0u;
-
-    for (int i = 0; i < dataSize; i++) crc = (crc >> 8) ^ crcTable[data[i] ^ (crc & 0xff)];
-
-    return ~crc;
-}
-
-// Compute MD5 hash code
-// NOTE: Returns a static int[4] array (16 bytes)
-unsigned int *ComputeMD5(unsigned char *data, int dataSize)
-{
-    #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
-
-    static unsigned int hash[4] = { 0 };  // Hash to be returned
-
-    // WARNING: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
-
-    // NOTE: r specifies the per-round shift amounts
-    unsigned int r[] = {
-        7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
-        5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
-        4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
-        6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
-    };
-
-    // Using binary integer part of the sines of integers (in radians) as constants
-    unsigned int k[] = {
-        0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
-        0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
-        0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
-        0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
-        0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
-        0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
-        0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
-        0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
-        0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
-        0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
-        0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
-        0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
-        0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
-        0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
-        0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
-        0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
-    };
-
-    hash[0] = 0x67452301;
-    hash[1] = 0xefcdab89;
-    hash[2] = 0x98badcfe;
-    hash[3] = 0x10325476;
-
-    // Pre-processing: adding a single 1 bit
-    // Append '1' bit to message
-    // NOTE: The input bytes are considered as bits strings,
-    // where the first bit is the most significant bit of the byte
-
-    // Pre-processing: padding with zeros
-    // Append '0' bit until message length in bit 448 (mod 512)
-    // Append length mod (2 pow 64) to message
-
-    int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
-
-    unsigned char *msg = RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
-    memcpy(msg, data, dataSize);
-    msg[dataSize] = 128; // Write the '1' bit
-
-    unsigned int bitsLen = 8*dataSize;
-    memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
-
-    // Process the message in successive 512-bit chunks for each 512-bit chunk of message
-    for (int offset = 0; offset < newDataSize; offset += (512/8))
-    {
-        // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
-        unsigned int *w = (unsigned int *)(msg + offset);
-
-        // Initialize hash value for this chunk
-        unsigned int a = hash[0];
-        unsigned int b = hash[1];
-        unsigned int c = hash[2];
-        unsigned int d = hash[3];
-
-        for (int i = 0; i < 64; i++)
-        {
-            unsigned int f = 0;
-            unsigned int g = 0;
-
-            if (i < 16)
-            {
-                f = (b & c) | ((~b) & d);
-                g = i;
-            }
-            else if (i < 32)
-            {
-                f = (d & b) | ((~d) & c);
-                g = (5*i + 1)%16;
-            }
-            else if (i < 48)
-            {
-                f = b ^ c ^ d;
-                g = (3*i + 5)%16;
-            }
-            else
-            {
-                f = c ^ (b | (~d));
-                g = (7*i)%16;
-            }
-
-            unsigned int temp = d;
-            d = c;
-            c = b;
-            b = b + ROTATE_LEFT((a + f + k[i] + w[g]), r[i]);
-            a = temp;
-        }
-
-        // Add chunk's hash to result so far
-        hash[0] += a;
-        hash[1] += b;
-        hash[2] += c;
-        hash[3] += d;
-    }
-
-    RL_FREE(msg);
-
-    return hash;
-}
-
-// Compute SHA-1 hash code
-// NOTE: Returns a static int[5] array (20 bytes)
-unsigned int *ComputeSHA1(unsigned char *data, int dataSize) {
-    #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
-
-    static unsigned int hash[5] = { 0 };  // Hash to be returned
-
-    // Initialize hash values
-    hash[0] = 0x67452301;
-    hash[1] = 0xEFCDAB89;
-    hash[2] = 0x98BADCFE;
-    hash[3] = 0x10325476;
-    hash[4] = 0xC3D2E1F0;
-
-    // Pre-processing: adding a single 1 bit
-    // Append '1' bit to message
-    // NOTE: The input bytes are considered as bits strings,
-    // where the first bit is the most significant bit of the byte
-
-    // Pre-processing: padding with zeros
-    // Append '0' bit until message length in bit 448 (mod 512)
-    // Append length mod (2 pow 64) to message
-
-    int newDataSize = ((((dataSize + 8)/64) + 1)*64);
-
-    unsigned char *msg = RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
-    memcpy(msg, data, dataSize);
-    msg[dataSize] = 128; // Write the '1' bit
-
-    unsigned int bitsLen = 8*dataSize;
-    msg[newDataSize-1] = bitsLen;
-
-    // Process the message in successive 512-bit chunks
-    for (int offset = 0; offset < newDataSize; offset += (512/8))
-    {
-        // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
-        unsigned int w[80] = {0};
-        for (int i = 0; i < 16; i++) {
-            w[i] = (msg[offset + (i * 4) + 0] << 24) |
-                   (msg[offset + (i * 4) + 1] << 16) |
-                   (msg[offset + (i * 4) + 2] << 8) |
-                   (msg[offset + (i * 4) + 3]);
-        }
-
-        // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
-        for (int i = 16; i < 80; ++i) {
-            w[i] = ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
-        }
-
-        // Initialize hash value for this chunk
-        unsigned int a = hash[0];
-        unsigned int b = hash[1];
-        unsigned int c = hash[2];
-        unsigned int d = hash[3];
-        unsigned int e = hash[4];
-
-        for (int i = 0; i < 80; i++)
-        {
-            unsigned int f = 0;
-            unsigned int k = 0;
-
-            if (i < 20) {
-                f = (b & c) | ((~b) & d);
-                k = 0x5A827999;
-            } else if (i < 40) {
-                f = b ^ c ^ d;
-                k = 0x6ED9EBA1;
-            } else if (i < 60) {
-                f = (b & c) | (b & d) | (c & d);
-                k = 0x8F1BBCDC;
-            } else {
-                f = b ^ c ^ d;
-                k = 0xCA62C1D6;
-            }
-
-            unsigned int temp = ROTATE_LEFT(a, 5) + f + e + k + w[i];
-            e = d;
-            d = c;
-            c = ROTATE_LEFT(b, 30);
-            b = a;
-            a = temp;
-        }
-
-        // Add this chunk's hash to result so far
-        hash[0] += a;
-        hash[1] += b;
-        hash[2] += c;
-        hash[3] += d;
-        hash[4] += e;
-    }
-
-    free(msg);
-
-    return hash;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Automation Events Recording and Playing
-//----------------------------------------------------------------------------------
-
-// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
-AutomationEventList LoadAutomationEventList(const char *fileName)
-{
-    AutomationEventList list = { 0 };
-
-    // Allocate and empty automation event list, ready to record new events
-    list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
-    list.capacity = MAX_AUTOMATION_EVENTS;
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
-    else
-    {
-        // Load automation events file (binary)
-        /*
-        //int dataSize = 0;
-        //unsigned char *data = LoadFileData(fileName, &dataSize);
-
-        FILE *raeFile = fopen(fileName, "rb");
-        unsigned char fileId[4] = { 0 };
-
-        fread(fileId, 1, 4, raeFile);
-
-        if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
-        {
-            fread(&eventCount, sizeof(int), 1, raeFile);
-            TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
-            fread(events, sizeof(AutomationEvent), eventCount, raeFile);
-        }
-
-        fclose(raeFile);
-        */
-
-        // Load events file (text)
-        //unsigned char *buffer = LoadFileText(fileName);
-        FILE *raeFile = fopen(fileName, "rt");
-
-        if (raeFile != NULL)
-        {
-            unsigned int counter = 0;
-            char buffer[256] = { 0 };
-            char eventDesc[64] = { 0 };
-
-            fgets(buffer, 256, raeFile);
-
-            while (!feof(raeFile))
-            {
-                switch (buffer[0])
-                {
-                    case 'c': sscanf(buffer, "c %i", &list.count); break;
-                    case 'e':
-                    {
-                        sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
-                               &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
-
-                        counter++;
-                    } break;
-                    default: break;
-                }
-
-                fgets(buffer, 256, raeFile);
-            }
-
-            if (counter != list.count)
-            {
-                TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
-                list.count = counter;
-            }
-
-            fclose(raeFile);
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
-        }
-
-        TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
-    }
-#endif
-    return list;
-}
-
-// Unload automation events list from file
-void UnloadAutomationEventList(AutomationEventList list)
-{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    RL_FREE(list.events);
-#endif
-}
-
-// Export automation events list as text file
-bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
-{
-    bool success = false;
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    // Export events as binary file
-    // TODO: Save to memory buffer and SaveFileData()
-    /*
-    unsigned char fileId[4] = "rAE ";
-    FILE *raeFile = fopen(fileName, "wb");
-    fwrite(fileId, sizeof(unsigned char), 4, raeFile);
-    fwrite(&eventCount, sizeof(int), 1, raeFile);
-    fwrite(events, sizeof(AutomationEvent), eventCount, raeFile);
-    fclose(raeFile);
-    */
-
-    // Export events as text
-    // TODO: Save to memory buffer and SaveFileText()
-    char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
-
-    int byteCount = 0;
-    byteCount += sprintf(txtData + byteCount, "#\n");
-    byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
-    byteCount += sprintf(txtData + byteCount, "#\n");
-    byteCount += sprintf(txtData + byteCount, "#    c <events_count>\n");
-    byteCount += sprintf(txtData + byteCount, "#    e <frame> <event_type> <param0> <param1> <param2> <param3> // <event_type_name>\n");
-    byteCount += sprintf(txtData + byteCount, "#\n");
-    byteCount += sprintf(txtData + byteCount, "# more info and bugs-report:  github.com/raysan5/raylib\n");
-    byteCount += sprintf(txtData + byteCount, "# feedback and support:       ray[at]raylib.com\n");
-    byteCount += sprintf(txtData + byteCount, "#\n");
-    byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n");
-    byteCount += sprintf(txtData + byteCount, "#\n\n");
-
-    // Add events data
-    byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
-    for (unsigned int i = 0; i < list.count; i++)
-    {
-        byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
-            list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]);
-    }
-
-    // NOTE: Text data size exported is determined by '\0' (NULL) character
-    success = SaveFileText(fileName, txtData);
-
-    RL_FREE(txtData);
-#endif
-
-    return success;
-}
-
-// Setup automation event list to record to
-void SetAutomationEventList(AutomationEventList *list)
-{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    currentEventList = list;
-#endif
-}
-
-// Set automation event internal base frame to start recording
-void SetAutomationEventBaseFrame(int frame)
-{
-    CORE.Time.frameCounter = frame;
-}
-
-// Start recording automation events (AutomationEventList must be set)
-void StartAutomationEventRecording(void)
-{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    automationEventRecording = true;
-#endif
-}
-
-// Stop recording automation events
-void StopAutomationEventRecording(void)
-{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    automationEventRecording = false;
-#endif
-}
-
-// Play a recorded automation event
-void PlayAutomationEvent(AutomationEvent event)
-{
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-    // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
-
-    if (!automationEventRecording)      // TODO: Allow recording events while playing?
-    {
-        switch (event.type)
-        {
-            // Input event
-            case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break;             // param[0]: key
-            case INPUT_KEY_DOWN: {                                                                              // param[0]: key
-                CORE.Input.Keyboard.currentKeyState[event.params[0]] = true;
-
-                if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false)
-                {
-                    if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
-                    {
-                        // Add character to the queue
-                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0];
-                        CORE.Input.Keyboard.keyPressedQueueCount++;
-                    }
-                }
-            } break;
-            case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break;    // param[0]: key
-            case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break;   // param[0]: key
-            case INPUT_MOUSE_POSITION:      // param[0]: x, param[1]: y
-            {
-                CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
-                CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
-            } break;
-            case INPUT_MOUSE_WHEEL_MOTION:  // param[0]: x delta, param[1]: y delta
-            {
-                CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
-                CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
-            } break;
-            case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break;            // param[0]: id
-            case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break;           // param[0]: id
-            case INPUT_TOUCH_POSITION:      // param[0]: id, param[1]: x, param[2]: y
-            {
-                CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
-                CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
-            } break;
-            case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break;                // param[0]: gamepad
-            case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break;            // param[0]: gamepad
-            case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break;    // param[0]: gamepad, param[1]: button
-            case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break;   // param[0]: gamepad, param[1]: button
-            case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
-            {
-                CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
-            } break;
-    #if defined(SUPPORT_GESTURES_SYSTEM)
-            case INPUT_GESTURE: GESTURES.current = event.params[0]; break;     // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
-    #endif
-            // Window event
-            case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
-            case WINDOW_MAXIMIZE: MaximizeWindow(); break;
-            case WINDOW_MINIMIZE: MinimizeWindow(); break;
-            case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
-
-            // Custom event
-    #if defined(SUPPORT_SCREEN_CAPTURE)
-            case ACTION_TAKE_SCREENSHOT:
-            {
-                TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
-                screenshotCounter++;
-            } break;
-    #endif
-            case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
-            default: break;
-        }
-
-        TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]);
-    }
-#endif
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Input Handling: Keyboard
-//----------------------------------------------------------------------------------
-
-// Check if a key has been pressed once
-bool IsKeyPressed(int key)
-{
-
-    bool pressed = false;
-
-    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
-    {
-        if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
-    }
-
-    return pressed;
-}
-
-// Check if a key has been pressed again
-bool IsKeyPressedRepeat(int key)
-{
-    bool repeat = false;
-
-    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
-    {
-        if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true;
-    }
-
-    return repeat;
-}
-
-// Check if a key is being pressed (key held down)
-bool IsKeyDown(int key)
-{
-    bool down = false;
-
-    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
-    {
-        if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true;
-    }
-
-    return down;
-}
-
-// Check if a key has been released once
-bool IsKeyReleased(int key)
-{
-    bool released = false;
-
-    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
-    {
-        if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
-    }
-
-    return released;
-}
-
-// Check if a key is NOT being pressed (key not held down)
-bool IsKeyUp(int key)
-{
-    bool up = false;
-
-    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
-    {
-        if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true;
-    }
-
-    return up;
-}
-
-// Get the last key pressed
-int GetKeyPressed(void)
-{
-    int value = 0;
-
-    if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
-    {
-        // Get character from the queue head
-        value = CORE.Input.Keyboard.keyPressedQueue[0];
-
-        // Shift elements 1 step toward the head
-        for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
-            CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
-
-        // Reset last character in the queue
-        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
-        CORE.Input.Keyboard.keyPressedQueueCount--;
-    }
-
-    return value;
-}
-
-// Get the last char pressed
-int GetCharPressed(void)
-{
-    int value = 0;
-
-    if (CORE.Input.Keyboard.charPressedQueueCount > 0)
-    {
-        // Get character from the queue head
-        value = CORE.Input.Keyboard.charPressedQueue[0];
-
-        // Shift elements 1 step toward the head
-        for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
-            CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
-
-        // Reset last character in the queue
-        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
-        CORE.Input.Keyboard.charPressedQueueCount--;
-    }
-
-    return value;
-}
-
-// Set a custom key to exit program
-// NOTE: default exitKey is set to ESCAPE
-void SetExitKey(int key)
-{
-    CORE.Input.Keyboard.exitKey = key;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Input Handling: Gamepad
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//int SetGamepadMappings(const char *mappings)
-
-// Check if a gamepad is available
-bool IsGamepadAvailable(int gamepad)
-{
-    bool result = false;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
-
-    return result;
-}
-
-// Get gamepad internal name id
-const char *GetGamepadName(int gamepad)
-{
-    return CORE.Input.Gamepad.name[gamepad];
-}
-
-// Check if a gamepad button has been pressed once
-bool IsGamepadButtonPressed(int gamepad, int button)
-{
-    bool pressed = false;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
-        (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
-
-    return pressed;
-}
-
-// Check if a gamepad button is being pressed
-bool IsGamepadButtonDown(int gamepad, int button)
-{
-    bool down = false;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
-        (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true;
-
-    return down;
-}
-
-// Check if a gamepad button has NOT been pressed once
-bool IsGamepadButtonReleased(int gamepad, int button)
-{
-    bool released = false;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
-        (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
-
-    return released;
-}
-
-// Check if a gamepad button is NOT being pressed
-bool IsGamepadButtonUp(int gamepad, int button)
-{
-    bool up = false;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
-        (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true;
-
-    return up;
-}
-
-// Get the last gamepad button pressed
-int GetGamepadButtonPressed(void)
-{
-    return CORE.Input.Gamepad.lastButtonPressed;
-}
-
-// Get gamepad axis count
-int GetGamepadAxisCount(int gamepad)
-{
-    return CORE.Input.Gamepad.axisCount[gamepad];
-}
-
-// Get axis movement vector for a gamepad
-float GetGamepadAxisMovement(int gamepad, int axis)
-{
-    float value = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
-
-    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS)) {
-        float movement = value < 0.0f ? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
-
-        // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA
-        if (movement > value + 0.1f) value = CORE.Input.Gamepad.axisState[gamepad][axis];
-    }
-
-    return value;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Input Handling: Mouse
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//void SetMousePosition(int x, int y)
-//void SetMouseCursor(int cursor)
-
-// Check if a mouse button has been pressed once
-bool IsMouseButtonPressed(int button)
-{
-    bool pressed = false;
-
-    if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
-
-    // Map touches to mouse buttons checking
-    if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
-
-    return pressed;
-}
-
-// Check if a mouse button is being pressed
-bool IsMouseButtonDown(int button)
-{
-    bool down = false;
-
-    if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
-
-    // NOTE: Touches are considered like mouse buttons
-    if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
-
-    return down;
-}
-
-// Check if a mouse button has been released once
-bool IsMouseButtonReleased(int button)
-{
-    bool released = false;
-
-    if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
-
-    // Map touches to mouse buttons checking
-    if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
-
-    return released;
-}
-
-// Check if a mouse button is NOT being pressed
-bool IsMouseButtonUp(int button)
-{
-    bool up = false;
-
-    if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true;
-
-    // NOTE: Touches are considered like mouse buttons
-    if (CORE.Input.Touch.currentTouchState[button] == 0) up = true;
-
-    return up;
-}
-
-// Get mouse position X
-int GetMouseX(void)
-{
-    int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
-    return mouseX;
-}
-
-// Get mouse position Y
-int GetMouseY(void)
-{
-    int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
-    return mouseY;
-}
-
-// Get mouse position XY
-Vector2 GetMousePosition(void)
-{
-    Vector2 position = { 0 };
-
-    position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
-    position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
-
-    return position;
-}
-
-// Get mouse delta between frames
-Vector2 GetMouseDelta(void)
-{
-    Vector2 delta = { 0 };
-
-    delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
-    delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
-
-    return delta;
-}
-
-// Set mouse offset
-// NOTE: Useful when rendering to different size targets
-void SetMouseOffset(int offsetX, int offsetY)
-{
-    CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
-}
-
-// Set mouse scaling
-// NOTE: Useful when rendering to different size targets
-void SetMouseScale(float scaleX, float scaleY)
-{
-    CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
-}
-
-// Get mouse wheel movement Y
-float GetMouseWheelMove(void)
-{
-    float result = 0.0f;
-
-    if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
-    else result = (float)CORE.Input.Mouse.currentWheelMove.y;
-
-    return result;
-}
-
-// Get mouse wheel movement X/Y as a vector
-Vector2 GetMouseWheelMoveV(void)
-{
-    Vector2 result = { 0 };
-
-    result = CORE.Input.Mouse.currentWheelMove;
-
-    return result;
-}
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition: Input Handling: Touch
-//----------------------------------------------------------------------------------
-
-// Get touch position X for touch point 0 (relative to screen size)
-int GetTouchX(void)
-{
-    int touchX = (int)CORE.Input.Touch.position[0].x;
-    return touchX;
-}
-
-// Get touch position Y for touch point 0 (relative to screen size)
-int GetTouchY(void)
-{
-    int touchY = (int)CORE.Input.Touch.position[0].y;
-    return touchY;
-}
-
-// Get touch position XY for a touch point index (relative to screen size)
-// TODO: Touch position should be scaled depending on display size and render size
-Vector2 GetTouchPosition(int index)
-{
-    Vector2 position = { -1.0f, -1.0f };
-
-    if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
-    else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
-
-    return position;
-}
-
-// Get touch point identifier for given index
-int GetTouchPointId(int index)
-{
-    int id = -1;
-
-    if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
-
-    return id;
-}
-
-// Get number of touch points
-int GetTouchPointCount(void)
-{
-    return CORE.Input.Touch.pointCount;
-}
-
-//----------------------------------------------------------------------------------
-// Module Internal Functions Definition
-//----------------------------------------------------------------------------------
-
-// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
-//int InitPlatform(void)
-//void ClosePlatform(void)
-
-// Initialize hi-resolution timer
-void InitTimer(void)
-{
-    // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
-    // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
-    // High resolutions can also prevent the CPU power management system from entering power-saving modes
-    // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
-#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
-    timeBeginPeriod(1);                 // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
-#endif
-
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
-    struct timespec now = { 0 };
-
-    if (clock_gettime(CLOCK_MONOTONIC, &now) == 0)  // Success
-    {
-        CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
-    }
-    else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
-#endif
-
-    CORE.Time.previous = GetTime();     // Get time as double
-}
-
-// Set viewport for a provided width and height
-void SetupViewport(int width, int height)
-{
-    CORE.Window.render.width = width;
-    CORE.Window.render.height = height;
-
-    // Set viewport width and height
-    // NOTE: We consider render size (scaled) and offset in case black bars are required and
-    // render area does not match full display area (this situation is only applicable on fullscreen mode)
-#if defined(__APPLE__)
-    Vector2 scale = GetWindowScaleDPI();
-    rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y);
-#else
-    rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
-#endif
-
-    rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
-    rlLoadIdentity();                   // Reset current matrix (projection)
-
-    // Set orthographic projection to current framebuffer size
-    // NOTE: Configured top-left corner as (0, 0)
-    rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
-
-    rlMatrixMode(RL_MODELVIEW);         // Switch back to modelview matrix
-    rlLoadIdentity();                   // Reset current matrix (modelview)
-}
-
-// Compute framebuffer size relative to screen size and display size
-// NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified
-void SetupFramebuffer(int width, int height)
-{
-    // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var)
-    if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height))
-    {
-        TRACELOG(LOG_WARNING, "DISPLAY: Downscaling required: Screen size (%ix%i) is bigger than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
-
-        // Downscaling to fit display with border-bars
-        float widthRatio = (float)CORE.Window.display.width/(float)CORE.Window.screen.width;
-        float heightRatio = (float)CORE.Window.display.height/(float)CORE.Window.screen.height;
-
-        if (widthRatio <= heightRatio)
-        {
-            CORE.Window.render.width = CORE.Window.display.width;
-            CORE.Window.render.height = (int)round((float)CORE.Window.screen.height*widthRatio);
-            CORE.Window.renderOffset.x = 0;
-            CORE.Window.renderOffset.y = (CORE.Window.display.height - CORE.Window.render.height);
-        }
-        else
-        {
-            CORE.Window.render.width = (int)round((float)CORE.Window.screen.width*heightRatio);
-            CORE.Window.render.height = CORE.Window.display.height;
-            CORE.Window.renderOffset.x = (CORE.Window.display.width - CORE.Window.render.width);
-            CORE.Window.renderOffset.y = 0;
-        }
-
-        // Screen scaling required
-        float scaleRatio = (float)CORE.Window.render.width/(float)CORE.Window.screen.width;
-        CORE.Window.screenScale = MatrixScale(scaleRatio, scaleRatio, 1.0f);
-
-        // NOTE: We render to full display resolution!
-        // We just need to calculate above parameters for downscale matrix and offsets
-        CORE.Window.render.width = CORE.Window.display.width;
-        CORE.Window.render.height = CORE.Window.display.height;
-
-        TRACELOG(LOG_WARNING, "DISPLAY: Downscale matrix generated, content will be rendered at (%ix%i)", CORE.Window.render.width, CORE.Window.render.height);
-    }
-    else if ((CORE.Window.screen.width < CORE.Window.display.width) || (CORE.Window.screen.height < CORE.Window.display.height))
-    {
-        // Required screen size is smaller than display size
-        TRACELOG(LOG_INFO, "DISPLAY: Upscaling required: Screen size (%ix%i) smaller than display size (%ix%i)", CORE.Window.screen.width, CORE.Window.screen.height, CORE.Window.display.width, CORE.Window.display.height);
-
-        if ((CORE.Window.screen.width == 0) || (CORE.Window.screen.height == 0))
-        {
-            CORE.Window.screen.width = CORE.Window.display.width;
-            CORE.Window.screen.height = CORE.Window.display.height;
-        }
-
-        // Upscaling to fit display with border-bars
-        float displayRatio = (float)CORE.Window.display.width/(float)CORE.Window.display.height;
-        float screenRatio = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height;
-
-        if (displayRatio <= screenRatio)
-        {
-            CORE.Window.render.width = CORE.Window.screen.width;
-            CORE.Window.render.height = (int)round((float)CORE.Window.screen.width/displayRatio);
-            CORE.Window.renderOffset.x = 0;
-            CORE.Window.renderOffset.y = (CORE.Window.render.height - CORE.Window.screen.height);
-        }
-        else
-        {
-            CORE.Window.render.width = (int)round((float)CORE.Window.screen.height*displayRatio);
-            CORE.Window.render.height = CORE.Window.screen.height;
-            CORE.Window.renderOffset.x = (CORE.Window.render.width - CORE.Window.screen.width);
-            CORE.Window.renderOffset.y = 0;
-        }
-    }
-    else
-    {
-        CORE.Window.render.width = CORE.Window.screen.width;
-        CORE.Window.render.height = CORE.Window.screen.height;
-        CORE.Window.renderOffset.x = 0;
-        CORE.Window.renderOffset.y = 0;
-    }
-}
-
-// Scan all files and directories in a base path
-// WARNING: files.paths[] must be previously allocated and
-// contain enough space to store all required paths
-static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
-{
-    static char path[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(path, 0, MAX_FILEPATH_LENGTH);
-
-    struct dirent *dp = NULL;
-    DIR *dir = opendir(basePath);
-
-    if (dir != NULL)
-    {
-        while ((dp = readdir(dir)) != NULL)
-        {
-            if ((strcmp(dp->d_name, ".") != 0) &&
-                (strcmp(dp->d_name, "..") != 0))
-            {
-            #if defined(_WIN32)
-                sprintf(path, "%s\\%s", basePath, dp->d_name);
-            #else
-                sprintf(path, "%s/%s", basePath, dp->d_name);
-            #endif
-
-                if (filter != NULL)
-                {
-                    if (IsPathFile(path))
-                    {
-                        if (IsFileExtension(path, filter))
-                        {
-                            strcpy(files->paths[files->count], path);
-                            files->count++;
-                        }
-                    }
-                    else
-                    {
-                        if (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0)
-                        {
-                            strcpy(files->paths[files->count], path);
-                            files->count++;
-                        }
-                    }
-                }
-                else
-                {
-                    strcpy(files->paths[files->count], path);
-                    files->count++;
-                }
-            }
-        }
-
-        closedir(dir);
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
-}
-
-// Scan all files and directories recursively from a base path
-static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
-{
-    char path[MAX_FILEPATH_LENGTH] = { 0 };
-    memset(path, 0, MAX_FILEPATH_LENGTH);
-
-    struct dirent *dp = NULL;
-    DIR *dir = opendir(basePath);
-
-    if (dir != NULL)
-    {
-        while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
-        {
-            if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
-            {
-                // Construct new path from our base path
-            #if defined(_WIN32)
-                sprintf(path, "%s\\%s", basePath, dp->d_name);
-            #else
-                sprintf(path, "%s/%s", basePath, dp->d_name);
-            #endif
-
-                if (IsPathFile(path))
-                {
-                    if (filter != NULL)
-                    {
-                        if (IsFileExtension(path, filter))
-                        {
-                            strcpy(files->paths[files->count], path);
-                            files->count++;
-                        }
-                    }
-                    else
-                    {
-                        strcpy(files->paths[files->count], path);
-                        files->count++;
-                    }
-
-                    if (files->count >= files->capacity)
-                    {
-                        TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
-                        break;
-                    }
-                }
-                else
-                {
-                    if ((filter != NULL) && (TextFindIndex(filter, DIRECTORY_FILTER_TAG) >= 0))
-                    {
-                        strcpy(files->paths[files->count], path);
-                        files->count++;
-                    }
-
-                    if (files->count >= files->capacity)
-                    {
-                        TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
-                        break;
-                    }
-
-                    ScanDirectoryFilesRecursively(path, files, filter);
-                }
-            }
-        }
-
-        closedir(dir);
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
-}
-
-#if defined(SUPPORT_AUTOMATION_EVENTS)
-// Automation event recording
-// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
-static void RecordAutomationEvent(void)
-{
-    // Checking events in current frame and save them into currentEventList
-    // TODO: How important is the current frame? Could it be modified?
-
-    if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-    // Keyboard input events recording
-    //-------------------------------------------------------------------------------------
-    for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
-    {
-        // Event type: INPUT_KEY_UP (only saved once)
-        if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
-            currentEventList->events[currentEventList->count].params[0] = key;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-        // Event type: INPUT_KEY_DOWN
-        if (CORE.Input.Keyboard.currentKeyState[key])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
-            currentEventList->events[currentEventList->count].params[0] = key;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-    //-------------------------------------------------------------------------------------
-
-    // Mouse input currentEventList->events recording
-    //-------------------------------------------------------------------------------------
-    for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
-    {
-        // Event type: INPUT_MOUSE_BUTTON_UP
-        if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
-            currentEventList->events[currentEventList->count].params[0] = button;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-        // Event type: INPUT_MOUSE_BUTTON_DOWN
-        if (CORE.Input.Mouse.currentButtonState[button])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
-            currentEventList->events[currentEventList->count].params[0] = button;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-
-    // Event type: INPUT_MOUSE_POSITION (only saved if changed)
-    if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
-        ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
-    {
-        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-        currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
-        currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
-        currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
-        currentEventList->events[currentEventList->count].params[2] = 0;
-
-        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-        currentEventList->count++;
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-
-    // Event type: INPUT_MOUSE_WHEEL_MOTION
-    if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
-        ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
-    {
-        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-        currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
-        currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
-        currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
-        currentEventList->events[currentEventList->count].params[2] = 0;
-
-        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-        currentEventList->count++;
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-    //-------------------------------------------------------------------------------------
-
-    // Touch input currentEventList->events recording
-    //-------------------------------------------------------------------------------------
-    for (int id = 0; id < MAX_TOUCH_POINTS; id++)
-    {
-        // Event type: INPUT_TOUCH_UP
-        if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
-            currentEventList->events[currentEventList->count].params[0] = id;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-        // Event type: INPUT_TOUCH_DOWN
-        if (CORE.Input.Touch.currentTouchState[id])
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
-            currentEventList->events[currentEventList->count].params[0] = id;
-            currentEventList->events[currentEventList->count].params[1] = 0;
-            currentEventList->events[currentEventList->count].params[2] = 0;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-        // Event type: INPUT_TOUCH_POSITION
-        // TODO: It requires the id!
-        /*
-        if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
-            ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
-        {
-            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
-            currentEventList->events[currentEventList->count].params[0] = id;
-            currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
-            currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
-
-            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-            currentEventList->count++;
-        }
-        */
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-    //-------------------------------------------------------------------------------------
-
-    // Gamepad input currentEventList->events recording
-    //-------------------------------------------------------------------------------------
-    for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
-    {
-        // Event type: INPUT_GAMEPAD_CONNECT
-        /*
-        if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
-            (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
-        {
-            // TODO: Save gamepad connect event
-        }
-        */
-
-        // Event type: INPUT_GAMEPAD_DISCONNECT
-        /*
-        if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
-            (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
-        {
-            // TODO: Save gamepad disconnect event
-        }
-        */
-
-        for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
-        {
-            // Event type: INPUT_GAMEPAD_BUTTON_UP
-            if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
-            {
-                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
-                currentEventList->events[currentEventList->count].params[0] = gamepad;
-                currentEventList->events[currentEventList->count].params[1] = button;
-                currentEventList->events[currentEventList->count].params[2] = 0;
-
-                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-                currentEventList->count++;
-            }
-
-            if (currentEventList->count == currentEventList->capacity) return;    // Security check
-
-            // Event type: INPUT_GAMEPAD_BUTTON_DOWN
-            if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
-            {
-                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
-                currentEventList->events[currentEventList->count].params[0] = gamepad;
-                currentEventList->events[currentEventList->count].params[1] = button;
-                currentEventList->events[currentEventList->count].params[2] = 0;
-
-                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-                currentEventList->count++;
-            }
-
-            if (currentEventList->count == currentEventList->capacity) return;    // Security check
-        }
-
-        for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
-        {
-            // Event type: INPUT_GAMEPAD_AXIS_MOTION
-            float defaultMovement = (axis == GAMEPAD_AXIS_LEFT_TRIGGER || axis == GAMEPAD_AXIS_RIGHT_TRIGGER)? -1.0f : 0.0f;
-            if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
-            {
-                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
-                currentEventList->events[currentEventList->count].params[0] = gamepad;
-                currentEventList->events[currentEventList->count].params[1] = axis;
-                currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
-
-                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-                currentEventList->count++;
-            }
-
-            if (currentEventList->count == currentEventList->capacity) return;    // Security check
-        }
-    }
-    //-------------------------------------------------------------------------------------
-
-#if defined(SUPPORT_GESTURES_SYSTEM)
-    // Gestures input currentEventList->events recording
-    //-------------------------------------------------------------------------------------
-    if (GESTURES.current != GESTURE_NONE)
-    {
-        // Event type: INPUT_GESTURE
-        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
-        currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
-        currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
-        currentEventList->events[currentEventList->count].params[1] = 0;
-        currentEventList->events[currentEventList->count].params[2] = 0;
-
-        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
-        currentEventList->count++;
-
-        if (currentEventList->count == currentEventList->capacity) return;    // Security check
-    }
-    //-------------------------------------------------------------------------------------
-#endif
-}
-#endif
-
-#if !defined(SUPPORT_MODULE_RTEXT)
-// Formatting of text with variables to 'embed'
-// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
-const char *TextFormat(const char *text, ...)
-{
-#ifndef MAX_TEXTFORMAT_BUFFERS
-    #define MAX_TEXTFORMAT_BUFFERS      4        // Maximum number of static buffers for text formatting
-#endif
-#ifndef MAX_TEXT_BUFFER_LENGTH
-    #define MAX_TEXT_BUFFER_LENGTH   1024        // Maximum size of static text buffer
-#endif
-
-    // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
-    static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
-    static int index = 0;
-
-    char *currentBuffer = buffers[index];
-    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
-
-    va_list args;
-    va_start(args, text);
-    int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
-    va_end(args);
-
-    // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
-    if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
-    {
-        // Inserting "..." at the end of the string to mark as truncated
-        char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
-        sprintf(truncBuffer, "...");
-    }
-
-    index += 1;     // Move to next buffer for next function call
-    if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+*       > PLATFORM_DESKTOP_RGFW (RGFW backend):
+*           - Windows (Win32, Win64)
+*           - Linux (X11/Wayland desktop mode)
+*           - macOS/OSX (x64, arm64)
+*           - Others (not tested)
+*       > PLATFORM_WEB_RGFW:
+*           - HTML5 (WebAssembly)
+*       > PLATFORM_WEB:
+*           - HTML5 (WebAssembly)
+*       > PLATFORM_DRM:
+*           - Raspberry Pi 0-5 (DRM/KMS)
+*           - Linux DRM subsystem (KMS mode)
+*       > PLATFORM_ANDROID:
+*           - Android (ARM, ARM64)
+*       > PLATFORM_DESKTOP_WIN32 (Native Win32):
+*           - Windows (Win32, Win64)
+*       > PLATFORM_MEMORY
+*           - Memory framebuffer output, using software renderer, no OS required
+*   CONFIGURATION:
+*       #define SUPPORT_DEFAULT_FONT (default)
+*           Default font is loaded on window initialization to be available for the user to render simple text
+*           NOTE: If enabled, uses external module functions to load default raylib font (module: text)
+*
+*       #define SUPPORT_CAMERA_SYSTEM
+*           Camera module is included (rcamera.h) and multiple predefined cameras are available:
+*               free, 1st/3rd person, orbital, custom
+*
+*       #define SUPPORT_GESTURES_SYSTEM
+*           Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag
+*
+*       #define SUPPORT_MOUSE_GESTURES
+*           Mouse gestures are directly mapped like touches and processed by gestures system
+*
+*       #define SUPPORT_BUSY_WAIT_LOOP
+*           Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used
+*
+*       #define SUPPORT_PARTIALBUSY_WAIT_LOOP
+*           Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end
+*
+*       #define SUPPORT_SCREEN_CAPTURE
+*           Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()
+*
+*       #define SUPPORT_COMPRESSION_API
+*           Support CompressData() and DecompressData() functions, those functions use zlib implementation
+*           provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
+*           for linkage
+*
+*       #define SUPPORT_AUTOMATION_EVENTS
+*           Support automatic events recording and playing, useful for automated testing systems or AI based game playing
+*
+*   DEPENDENCIES:
+*       raymath  - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
+*       camera   - Multiple 3D camera modes (free, orbital, 1st person, 3rd person)
+*       gestures - Gestures system for touch-ready devices (or simulated from mouse inputs)
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5) and contributors
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+//----------------------------------------------------------------------------------
+// Feature Test Macros required for this module
+//----------------------------------------------------------------------------------
+#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_XOPEN_SOURCE < 500)
+    #undef _XOPEN_SOURCE
+    #define _XOPEN_SOURCE 500       // Required for: readlink if compiled with c99 without GNU extensions
+#endif
+
+#if (defined(__linux__) || defined(PLATFORM_WEB) || defined(PLATFORM_WEB_RGFW)) && (_POSIX_C_SOURCE < 199309L)
+    #undef _POSIX_C_SOURCE
+    #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without GNU extensions
+#endif
+
+#include "raylib.h"                 // Declares module functions
+
+#include "config.h"                 // Defines module configuration flags
+
+#include <stdlib.h>                 // Required for: srand(), rand(), atexit(), exit()
+#include <stdio.h>                  // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose(), sprintf() [Used in OpenURL()]
+#include <string.h>                 // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset(), strcat()
+#include <stdarg.h>                 // Required for: va_list, va_start(), va_end() [Used in TraceLog()]
+#include <time.h>                   // Required for: time() [Used in InitTimer()]
+#include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
+
+#if defined(PLATFORM_MEMORY) || defined(PLATFORM_WEB)
+    #define SW_GL_FRAMEBUFFER_COPY_BGRA false
+#endif
+#define RLGL_IMPLEMENTATION
+#include "rlgl.h"                   // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
+
+#define RAYMATH_IMPLEMENTATION
+#include "raymath.h"                // Vector2, Vector3, Quaternion and Matrix functionality
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    #define RGESTURES_IMPLEMENTATION
+    #include "rgestures.h"          // Gestures detection functionality
+#endif
+
+#if defined(SUPPORT_CAMERA_SYSTEM)
+    #define RCAMERA_IMPLEMENTATION
+    #include "rcamera.h"            // Camera system functionality
+#endif
+
+#if defined(SUPPORT_COMPRESSION_API)
+    #define SINFL_IMPLEMENTATION
+    #define SINFL_NO_SIMD
+    #include "external/sinfl.h"     // Deflate (RFC 1951) decompressor
+
+    #define SDEFL_IMPLEMENTATION
+    #include "external/sdefl.h"     // Deflate (RFC 1951) compressor
+#endif
+
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    #define RPRAND_IMPLEMENTATION
+    #include "external/rprand.h"
+#endif
+
+#if defined(__linux__) && !defined(_GNU_SOURCE)
+    #define _GNU_SOURCE
+#endif
+
+// Platform specific defines to handle GetApplicationDirectory()
+#if defined(_WIN32)
+    #if !defined(MAX_PATH)
+        #define MAX_PATH 260
+    #endif
+
+    struct HINSTANCE__;
+    #if defined(__cplusplus)
+    extern "C" {
+    #endif
+    __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(struct HINSTANCE__ *hModule, char *lpFilename, unsigned long nSize);
+    __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(struct HINSTANCE__ *hModule, wchar_t *lpFilename, unsigned long nSize);
+    __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
+    __declspec(dllimport) unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod);
+    __declspec(dllimport) unsigned int __stdcall timeEndPeriod(unsigned int uPeriod);
+    #if defined(__cplusplus)
+    }
+    #endif
+#elif defined(__linux__)
+    #include <unistd.h>
+#elif defined(__FreeBSD__)
+    #include <sys/types.h>
+    #include <sys/sysctl.h>
+    #include <unistd.h>
+#elif defined(__APPLE__)
+    #include <sys/syslimits.h>
+    #include <mach-o/dyld.h>
+#endif // OSs
+
+#define _CRT_INTERNAL_NONSTDC_NAMES  1
+#include <sys/stat.h>               // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()]
+
+#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
+    #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#endif
+
+#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
+    #define DIRENT_MALLOC RL_MALLOC
+    #define DIRENT_FREE RL_FREE
+
+    #include "external/dirent.h"    // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
+#else
+    #include <dirent.h>             // Required for: DIR, opendir(), closedir() [Used in LoadDirectoryFiles()]
+#endif
+
+#if defined(_WIN32)
+    #include <io.h>                 // Required for: _access() [Used in FileExists()]
+    #include <direct.h>             // Required for: _getch(), _chdir(), _mkdir()
+    #define GETCWD _getcwd          // NOTE: MSDN recommends not to use getcwd(), chdir()
+    #define CHDIR _chdir
+    #define MKDIR(dir) _mkdir(dir)
+    #define ACCESS(fn) _access(fn, 0)
+#else
+    #include <unistd.h>             // Required for: getch(), chdir(), mkdir(), access()
+    #define GETCWD getcwd
+    #define CHDIR chdir
+    #define MKDIR(dir) mkdir(dir, 0777)
+    #define ACCESS(fn) access(fn, F_OK)
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#ifndef MAX_TRACELOG_MSG_LENGTH
+    #define MAX_TRACELOG_MSG_LENGTH      256        // Max length of one trace-log message
+#endif
+
+#ifndef MAX_FILEPATH_CAPACITY
+    #define MAX_FILEPATH_CAPACITY       8192        // Maximum capacity for filepath
+#endif
+#ifndef MAX_FILEPATH_LENGTH
+    #if defined(_WIN32)
+        #define MAX_FILEPATH_LENGTH      256        // On Win32, MAX_PATH = 260 (limits.h) but Windows 10, Version 1607 enables long paths...
+    #else
+        #define MAX_FILEPATH_LENGTH     4096        // On Linux, PATH_MAX = 4096 by default (limits.h)
+    #endif
+#endif
+
+#ifndef MAX_KEYBOARD_KEYS
+    #define MAX_KEYBOARD_KEYS            512        // Maximum number of keyboard keys supported
+#endif
+#ifndef MAX_MOUSE_BUTTONS
+    #define MAX_MOUSE_BUTTONS              8        // Maximum number of mouse buttons supported
+#endif
+#ifndef MAX_GAMEPADS
+    #define MAX_GAMEPADS                   4        // Maximum number of gamepads supported
+#endif
+#ifndef MAX_GAMEPAD_NAME_LENGTH
+    #define MAX_GAMEPAD_NAME_LENGTH      128        // Maximum number of characters in a gamepad name (byte size)
+#endif
+#ifndef MAX_GAMEPAD_AXES
+    #define MAX_GAMEPAD_AXES               8        // Maximum number of axes supported (per gamepad)
+#endif
+#ifndef MAX_GAMEPAD_BUTTONS
+    #define MAX_GAMEPAD_BUTTONS           32        // Maximum number of buttons supported (per gamepad)
+#endif
+#ifndef MAX_GAMEPAD_VIBRATION_TIME
+    #define MAX_GAMEPAD_VIBRATION_TIME     2.0f     // Maximum vibration time in seconds
+#endif
+#ifndef MAX_TOUCH_POINTS
+    #define MAX_TOUCH_POINTS               8        // Maximum number of touch points supported
+#endif
+#ifndef MAX_KEY_PRESSED_QUEUE
+    #define MAX_KEY_PRESSED_QUEUE         16        // Maximum number of keys in the key input queue
+#endif
+#ifndef MAX_CHAR_PRESSED_QUEUE
+    #define MAX_CHAR_PRESSED_QUEUE        16        // Maximum number of characters in the char input queue
+#endif
+
+#ifndef MAX_DECOMPRESSION_SIZE
+    #define MAX_DECOMPRESSION_SIZE        64        // Maximum size allocated for decompression in MB
+#endif
+
+#ifndef MAX_AUTOMATION_EVENTS
+    #define MAX_AUTOMATION_EVENTS      16384        // Maximum number of automation events to record
+#endif
+
+#ifndef DIRECTORY_FILTER_TAG
+    #define DIRECTORY_FILTER_TAG       "DIR"        // Name tag used to request directory inclusion on directory scan
+#endif                                              // NOTE: Used in ScanDirectoryFiles(), ScanDirectoryFilesRecursively() and LoadDirectoryFilesEx()
+
+// Flags operation macros
+#define FLAG_SET(n, f) ((n) |= (f))
+#define FLAG_CLEAR(n, f) ((n) &= ~(f))
+#define FLAG_TOGGLE(n, f) ((n) ^= (f))
+#define FLAG_IS_SET(n, f) (((n) & (f)) == (f))
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+typedef struct { int x; int y; } Point;
+typedef struct { unsigned int width; unsigned int height; } Size;
+
+// Core global state context data
+typedef struct CoreData {
+    struct {
+        const char *title;                  // Window text title const pointer
+        unsigned int flags;                 // Configuration flags (bit based), keeps window state
+        bool ready;                         // Check if window has been initialized successfully
+        bool shouldClose;                   // Check if window set for closing
+        bool resizedLastFrame;              // Check if window has been resized last frame
+        bool eventWaiting;                  // Wait for events before ending frame
+        bool usingFbo;                      // Using FBO (RenderTexture) for rendering instead of default framebuffer
+
+        Size display;                       // Display width and height (monitor, device-screen, LCD, ...)
+        Size screen;                        // Screen current width and height
+        Point position;                     // Window current position
+        Size previousScreen;                // Screen previous width and height (required on fullscreen/borderless-windowed toggle)
+        Point previousPosition;             // Window previous position (required on fullscreeen/borderless-windowed toggle)
+        Size render;                        // Screen framebuffer width and height
+        Point renderOffset;                 // Screen framebuffer render offset (Not required anymore?)
+        Size currentFbo;                    // Current framebuffer render width and height (depends on active render texture)
+        Size screenMin;                     // Screen minimum width and height (for resizable window)
+        Size screenMax;                     // Screen maximum width and height (for resizable window)
+        Matrix screenScale;                 // Matrix to scale screen (framebuffer rendering)
+
+        char **dropFilepaths;               // Store dropped files paths pointers (provided by GLFW)
+        unsigned int dropFileCount;         // Count dropped files strings
+
+    } Window;
+    struct {
+        const char *basePath;               // Base path for data storage
+
+    } Storage;
+    struct {
+        struct {
+            int exitKey;                    // Default exit key
+            char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state
+            char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state
+
+            // NOTE: Since key press logic involves comparing previous vs currrent key state,
+            // key repeats needs to be handled specially
+            char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame
+
+            int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue
+            int keyPressedQueueCount;       // Input keys queue count
+
+            int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode)
+            int charPressedQueueCount;      // Input characters queue count
+
+        } Keyboard;
+        struct {
+            Vector2 offset;                 // Mouse offset
+            Vector2 scale;                  // Mouse scaling
+            Vector2 currentPosition;        // Mouse position on screen
+            Vector2 previousPosition;       // Previous mouse position
+            Vector2 lockedPosition;         // Mouse position when locked
+
+            int cursor;                     // Tracks current mouse cursor
+            bool cursorHidden;              // Track if cursor is hidden
+            bool cursorLocked;              // Track if cursor is locked (disabled)
+            bool cursorOnScreen;            // Tracks if cursor is inside client area
+
+            char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state
+            char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state
+            Vector2 currentWheelMove;       // Registers current mouse wheel variation
+            Vector2 previousWheelMove;      // Registers previous mouse wheel variation
+
+        } Mouse;
+        struct {
+            int pointCount;                                 // Number of touch points active
+            int pointId[MAX_TOUCH_POINTS];                  // Point identifiers
+            Vector2 position[MAX_TOUCH_POINTS];             // Touch position on screen
+            Vector2 previousPosition[MAX_TOUCH_POINTS];     // Previous touch position on screen
+            char currentTouchState[MAX_TOUCH_POINTS];       // Registers current touch state
+            char previousTouchState[MAX_TOUCH_POINTS];      // Registers previous touch state
+
+        } Touch;
+        struct {
+            int lastButtonPressed;          // Register last gamepad button pressed
+            int axisCount[MAX_GAMEPADS];    // Register number of available gamepad axes
+            bool ready[MAX_GAMEPADS];       // Flag to know if gamepad is ready
+            char name[MAX_GAMEPADS][MAX_GAMEPAD_NAME_LENGTH];               // Gamepad name holder
+            char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];     // Current gamepad buttons state
+            char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS];    // Previous gamepad buttons state
+            float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXES];                // Gamepad axes state
+
+        } Gamepad;
+    } Input;
+    struct {
+        double current;                     // Current time measure
+        double previous;                    // Previous time measure
+        double update;                      // Time measure for frame update
+        double draw;                        // Time measure for frame draw
+        double frame;                       // Time measure for one frame
+        double target;                      // Desired time for one frame, if 0 not applied
+        unsigned long long int base;        // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM)
+        unsigned int frameCounter;          // Frame counter
+
+    } Time;
+} CoreData;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+RLAPI const char *raylib_version = RAYLIB_VERSION;  // raylib version exported symbol, required for some bindings
+
+CoreData CORE = { 0 };                              // Global CORE state context
+
+static int logTypeLevel = LOG_INFO;                 // Minimum log type level
+
+static TraceLogCallback traceLog = NULL;            // TraceLog callback function pointer
+static LoadFileDataCallback loadFileData = NULL;    // LoadFileData callback function pointer
+static SaveFileDataCallback saveFileData = NULL;    // SaveFileText callback function pointer
+static LoadFileTextCallback loadFileText = NULL;    // LoadFileText callback function pointer
+static SaveFileTextCallback saveFileText = NULL;    // SaveFileText callback function pointer
+
+#if defined(SUPPORT_SCREEN_CAPTURE)
+static int screenshotCounter = 0;                   // Screenshots counter
+#endif
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+// Automation events type
+typedef enum AutomationEventType {
+    EVENT_NONE = 0,
+    // Input events
+    INPUT_KEY_UP,                   // param[0]: key
+    INPUT_KEY_DOWN,                 // param[0]: key
+    INPUT_KEY_PRESSED,              // param[0]: key
+    INPUT_KEY_RELEASED,             // param[0]: key
+    INPUT_MOUSE_BUTTON_UP,          // param[0]: button
+    INPUT_MOUSE_BUTTON_DOWN,        // param[0]: button
+    INPUT_MOUSE_POSITION,           // param[0]: x, param[1]: y
+    INPUT_MOUSE_WHEEL_MOTION,       // param[0]: x delta, param[1]: y delta
+    INPUT_GAMEPAD_CONNECT,          // param[0]: gamepad
+    INPUT_GAMEPAD_DISCONNECT,       // param[0]: gamepad
+    INPUT_GAMEPAD_BUTTON_UP,        // param[0]: button
+    INPUT_GAMEPAD_BUTTON_DOWN,      // param[0]: button
+    INPUT_GAMEPAD_AXIS_MOTION,      // param[0]: axis, param[1]: delta
+    INPUT_TOUCH_UP,                 // param[0]: id
+    INPUT_TOUCH_DOWN,               // param[0]: id
+    INPUT_TOUCH_POSITION,           // param[0]: x, param[1]: y
+    INPUT_GESTURE,                  // param[0]: gesture
+    // Window events
+    WINDOW_CLOSE,                   // no params
+    WINDOW_MAXIMIZE,                // no params
+    WINDOW_MINIMIZE,                // no params
+    WINDOW_RESIZE,                  // param[0]: width, param[1]: height
+    // Custom events
+    ACTION_TAKE_SCREENSHOT,         // no params
+    ACTION_SETTARGETFPS             // param[0]: fps
+} AutomationEventType;
+
+// Event type to config events flags
+// WARNING: Not used at the moment
+typedef enum {
+    EVENT_INPUT_KEYBOARD    = 0,
+    EVENT_INPUT_MOUSE       = 1,
+    EVENT_INPUT_GAMEPAD     = 2,
+    EVENT_INPUT_TOUCH       = 4,
+    EVENT_INPUT_GESTURE     = 8,
+    EVENT_WINDOW            = 16,
+    EVENT_CUSTOM            = 32
+} EventType;
+
+// Event type name strings, required for export
+static const char *autoEventTypeName[] = {
+    "EVENT_NONE",
+    "INPUT_KEY_UP",
+    "INPUT_KEY_DOWN",
+    "INPUT_KEY_PRESSED",
+    "INPUT_KEY_RELEASED",
+    "INPUT_MOUSE_BUTTON_UP",
+    "INPUT_MOUSE_BUTTON_DOWN",
+    "INPUT_MOUSE_POSITION",
+    "INPUT_MOUSE_WHEEL_MOTION",
+    "INPUT_GAMEPAD_CONNECT",
+    "INPUT_GAMEPAD_DISCONNECT",
+    "INPUT_GAMEPAD_BUTTON_UP",
+    "INPUT_GAMEPAD_BUTTON_DOWN",
+    "INPUT_GAMEPAD_AXIS_MOTION",
+    "INPUT_TOUCH_UP",
+    "INPUT_TOUCH_DOWN",
+    "INPUT_TOUCH_POSITION",
+    "INPUT_GESTURE",
+    "WINDOW_CLOSE",
+    "WINDOW_MAXIMIZE",
+    "WINDOW_MINIMIZE",
+    "WINDOW_RESIZE",
+    "ACTION_TAKE_SCREENSHOT",
+    "ACTION_SETTARGETFPS"
+};
+
+/*
+// Automation event (24 bytes)
+// NOTE: Opaque struct, internal to raylib
+struct AutomationEvent {
+    unsigned int frame;                 // Event frame
+    unsigned int type;                  // Event type (AutomationEventType)
+    int params[4];                      // Event parameters (if required)
+};
+*/
+
+static AutomationEventList *currentEventList = NULL;        // Current automation events list, set by user, keep internal pointer
+static bool automationEventRecording = false;               // Recording automation events flag
+//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
+#endif
+//-----------------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+// NOTE: Those functions are common for all platforms!
+//----------------------------------------------------------------------------------
+
+#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+extern void LoadFontDefault(void);      // [Module: text] Loads default font on InitWindow()
+extern void UnloadFontDefault(void);    // [Module: text] Unloads default font from GPU memory
+#endif
+
+extern int InitPlatform(void);          // Initialize platform (graphics, inputs and more)
+extern void ClosePlatform(void);        // Close platform
+
+static void InitTimer(void);                                // Initialize timer, hi-resolution if available (required by InitPlatform())
+static void SetupViewport(int width, int height);           // Set viewport for a provided width and height
+
+static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter);   // Scan all files and directories in a base path
+static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter);  // Scan all files and directories recursively from a base path
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+static void RecordAutomationEvent(void); // Record frame events (to internal events array)
+#endif
+
+#if defined(_WIN32) && !defined(PLATFORM_DESKTOP_RGFW)
+// NOTE: We declare Sleep() function symbol to avoid including windows.h (kernel32.lib linkage required)
+__declspec(dllimport) void __stdcall Sleep(unsigned long msTimeout); // Required for: WaitTime()
+#endif
+
+#if !defined(SUPPORT_MODULE_RTEXT)
+const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
+#endif // !SUPPORT_MODULE_RTEXT
+
+#if defined(PLATFORM_DESKTOP)
+    #define PLATFORM_DESKTOP_GLFW
+#endif
+
+// We're using '#pragma message' because '#warning' is not adopted by MSVC
+#if defined(SUPPORT_CLIPBOARD_IMAGE)
+    #if !defined(SUPPORT_MODULE_RTEXTURES)
+        #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_MODULE_RTEXTURES to work properly")
+    #endif
+
+    // It's nice to have support Bitmap on Linux as well, but not as necessary as Windows
+    #if !defined(SUPPORT_FILEFORMAT_BMP) && defined(_WIN32)
+        #pragma message ("WARNING: Enabling SUPPORT_CLIPBOARD_IMAGE requires SUPPORT_FILEFORMAT_BMP, specially on Windows")
+    #endif
+
+    // From what I've tested applications on Wayland saves images on clipboard as PNG
+    #if (!defined(SUPPORT_FILEFORMAT_PNG) || !defined(SUPPORT_FILEFORMAT_JPG)) && !defined(_WIN32)
+        #pragma message ("WARNING: Getting image from the clipboard might not work without SUPPORT_FILEFORMAT_PNG or SUPPORT_FILEFORMAT_JPG")
+    #endif
+#endif // SUPPORT_CLIPBOARD_IMAGE
+
+// Include platform-specific submodules
+#if defined(PLATFORM_DESKTOP_GLFW)
+    #include "platforms/rcore_desktop_glfw.c"
+#elif defined(PLATFORM_DESKTOP_SDL)
+    #include "platforms/rcore_desktop_sdl.c"
+#elif (defined(PLATFORM_DESKTOP_RGFW) || defined(PLATFORM_WEB_RGFW))
+    #include "platforms/rcore_desktop_rgfw.c"
+#elif defined(PLATFORM_DESKTOP_WIN32)
+    #include "platforms/rcore_desktop_win32.c"
+#elif defined(PLATFORM_WEB)
+    #include "platforms/rcore_web.c"
+#elif defined(PLATFORM_DRM)
+    #include "platforms/rcore_drm.c"
+#elif defined(PLATFORM_ANDROID)
+    #include "platforms/rcore_android.c"
+#elif defined(PLATFORM_MEMORY)
+    #include "platforms/rcore_memory.c"
+#else
+    // TODO: Include your custom platform backend!
+    // i.e software rendering backend or console backend!
+    #pragma message ("WARNING: No [rcore] platform defined")
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Window and Graphics Device
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//bool WindowShouldClose(void)
+//void ToggleFullscreen(void)
+//void ToggleBorderlessWindowed(void)
+//void MaximizeWindow(void)
+//void MinimizeWindow(void)
+//void RestoreWindow(void)
+
+//void SetWindowState(unsigned int flags)
+//void ClearWindowState(unsigned int flags)
+
+//void SetWindowIcon(Image image)
+//void SetWindowIcons(Image *images, int count)
+//void SetWindowTitle(const char *title)
+//void SetWindowPosition(int x, int y)
+//void SetWindowMonitor(int monitor)
+//void SetWindowMinSize(int width, int height)
+//void SetWindowMaxSize(int width, int height)
+//void SetWindowSize(int width, int height)
+//void SetWindowOpacity(float opacity)
+//void SetWindowFocused(void)
+//void *GetWindowHandle(void)
+//Vector2 GetWindowPosition(void)
+//Vector2 GetWindowScaleDPI(void)
+
+//int GetMonitorCount(void)
+//int GetCurrentMonitor(void)
+//int GetMonitorWidth(int monitor)
+//int GetMonitorHeight(int monitor)
+//int GetMonitorPhysicalWidth(int monitor)
+//int GetMonitorPhysicalHeight(int monitor)
+//int GetMonitorRefreshRate(int monitor)
+//Vector2 GetMonitorPosition(int monitor)
+//const char *GetMonitorName(int monitor)
+
+//void SetClipboardText(const char *text)
+//const char *GetClipboardText(void)
+
+//void ShowCursor(void)
+//void HideCursor(void)
+//void EnableCursor(void)
+//void DisableCursor(void)
+
+// Initialize window and OpenGL context
+void InitWindow(int width, int height, const char *title)
+{
+    TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION);
+
+#if defined(PLATFORM_DESKTOP_GLFW)
+    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)");
+#elif defined(PLATFORM_DESKTOP_SDL)
+    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)");
+#elif defined(PLATFORM_DESKTOP_RGFW)
+    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (RGFW)");
+#elif defined(PLATFORM_DESKTOP_WIN32)
+    TRACELOG(LOG_INFO, "Platform backend: DESKTOP (WIN32)");
+#elif defined(PLATFORM_WEB_RGFW)
+    TRACELOG(LOG_INFO, "Platform backend: WEB (RGFW) (HTML5)");
+#elif defined(PLATFORM_WEB)
+    TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)");
+#elif defined(PLATFORM_DRM)
+    TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM");
+#elif defined(PLATFORM_ANDROID)
+    TRACELOG(LOG_INFO, "Platform backend: ANDROID");
+#elif defined(PLATFORM_MEMORY)
+    TRACELOG(LOG_INFO, "Platform backend: MEMORY (No OS)");
+#else
+    // TODO: Include your custom platform backend!
+    // i.e software rendering backend or console backend!
+    TRACELOG(LOG_INFO, "Platform backend: CUSTOM");
+#endif
+
+    TRACELOG(LOG_INFO, "Supported raylib modules:");
+    TRACELOG(LOG_INFO, "    > rcore:..... loaded (mandatory)");
+    TRACELOG(LOG_INFO, "    > rlgl:...... loaded (mandatory)");
+#if defined(SUPPORT_MODULE_RSHAPES)
+    TRACELOG(LOG_INFO, "    > rshapes:... loaded (optional)");
+#else
+    TRACELOG(LOG_INFO, "    > rshapes:... not loaded (optional)");
+#endif
+#if defined(SUPPORT_MODULE_RTEXTURES)
+    TRACELOG(LOG_INFO, "    > rtextures:. loaded (optional)");
+#else
+    TRACELOG(LOG_INFO, "    > rtextures:. not loaded (optional)");
+#endif
+#if defined(SUPPORT_MODULE_RTEXT)
+    TRACELOG(LOG_INFO, "    > rtext:..... loaded (optional)");
+#else
+    TRACELOG(LOG_INFO, "    > rtext:..... not loaded (optional)");
+#endif
+#if defined(SUPPORT_MODULE_RMODELS)
+    TRACELOG(LOG_INFO, "    > rmodels:... loaded (optional)");
+#else
+    TRACELOG(LOG_INFO, "    > rmodels:... not loaded (optional)");
+#endif
+#if defined(SUPPORT_MODULE_RAUDIO)
+    TRACELOG(LOG_INFO, "    > raudio:.... loaded (optional)");
+#else
+    TRACELOG(LOG_INFO, "    > raudio:.... not loaded (optional)");
+#endif
+
+    // Initialize window data
+    CORE.Window.screen.width = width;
+    CORE.Window.screen.height = height;
+    CORE.Window.currentFbo.width = CORE.Window.screen.width;
+    CORE.Window.currentFbo.height = CORE.Window.screen.height;
+
+    CORE.Window.eventWaiting = false;
+    CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default
+    if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title;
+
+    // Initialize global input state
+    memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0
+    CORE.Input.Keyboard.exitKey = KEY_ESCAPE;
+    CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
+    CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
+    CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
+
+    // Initialize platform
+    //--------------------------------------------------------------
+    int result = InitPlatform();
+
+    if (result != 0)
+    {
+        TRACELOG(LOG_WARNING, "SYSTEM: Failed to initialize platform");
+        return;
+    }
+    //--------------------------------------------------------------
+
+    // Initialize rlgl default data (buffers and shaders)
+    // NOTE: Current fbo size stored as globals in rlgl for convenience
+    rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+
+    // Setup default viewport
+    SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
+
+#if defined(SUPPORT_MODULE_RTEXT)
+    #if defined(SUPPORT_DEFAULT_FONT)
+        // Load default font
+        // WARNING: External function: Module required: rtext
+        LoadFontDefault();
+        #if defined(SUPPORT_MODULE_RSHAPES)
+        // Set font white rectangle for shapes drawing, so shapes and text can be batched together
+        // WARNING: rshapes module is required, if not available, default internal white rectangle is used
+        Rectangle rec = GetFontDefault().recs[95];
+        if (FLAG_IS_SET(CORE.Window.flags, FLAG_MSAA_4X_HINT))
+        {
+            // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering
+            SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 });
+        }
+        else
+        {
+            // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding
+            SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 });
+        }
+        #endif
+    #endif
+#else
+    #if defined(SUPPORT_MODULE_RSHAPES)
+    // Set default texture and rectangle to be used for shapes drawing
+    // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
+    Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
+    SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f });    // WARNING: Module required: rshapes
+    #endif
+#endif
+
+    CORE.Time.frameCounter = 0;
+    CORE.Window.shouldClose = false;
+
+    // Initialize random seed
+    SetRandomSeed((unsigned int)time(NULL));
+
+    TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", GetWorkingDirectory());
+}
+
+// Close window and unload OpenGL context
+void CloseWindow(void)
+{
+#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
+    UnloadFontDefault();        // WARNING: Module required: rtext
+#endif
+
+    rlglClose();                // De-init rlgl
+
+    // De-initialize platform
+    //--------------------------------------------------------------
+    ClosePlatform();
+    //--------------------------------------------------------------
+
+    CORE.Window.ready = false;
+    TRACELOG(LOG_INFO, "Window closed successfully");
+}
+
+// Check if window has been initialized successfully
+bool IsWindowReady(void)
+{
+    return CORE.Window.ready;
+}
+
+// Check if window is currently fullscreen
+bool IsWindowFullscreen(void)
+{
+    return FLAG_IS_SET(CORE.Window.flags, FLAG_FULLSCREEN_MODE);
+}
+
+// Check if window is currently hidden
+bool IsWindowHidden(void)
+{
+    return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIDDEN);
+}
+
+// Check if window has been minimized
+bool IsWindowMinimized(void)
+{
+    return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MINIMIZED);
+}
+
+// Check if window has been maximized
+bool IsWindowMaximized(void)
+{
+    return FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_MAXIMIZED);
+}
+
+// Check if window has the focus
+bool IsWindowFocused(void)
+{
+    return !FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_UNFOCUSED);
+}
+
+// Check if window has been resizedLastFrame
+bool IsWindowResized(void)
+{
+    return CORE.Window.resizedLastFrame;
+}
+
+// Check if one specific window flag is enabled
+bool IsWindowState(unsigned int flag)
+{
+    return FLAG_IS_SET(CORE.Window.flags, flag);
+}
+
+// Get current screen width
+int GetScreenWidth(void)
+{
+    return CORE.Window.screen.width;
+}
+
+// Get current screen height
+int GetScreenHeight(void)
+{
+    return CORE.Window.screen.height;
+}
+
+// Get current render width which is equal to screen width*dpi scale
+int GetRenderWidth(void)
+{
+    int width = 0;
+
+    if (CORE.Window.usingFbo) return CORE.Window.currentFbo.width;
+    else width = CORE.Window.render.width;
+
+    return width;
+}
+
+// Get current screen height which is equal to screen height*dpi scale
+int GetRenderHeight(void)
+{
+    int height = 0;
+
+    if (CORE.Window.usingFbo) return CORE.Window.currentFbo.height;
+    else height = CORE.Window.render.height;
+
+    return height;
+}
+
+// Enable waiting for events on EndDrawing(), no automatic event polling
+void EnableEventWaiting(void)
+{
+    CORE.Window.eventWaiting = true;
+}
+
+// Disable waiting for events on EndDrawing(), automatic events polling
+void DisableEventWaiting(void)
+{
+    CORE.Window.eventWaiting = false;
+}
+
+// Check if cursor is not visible
+bool IsCursorHidden(void)
+{
+    return CORE.Input.Mouse.cursorHidden;
+}
+
+// Check if cursor is on the current screen
+bool IsCursorOnScreen(void)
+{
+    return CORE.Input.Mouse.cursorOnScreen;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Screen Drawing
+//----------------------------------------------------------------------------------
+
+// Set background color (framebuffer clear color)
+void ClearBackground(Color color)
+{
+    rlClearColor(color.r, color.g, color.b, color.a);   // Set clear color
+    rlClearScreenBuffers();                             // Clear current framebuffers
+}
+
+// Setup canvas (framebuffer) to start drawing
+void BeginDrawing(void)
+{
+    // WARNING: Previously to BeginDrawing() other render textures drawing could happen,
+    // consequently the measure for update vs draw is not accurate (only the total frame time is accurate)
+
+    CORE.Time.current = GetTime();      // Number of elapsed seconds since InitTimer()
+    CORE.Time.update = CORE.Time.current - CORE.Time.previous;
+    CORE.Time.previous = CORE.Time.current;
+
+    rlLoadIdentity();                   // Reset current matrix (modelview)
+    rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling
+
+    //rlTranslatef(0.375, 0.375, 0);    // HACK to have 2D pixel-perfect drawing on OpenGL 1.1
+                                        // NOTE: Not required with OpenGL 3.3+
+}
+
+// End canvas drawing and swap buffers (double buffering)
+void EndDrawing(void)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    if (automationEventRecording) RecordAutomationEvent();    // Event recording
+#endif
+
+#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
+    SwapScreenBuffer();                  // Copy back buffer to front buffer (screen)
+
+    // Frame time control system
+    CORE.Time.current = GetTime();
+    CORE.Time.draw = CORE.Time.current - CORE.Time.previous;
+    CORE.Time.previous = CORE.Time.current;
+
+    CORE.Time.frame = CORE.Time.update + CORE.Time.draw;
+
+    // Wait for some milliseconds...
+    if (CORE.Time.frame < CORE.Time.target)
+    {
+        WaitTime(CORE.Time.target - CORE.Time.frame);
+
+        CORE.Time.current = GetTime();
+        double waitTime = CORE.Time.current - CORE.Time.previous;
+        CORE.Time.previous = CORE.Time.current;
+
+        CORE.Time.frame += waitTime;    // Total frame time: update + draw + wait
+    }
+
+    PollInputEvents();      // Poll user events (before next frame update)
+#endif
+
+#if defined(SUPPORT_SCREEN_CAPTURE)
+    if (IsKeyPressed(KEY_F12))
+    {
+        TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
+        screenshotCounter++;
+    }
+#endif  // SUPPORT_SCREEN_CAPTURE
+
+    CORE.Time.frameCounter++;
+}
+
+// Initialize 2D mode with custom camera (2D)
+void BeginMode2D(Camera2D camera)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlLoadIdentity();               // Reset current matrix (modelview)
+
+    // Apply 2d camera transformation to modelview
+    rlMultMatrixf(MatrixToFloat(GetCameraMatrix2D(camera)));
+}
+
+// Ends 2D mode with custom camera
+void EndMode2D(void)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlLoadIdentity();               // Reset current matrix (modelview)
+
+    if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
+}
+
+// Initializes 3D mode with custom camera (3D)
+void BeginMode3D(Camera camera)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
+    rlPushMatrix();                 // Save previous matrix, which contains the settings for the 2d ortho projection
+    rlLoadIdentity();               // Reset current matrix (projection)
+
+    float aspect = (float)CORE.Window.currentFbo.width/(float)CORE.Window.currentFbo.height;
+
+    // NOTE: zNear and zFar values are important when computing depth buffer values
+    if (camera.projection == CAMERA_PERSPECTIVE)
+    {
+        // Setup perspective projection
+        double top = rlGetCullDistanceNear()*tan(camera.fovy*0.5*DEG2RAD);
+        double right = top*aspect;
+
+        rlFrustum(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
+    {
+        // Setup orthographic projection
+        double top = camera.fovy/2.0;
+        double right = top*aspect;
+
+        rlOrtho(-right, right, -top,top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+
+    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
+    rlLoadIdentity();               // Reset current matrix (modelview)
+
+    // Setup Camera view
+    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
+    rlMultMatrixf(MatrixToFloat(matView));      // Multiply modelview matrix by view matrix (camera)
+
+    rlEnableDepthTest();            // Enable DEPTH_TEST for 3D
+}
+
+// Ends 3D mode and returns to default 2D orthographic mode
+void EndMode3D(void)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
+    rlPopMatrix();                  // Restore previous matrix (projection) from matrix stack
+
+    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
+    rlLoadIdentity();               // Reset current matrix (modelview)
+
+    if (rlGetActiveFramebuffer() == 0) rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
+
+    rlDisableDepthTest();           // Disable DEPTH_TEST for 2D
+}
+
+// Initializes render texture for drawing
+void BeginTextureMode(RenderTexture2D target)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlEnableFramebuffer(target.id); // Enable render target
+
+    // Set viewport and RLGL internal framebuffer size
+    rlViewport(0, 0, target.texture.width, target.texture.height);
+    rlSetFramebufferWidth(target.texture.width);
+    rlSetFramebufferHeight(target.texture.height);
+
+    rlMatrixMode(RL_PROJECTION);    // Switch to projection matrix
+    rlLoadIdentity();               // Reset current matrix (projection)
+
+    // Set orthographic projection to current framebuffer size
+    // NOTE: Configured top-left corner as (0, 0)
+    rlOrtho(0, target.texture.width, target.texture.height, 0, 0.0f, 1.0f);
+
+    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
+    rlLoadIdentity();               // Reset current matrix (modelview)
+
+    //rlScalef(0.0f, -1.0f, 0.0f);  // Flip Y-drawing (?)
+
+    // Setup current width/height for proper aspect ratio
+    // calculation when using BeginTextureMode()
+    CORE.Window.currentFbo.width = target.texture.width;
+    CORE.Window.currentFbo.height = target.texture.height;
+    CORE.Window.usingFbo = true;
+}
+
+// Ends drawing to render texture
+void EndTextureMode(void)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlDisableFramebuffer();         // Disable render target (fbo)
+
+    // Set viewport to default framebuffer size
+    SetupViewport(CORE.Window.render.width, CORE.Window.render.height);
+
+    // Go back to the modelview state from BeginDrawing since we are back to the default FBO
+    rlMatrixMode(RL_MODELVIEW);     // Switch back to modelview matrix
+    rlLoadIdentity();               // Reset current matrix (modelview)
+    rlMultMatrixf(MatrixToFloat(CORE.Window.screenScale)); // Apply screen scaling if required
+
+    // Reset current fbo to screen size
+    CORE.Window.currentFbo.width = CORE.Window.render.width;
+    CORE.Window.currentFbo.height = CORE.Window.render.height;
+    CORE.Window.usingFbo = false;
+}
+
+// Begin custom shader mode
+void BeginShaderMode(Shader shader)
+{
+    rlSetShader(shader.id, shader.locs);
+}
+
+// End custom shader mode (returns to default shader)
+void EndShaderMode(void)
+{
+    rlSetShader(rlGetShaderIdDefault(), rlGetShaderLocsDefault());
+}
+
+// Begin blending mode (alpha, additive, multiplied, subtract, custom)
+// NOTE: Blend modes supported are enumerated in BlendMode enum
+void BeginBlendMode(int mode)
+{
+    rlSetBlendMode(mode);
+}
+
+// End blending mode (reset to default: alpha blending)
+void EndBlendMode(void)
+{
+    rlSetBlendMode(BLEND_ALPHA);
+}
+
+// Begin scissor mode (define screen area for following drawing)
+// NOTE: Scissor rec refers to bottom-left corner, we change it to upper-left
+void BeginScissorMode(int x, int y, int width, int height)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+
+    rlEnableScissorTest();
+
+#if defined(__APPLE__)
+    if (!CORE.Window.usingFbo)
+    {
+        Vector2 scale = GetWindowScaleDPI();
+        rlScissor((int)(x*scale.x), (int)(GetScreenHeight()*scale.y - (((y + height)*scale.y))), (int)(width*scale.x), (int)(height*scale.y));
+    }
+#else
+    if (!CORE.Window.usingFbo && FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI))
+    {
+        Vector2 scale = GetWindowScaleDPI();
+        rlScissor((int)(x*scale.x), (int)(CORE.Window.currentFbo.height - (y + height)*scale.y), (int)(width*scale.x), (int)(height*scale.y));
+    }
+#endif
+    else
+    {
+        rlScissor(x, CORE.Window.currentFbo.height - (y + height), width, height);
+    }
+}
+
+// End scissor mode
+void EndScissorMode(void)
+{
+    rlDrawRenderBatchActive();      // Update and draw internal render batch
+    rlDisableScissorTest();
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: VR Stereo Rendering
+//----------------------------------------------------------------------------------
+
+// Begin VR drawing configuration
+void BeginVrStereoMode(VrStereoConfig config)
+{
+    rlEnableStereoRender();
+
+    // Set stereo render matrices
+    rlSetMatrixProjectionStereo(config.projection[0], config.projection[1]);
+    rlSetMatrixViewOffsetStereo(config.viewOffset[0], config.viewOffset[1]);
+}
+
+// End VR drawing process (and desktop mirror)
+void EndVrStereoMode(void)
+{
+    rlDisableStereoRender();
+}
+
+// Load VR stereo config for VR simulator device parameters
+VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device)
+{
+    VrStereoConfig config = { 0 };
+
+    if (rlGetVersion() != RL_OPENGL_11)
+    {
+        // Compute aspect ratio
+        float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution;
+
+        // Compute lens parameters
+        float lensShift = (device.hScreenSize*0.25f - device.lensSeparationDistance*0.5f)/device.hScreenSize;
+        config.leftLensCenter[0] = 0.25f + lensShift;
+        config.leftLensCenter[1] = 0.5f;
+        config.rightLensCenter[0] = 0.75f - lensShift;
+        config.rightLensCenter[1] = 0.5f;
+        config.leftScreenCenter[0] = 0.25f;
+        config.leftScreenCenter[1] = 0.5f;
+        config.rightScreenCenter[0] = 0.75f;
+        config.rightScreenCenter[1] = 0.5f;
+
+        // Compute distortion scale parameters
+        // NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
+        float lensRadius = fabsf(-1.0f - 4.0f*lensShift);
+        float lensRadiusSq = lensRadius*lensRadius;
+        float distortionScale = device.lensDistortionValues[0] +
+                                device.lensDistortionValues[1]*lensRadiusSq +
+                                device.lensDistortionValues[2]*lensRadiusSq*lensRadiusSq +
+                                device.lensDistortionValues[3]*lensRadiusSq*lensRadiusSq*lensRadiusSq;
+
+        float normScreenWidth = 0.5f;
+        float normScreenHeight = 1.0f;
+        config.scaleIn[0] = 2.0f/normScreenWidth;
+        config.scaleIn[1] = 2.0f/normScreenHeight/aspect;
+        config.scale[0] = normScreenWidth*0.5f/distortionScale;
+        config.scale[1] = normScreenHeight*0.5f*aspect/distortionScale;
+
+        // Fovy is normally computed with: 2*atan2f(device.vScreenSize, 2*device.eyeToScreenDistance)
+        // ...but with lens distortion it is increased (see Oculus SDK Documentation)
+        float fovy = 2.0f*atan2f(device.vScreenSize*0.5f*distortionScale, device.eyeToScreenDistance);     // Really need distortionScale?
+       // float fovy = 2.0f*(float)atan2f(device.vScreenSize*0.5f, device.eyeToScreenDistance);
+
+        // Compute camera projection matrices
+        float projOffset = 4.0f*lensShift;      // Scaled to projection space coordinates [-1..1]
+        Matrix proj = MatrixPerspective(fovy, aspect, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+
+        config.projection[0] = MatrixMultiply(proj, MatrixTranslate(projOffset, 0.0f, 0.0f));
+        config.projection[1] = MatrixMultiply(proj, MatrixTranslate(-projOffset, 0.0f, 0.0f));
+
+        // Compute camera transformation matrices
+        // NOTE: Camera movement might seem more natural if we model the head
+        // Our axis of rotation is the base of our head, so we might want to add
+        // some y (base of head to eye level) and -z (center of head to eye protrusion) to the camera positions
+        config.viewOffset[0] = MatrixTranslate(device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
+        config.viewOffset[1] = MatrixTranslate(-device.interpupillaryDistance*0.5f, 0.075f, 0.045f);
+
+        // Compute eyes Viewports
+        /*
+        config.eyeViewportRight[0] = 0;
+        config.eyeViewportRight[1] = 0;
+        config.eyeViewportRight[2] = device.hResolution/2;
+        config.eyeViewportRight[3] = device.vResolution;
+
+        config.eyeViewportLeft[0] = device.hResolution/2;
+        config.eyeViewportLeft[1] = 0;
+        config.eyeViewportLeft[2] = device.hResolution/2;
+        config.eyeViewportLeft[3] = device.vResolution;
+        */
+    }
+    else TRACELOG(LOG_WARNING, "RLGL: VR Simulator not supported on OpenGL 1.1");
+
+    return config;
+}
+
+// Unload VR stereo config properties
+void UnloadVrStereoConfig(VrStereoConfig config)
+{
+    TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c");
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Shaders Management
+//----------------------------------------------------------------------------------
+
+// Load shader from files and bind default locations
+// NOTE: If shader string is NULL, using default vertex/fragment shaders
+Shader LoadShader(const char *vsFileName, const char *fsFileName)
+{
+    Shader shader = { 0 };
+
+    char *vShaderStr = NULL;
+    char *fShaderStr = NULL;
+
+    if (vsFileName != NULL) vShaderStr = LoadFileText(vsFileName);
+    if (fsFileName != NULL) fShaderStr = LoadFileText(fsFileName);
+
+    if ((vShaderStr == NULL) && (fShaderStr == NULL)) TRACELOG(LOG_WARNING, "SHADER: Shader files provided are not valid, using default shader");
+
+    shader = LoadShaderFromMemory(vShaderStr, fShaderStr);
+
+    UnloadFileText(vShaderStr);
+    UnloadFileText(fShaderStr);
+
+    return shader;
+}
+
+// Load shader from code strings and bind default locations
+Shader LoadShaderFromMemory(const char *vsCode, const char *fsCode)
+{
+    Shader shader = { 0 };
+
+    shader.id = rlLoadShaderCode(vsCode, fsCode);
+
+    if (shader.id == 0)
+    {
+        // Shader could not be loaded but we still load the location points to avoid potential crashes
+        // NOTE: All locations set to -1 (no location)
+        shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
+        for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
+    }
+    else if (shader.id == rlGetShaderIdDefault()) shader.locs = rlGetShaderLocsDefault();
+    else if (shader.id > 0)
+    {
+        // After custom shader loading, we TRY to set default location names
+        // Default shader attribute locations have been binded before linking:
+        //  - vertex position location    = 0
+        //  - vertex texcoord location    = 1
+        //  - vertex normal location      = 2
+        //  - vertex color location       = 3
+        //  - vertex tangent location     = 4
+        //  - vertex texcoord2 location   = 5
+        //  - vertex boneIds location     = 6
+        //  - vertex boneWeights location = 7
+
+        // NOTE: If any location is not found, loc point becomes -1
+
+        // Load shader locations array
+        // NOTE: All locations set to -1 (no location)
+        shader.locs = (int *)RL_CALLOC(RL_MAX_SHADER_LOCATIONS, sizeof(int));
+        for (int i = 0; i < RL_MAX_SHADER_LOCATIONS; i++) shader.locs[i] = -1;
+
+        // Get handles to GLSL input attribute locations
+        shader.locs[SHADER_LOC_VERTEX_POSITION] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
+        shader.locs[SHADER_LOC_VERTEX_TEXCOORD01] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
+        shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
+        shader.locs[SHADER_LOC_VERTEX_NORMAL] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
+        shader.locs[SHADER_LOC_VERTEX_TANGENT] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
+        shader.locs[SHADER_LOC_VERTEX_COLOR] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
+        shader.locs[SHADER_LOC_VERTEX_BONEIDS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+        shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
+        shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] = rlGetLocationAttrib(shader.id, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
+
+        // Get handles to GLSL uniform locations (vertex shader)
+        shader.locs[SHADER_LOC_MATRIX_MVP] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
+        shader.locs[SHADER_LOC_MATRIX_VIEW] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW);
+        shader.locs[SHADER_LOC_MATRIX_PROJECTION] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION);
+        shader.locs[SHADER_LOC_MATRIX_MODEL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL);
+        shader.locs[SHADER_LOC_MATRIX_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL);
+        shader.locs[SHADER_LOC_BONE_MATRICES] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_BONE_MATRICES);
+
+        // Get handles to GLSL uniform locations (fragment shader)
+        shader.locs[SHADER_LOC_COLOR_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
+        shader.locs[SHADER_LOC_MAP_DIFFUSE] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);  // SHADER_LOC_MAP_ALBEDO
+        shader.locs[SHADER_LOC_MAP_SPECULAR] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1); // SHADER_LOC_MAP_METALNESS
+        shader.locs[SHADER_LOC_MAP_NORMAL] = rlGetLocationUniform(shader.id, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2);
+    }
+
+    return shader;
+}
+
+// Check if a shader is valid (loaded on GPU)
+bool IsShaderValid(Shader shader)
+{
+    return ((shader.id > 0) &&          // Validate shader id (GPU loaded successfully)
+            (shader.locs != NULL));     // Validate memory has been allocated for default shader locations
+
+    // The following locations are tried to be set automatically (locs[i] >= 0),
+    // any of them can be checked for validation but the only mandatory one is, afaik, SHADER_LOC_VERTEX_POSITION
+    // NOTE: Users can also setup manually their own attributes/uniforms and do not used the default raylib ones
+
+    // Vertex shader attribute locations (default)
+    // shader.locs[SHADER_LOC_VERTEX_POSITION]      // Set by default internal shader
+    // shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]    // Set by default internal shader
+    // shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]
+    // shader.locs[SHADER_LOC_VERTEX_NORMAL]
+    // shader.locs[SHADER_LOC_VERTEX_TANGENT]
+    // shader.locs[SHADER_LOC_VERTEX_COLOR]         // Set by default internal shader
+
+    // Vertex shader uniform locations (default)
+    // shader.locs[SHADER_LOC_MATRIX_MVP]           // Set by default internal shader
+    // shader.locs[SHADER_LOC_MATRIX_VIEW]
+    // shader.locs[SHADER_LOC_MATRIX_PROJECTION]
+    // shader.locs[SHADER_LOC_MATRIX_MODEL]
+    // shader.locs[SHADER_LOC_MATRIX_NORMAL]
+
+    // Fragment shader uniform locations (default)
+    // shader.locs[SHADER_LOC_COLOR_DIFFUSE]        // Set by default internal shader
+    // shader.locs[SHADER_LOC_MAP_DIFFUSE]          // Set by default internal shader
+    // shader.locs[SHADER_LOC_MAP_SPECULAR]
+    // shader.locs[SHADER_LOC_MAP_NORMAL]
+}
+
+// Unload shader from GPU memory (VRAM)
+void UnloadShader(Shader shader)
+{
+    if (shader.id != rlGetShaderIdDefault())
+    {
+        rlUnloadShaderProgram(shader.id);
+
+        // NOTE: If shader loading failed, it should be 0
+        RL_FREE(shader.locs);
+    }
+}
+
+// Get shader uniform location
+int GetShaderLocation(Shader shader, const char *uniformName)
+{
+    return rlGetLocationUniform(shader.id, uniformName);
+}
+
+// Get shader attribute location
+int GetShaderLocationAttrib(Shader shader, const char *attribName)
+{
+    return rlGetLocationAttrib(shader.id, attribName);
+}
+
+// Set shader uniform value
+void SetShaderValue(Shader shader, int locIndex, const void *value, int uniformType)
+{
+    SetShaderValueV(shader, locIndex, value, uniformType, 1);
+}
+
+// Set shader uniform value vector
+void SetShaderValueV(Shader shader, int locIndex, const void *value, int uniformType, int count)
+{
+    if (locIndex > -1)
+    {
+        rlEnableShader(shader.id);
+        rlSetUniform(locIndex, value, uniformType, count);
+        //rlDisableShader();      // Avoid resetting current shader program, in case other uniforms are set
+    }
+}
+
+// Set shader uniform value (matrix 4x4)
+void SetShaderValueMatrix(Shader shader, int locIndex, Matrix mat)
+{
+    if (locIndex > -1)
+    {
+        rlEnableShader(shader.id);
+        rlSetUniformMatrix(locIndex, mat);
+        //rlDisableShader();
+    }
+}
+
+// Set shader uniform value for texture
+void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture)
+{
+    if (locIndex > -1)
+    {
+        rlEnableShader(shader.id);
+        rlSetUniformSampler(locIndex, texture.id);
+        //rlDisableShader();
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Screen-space Queries
+//----------------------------------------------------------------------------------
+
+// Get a ray trace from screen position (i.e mouse)
+Ray GetScreenToWorldRay(Vector2 position, Camera camera)
+{
+    Ray ray = GetScreenToWorldRayEx(position, camera, GetScreenWidth(), GetScreenHeight());
+
+    return ray;
+}
+
+// Get a ray trace from the screen position (i.e mouse) within a specific section of the screen
+Ray GetScreenToWorldRayEx(Vector2 position, Camera camera, int width, int height)
+{
+    Ray ray = { 0 };
+
+    // Calculate normalized device coordinates
+    // NOTE: y value is negative
+    float x = (2.0f*position.x)/(float)width - 1.0f;
+    float y = 1.0f - (2.0f*position.y)/(float)height;
+    float z = 1.0f;
+
+    // Store values in a vector
+    Vector3 deviceCoords = { x, y, z };
+
+    // Calculate view matrix from camera look at
+    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
+
+    Matrix matProj = MatrixIdentity();
+
+    if (camera.projection == CAMERA_PERSPECTIVE)
+    {
+        // Calculate projection matrix from perspective
+        matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
+    {
+        double aspect = (double)width/(double)height;
+        double top = camera.fovy/2.0;
+        double right = top*aspect;
+
+        // Calculate projection matrix from orthographic
+        matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+
+    // Unproject far/near points
+    Vector3 nearPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
+    Vector3 farPoint = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
+
+    // Unproject the mouse cursor in the near plane
+    // We need this as the source position because orthographic projects,
+    // compared to perspective doesn't have a convergence point,
+    // meaning that the "eye" of the camera is more like a plane than a point
+    Vector3 cameraPlanePointerPos = Vector3Unproject((Vector3){ deviceCoords.x, deviceCoords.y, -1.0f }, matProj, matView);
+
+    // Calculate normalized direction vector
+    Vector3 direction = Vector3Normalize(Vector3Subtract(farPoint, nearPoint));
+
+    if (camera.projection == CAMERA_PERSPECTIVE) ray.position = camera.position;
+    else if (camera.projection == CAMERA_ORTHOGRAPHIC) ray.position = cameraPlanePointerPos;
+
+    // Apply calculated vectors to ray
+    ray.direction = direction;
+
+    return ray;
+}
+
+// Get transform matrix for camera
+Matrix GetCameraMatrix(Camera camera)
+{
+    Matrix mat = MatrixLookAt(camera.position, camera.target, camera.up);
+
+    return mat;
+}
+
+// Get camera 2d transform matrix
+Matrix GetCameraMatrix2D(Camera2D camera)
+{
+    Matrix matTransform = { 0 };
+    // The camera in world-space is set by
+    //   1. Move it to target
+    //   2. Rotate by -rotation and scale by (1/zoom)
+    //      When setting higher scale, it's more intuitive for the world to become bigger (= camera become smaller),
+    //      not for the camera getting bigger, hence the invert. Same deal with rotation
+    //   3. Move it by (-offset);
+    //      Offset defines target transform relative to screen, but since we're effectively "moving" screen (camera)
+    //      we need to do it into opposite direction (inverse transform)
+
+    // Having camera transform in world-space, inverse of it gives the modelview transform
+    // Since (A*B*C)' = C'*B'*A', the modelview is
+    //   1. Move to offset
+    //   2. Rotate and Scale
+    //   3. Move by -target
+    Matrix matOrigin = MatrixTranslate(-camera.target.x, -camera.target.y, 0.0f);
+    Matrix matRotation = MatrixRotate((Vector3){ 0.0f, 0.0f, 1.0f }, camera.rotation*DEG2RAD);
+    Matrix matScale = MatrixScale(camera.zoom, camera.zoom, 1.0f);
+    Matrix matTranslation = MatrixTranslate(camera.offset.x, camera.offset.y, 0.0f);
+
+    matTransform = MatrixMultiply(MatrixMultiply(matOrigin, MatrixMultiply(matScale, matRotation)), matTranslation);
+
+    return matTransform;
+}
+
+// Get the screen space position from a 3d world space position
+Vector2 GetWorldToScreen(Vector3 position, Camera camera)
+{
+    Vector2 screenPosition = GetWorldToScreenEx(position, camera, GetScreenWidth(), GetScreenHeight());
+
+    return screenPosition;
+}
+
+// Get size position for a 3d world space position (useful for texture drawing)
+Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int height)
+{
+    // Calculate projection matrix (from perspective instead of frustum
+    Matrix matProj = MatrixIdentity();
+
+    if (camera.projection == CAMERA_PERSPECTIVE)
+    {
+        // Calculate projection matrix from perspective
+        matProj = MatrixPerspective(camera.fovy*DEG2RAD, ((double)width/(double)height), rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+    else if (camera.projection == CAMERA_ORTHOGRAPHIC)
+    {
+        double aspect = (double)width/(double)height;
+        double top = camera.fovy/2.0;
+        double right = top*aspect;
+
+        // Calculate projection matrix from orthographic
+        matProj = MatrixOrtho(-right, right, -top, top, rlGetCullDistanceNear(), rlGetCullDistanceFar());
+    }
+
+    // Calculate view matrix from camera look at (and transpose it)
+    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
+
+    // Convert world position vector to quaternion
+    Quaternion worldPos = { position.x, position.y, position.z, 1.0f };
+
+    // Transform world position to view
+    worldPos = QuaternionTransform(worldPos, matView);
+
+    // Transform result to projection (clip space position)
+    worldPos = QuaternionTransform(worldPos, matProj);
+
+    // Calculate normalized device coordinates (inverted y)
+    Vector3 ndcPos = { worldPos.x/worldPos.w, -worldPos.y/worldPos.w, worldPos.z/worldPos.w };
+
+    // Calculate 2d screen position vector
+    Vector2 screenPosition = { (ndcPos.x + 1.0f)/2.0f*(float)width, (ndcPos.y + 1.0f)/2.0f*(float)height };
+
+    return screenPosition;
+}
+
+// Get the screen space position for a 2d camera world space position
+Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera)
+{
+    Matrix matCamera = GetCameraMatrix2D(camera);
+    Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, matCamera);
+
+    return (Vector2){ transform.x, transform.y };
+}
+
+// Get the world space position for a 2d camera screen space position
+Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera)
+{
+    Matrix invMatCamera = MatrixInvert(GetCameraMatrix2D(camera));
+    Vector3 transform = Vector3Transform((Vector3){ position.x, position.y, 0 }, invMatCamera);
+
+    return (Vector2){ transform.x, transform.y };
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Timming
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//double GetTime(void)
+
+// Set target FPS (maximum)
+void SetTargetFPS(int fps)
+{
+    if (fps < 1) CORE.Time.target = 0.0;
+    else CORE.Time.target = 1.0/(double)fps;
+
+    TRACELOG(LOG_INFO, "TIMER: Target time per frame: %02.03f milliseconds", (float)CORE.Time.target*1000.0f);
+}
+
+// Get current FPS
+// NOTE: We calculate an average framerate
+int GetFPS(void)
+{
+    int fps = 0;
+
+#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
+    #define FPS_CAPTURE_FRAMES_COUNT    30      // 30 captures
+    #define FPS_AVERAGE_TIME_SECONDS   0.5f     // 500 milliseconds
+    #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT)
+
+    static int index = 0;
+    static float history[FPS_CAPTURE_FRAMES_COUNT] = { 0 };
+    static float average = 0, last = 0;
+    float fpsFrame = GetFrameTime();
+
+    // if we reset the window, reset the FPS info
+    if (CORE.Time.frameCounter == 0)
+    {
+        average = 0;
+        last = 0;
+        index = 0;
+
+        for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) history[i] = 0;
+    }
+
+    if (fpsFrame == 0) return 0;
+
+    if ((GetTime() - last) > FPS_STEP)
+    {
+        last = (float)GetTime();
+        index = (index + 1)%FPS_CAPTURE_FRAMES_COUNT;
+        average -= history[index];
+        history[index] = fpsFrame/FPS_CAPTURE_FRAMES_COUNT;
+        average += history[index];
+    }
+
+    fps = (int)roundf(1.0f/average);
+#endif
+
+    return fps;
+}
+
+// Get time in seconds for last frame drawn (delta time)
+float GetFrameTime(void)
+{
+    return (float)CORE.Time.frame;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Custom frame control
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//void SwapScreenBuffer(void);
+//void PollInputEvents(void);
+
+// Wait for some time (stop program execution)
+// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could
+// take longer than expected... for that reason we use the busy wait loop
+// REF: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected
+// REF: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32!
+void WaitTime(double seconds)
+{
+    if (seconds < 0) return;    // Security check
+
+#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
+    double destinationTime = GetTime() + seconds;
+#endif
+
+#if defined(SUPPORT_BUSY_WAIT_LOOP)
+    while (GetTime() < destinationTime) { }
+#else
+    #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
+        double sleepSeconds = seconds - seconds*0.05;  // NOTE: We reserve a percentage of the time for busy waiting
+    #else
+        double sleepSeconds = seconds;
+    #endif
+
+    // System halt functions
+    #if defined(_WIN32)
+        Sleep((unsigned long)(sleepSeconds*1000.0));
+    #endif
+    #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
+        struct timespec req = { 0 };
+        time_t sec = sleepSeconds;
+        long nsec = (sleepSeconds - sec)*1000000000L;
+        req.tv_sec = sec;
+        req.tv_nsec = nsec;
+
+        // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated
+        while (nanosleep(&req, &req) == -1) continue;
+    #endif
+    #if defined(__APPLE__)
+        usleep(sleepSeconds*1000000.0);
+    #endif
+
+    #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
+        while (GetTime() < destinationTime) { }
+    #endif
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Misc
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//void OpenURL(const char *url)
+
+// Set the seed for the random number generator
+void SetRandomSeed(unsigned int seed)
+{
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    rprand_set_seed(seed);
+#else
+    srand(seed);
+#endif
+}
+
+// Get a random value between min and max included
+int GetRandomValue(int min, int max)
+{
+    int value = 0;
+
+    if (min > max)
+    {
+        int tmp = max;
+        max = min;
+        min = tmp;
+    }
+
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    value = rprand_get_value(min, max);
+#else
+    // WARNING: Ranges higher than RAND_MAX will return invalid results
+    // More specifically, if (max - min) > INT_MAX there will be an overflow,
+    // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold
+    // NOTE: Depending on the library it can be as low as 32767
+    if ((unsigned int)(max - min) > (unsigned int)RAND_MAX)
+    {
+        TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX);
+    }
+
+    // NOTE: This one-line approach produces a non-uniform distribution,
+    // as stated by Donald Knuth in the book The Art of Programming, so
+    // using below approach for more uniform results
+    //value = (rand()%(abs(max - min) + 1) + min);
+
+    // More uniform range solution
+    int range = (max - min) + 1;
+
+    // Degenerate/overflow case: fall back to min (same behavior as "always min" instead of UB)
+    if (range <= 0) value = min;
+    else
+    {
+        // Rejection sampling to get a uniform integer in [min, max]
+        unsigned long c = (unsigned long)RAND_MAX + 1UL;  // number of possible rand() results
+        unsigned long m = (unsigned long)range;           // size of the target interval
+        unsigned long t = c - (c%m);                    // largest multiple of m <= c
+        unsigned long r = 0;
+
+        for (;;)
+        {
+            r = (unsigned long)rand();
+            if (r < t) break;   // Only accept values within the fair region
+        }
+
+        value = min + (int)(r%m);
+    }
+#endif
+    return value;
+}
+
+// Load random values sequence, no values repeated, min and max included
+int *LoadRandomSequence(unsigned int count, int min, int max)
+{
+    int *values = NULL;
+
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    values = rprand_load_sequence(count, min, max);
+#else
+    if (count > ((unsigned int)abs(max - min) + 1)) return values;  // Security check
+
+    values = (int *)RL_CALLOC(count, sizeof(int));
+
+    int value = 0;
+    bool dupValue = false;
+
+    for (int i = 0; i < (int)count;)
+    {
+        value = GetRandomValue(min, max);
+        dupValue = false;
+
+        for (int j = 0; j < i; j++)
+        {
+            if (values[j] == value)
+            {
+                dupValue = true;
+                break;
+            }
+        }
+
+        if (!dupValue)
+        {
+            values[i] = value;
+            i++;
+        }
+    }
+#endif
+    return values;
+}
+
+// Unload random values sequence
+void UnloadRandomSequence(int *sequence)
+{
+#if defined(SUPPORT_RPRAND_GENERATOR)
+    rprand_unload_sequence(sequence);
+#else
+    RL_FREE(sequence);
+#endif
+}
+
+// Takes a screenshot of current screen
+// NOTE: Provided fileName should not contain paths, saving to working directory
+void TakeScreenshot(const char *fileName)
+{
+#if defined(SUPPORT_MODULE_RTEXTURES)
+    // Security check to (partially) avoid malicious code
+    if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; }
+
+    // Apply a scale if we are doing HIGHDPI auto-scaling
+    Vector2 scale = { 1.0f, 1.0f };
+    if (FLAG_IS_SET(CORE.Window.flags, FLAG_WINDOW_HIGHDPI)) scale = GetWindowScaleDPI();
+
+    unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
+    Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
+
+    char path[MAX_FILEPATH_LENGTH] = { 0 };
+    strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
+
+    ExportImage(image, path); // WARNING: Module required: rtextures
+    RL_FREE(imgData);
+
+    if (FileExists(path)) TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path);
+    else TRACELOG(LOG_WARNING, "SYSTEM: [%s] Screenshot could not be saved", path);
+#else
+    TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures");
+#endif
+}
+
+// Setup window configuration flags (view FLAGS)
+// NOTE: This function is expected to be called before window creation,
+// because it sets up some flags for the window creation process
+// To configure window states after creation, just use SetWindowState()
+void SetConfigFlags(unsigned int flags)
+{
+    if (CORE.Window.ready) TRACELOG(LOG_WARNING, "WINDOW: SetConfigFlags called after window initialization, Use \"SetWindowState\" to set flags instead");
+
+    // Selected flags are set but not evaluated at this point,
+    // flag evaluation happens at InitWindow() or SetWindowState()
+    FLAG_SET(CORE.Window.flags, flags);
+}
+
+// void OpenURL(const char *url);   // Defined per platform
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Logging system
+//----------------------------------------------------------------------------------
+// Set the current threshold (minimum) log level
+void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
+
+// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
+void TraceLog(int logType, const char *text, ...)
+{
+#if defined(SUPPORT_TRACELOG)
+    // Message has level below current threshold, don't emit
+    if ((logType < logTypeLevel) || (text == NULL)) return;
+
+    va_list args;
+    va_start(args, text);
+
+    if (traceLog)
+    {
+        traceLog(logType, text, args);
+        va_end(args);
+        return;
+    }
+
+#if defined(PLATFORM_ANDROID)
+    switch (logType)
+    {
+        case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break;
+        case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break;
+        case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break;
+        case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break;
+        case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break;
+        case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break;
+        default: break;
+    }
+#else
+    char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 };
+
+    switch (logType)
+    {
+        case LOG_TRACE: strncpy(buffer, "TRACE: ", 8); break;
+        case LOG_DEBUG: strncpy(buffer, "DEBUG: ", 8); break;
+        case LOG_INFO: strncpy(buffer, "INFO: ", 7); break;
+        case LOG_WARNING: strncpy(buffer, "WARNING: ", 10); break;
+        case LOG_ERROR: strncpy(buffer, "ERROR: ", 8); break;
+        case LOG_FATAL: strncpy(buffer, "FATAL: ", 8); break;
+        default: break;
+    }
+
+    unsigned int textLength = (unsigned int)strlen(text);
+    memcpy(buffer + strlen(buffer), text, (textLength < (MAX_TRACELOG_MSG_LENGTH - 12))? textLength : (MAX_TRACELOG_MSG_LENGTH - 12));
+    strcat(buffer, "\n");
+    vprintf(buffer, args);
+    fflush(stdout);
+#endif
+
+    va_end(args);
+
+    if (logType == LOG_FATAL) exit(EXIT_FAILURE);  // If fatal logging, exit program
+
+#endif  // SUPPORT_TRACELOG
+}
+
+// Set custom trace log
+void SetTraceLogCallback(TraceLogCallback callback)
+{ 
+    traceLog = callback;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Memory management
+//----------------------------------------------------------------------------------
+// Internal memory allocator
+// NOTE: Initializes to zero by default
+void *MemAlloc(unsigned int size)
+{
+    void *ptr = RL_CALLOC(size, 1);
+    return ptr;
+}
+
+// Internal memory reallocator
+void *MemRealloc(void *ptr, unsigned int size)
+{
+    void *ret = RL_REALLOC(ptr, size);
+    return ret;
+}
+
+// Internal memory free
+void MemFree(void *ptr)
+{
+    RL_FREE(ptr);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: File System management
+//----------------------------------------------------------------------------------
+// Load data from file into a buffer
+unsigned char *LoadFileData(const char *fileName, int *dataSize)
+{
+    unsigned char *data = NULL;
+    *dataSize = 0;
+
+    if (fileName != NULL)
+    {
+        if (loadFileData)
+        {
+            data = loadFileData(fileName, dataSize);
+            return data;
+        }
+#if defined(SUPPORT_STANDARD_FILEIO)
+        FILE *file = fopen(fileName, "rb");
+
+        if (file != NULL)
+        {
+            // WARNING: On binary streams SEEK_END could not be found,
+            // using fseek() and ftell() could not work in some (rare) cases
+            fseek(file, 0, SEEK_END);
+            int size = ftell(file);     // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes)
+            fseek(file, 0, SEEK_SET);
+
+            if (size > 0)
+            {
+                data = (unsigned char *)RL_CALLOC(size, sizeof(unsigned char));
+
+                if (data != NULL)
+                {
+                    // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
+                    size_t count = fread(data, sizeof(unsigned char), size, file);
+
+                    // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
+                    // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation
+                    if (count > 2147483647)
+                    {
+                        TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
+
+                        RL_FREE(data);
+                        data = NULL;
+                    }
+                    else
+                    {
+                        *dataSize = (int)count;
+
+                        if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count);
+                        else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
+                    }
+                }
+                else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
+            }
+            else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
+
+            fclose(file);
+        }
+        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
+#else
+    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
+#endif
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
+
+    return data;
+}
+
+// Unload file data allocated by LoadFileData()
+void UnloadFileData(unsigned char *data)
+{
+    RL_FREE(data);
+}
+
+// Save data to file from buffer
+bool SaveFileData(const char *fileName, void *data, int dataSize)
+{
+    bool success = false;
+
+    if (fileName != NULL)
+    {
+        if (saveFileData)
+        {
+            return saveFileData(fileName, data, dataSize);
+        }
+#if defined(SUPPORT_STANDARD_FILEIO)
+        FILE *file = fopen(fileName, "wb");
+
+        if (file != NULL)
+        {
+            // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
+            // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem
+            int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file);
+
+            if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
+            else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
+            else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
+
+            int result = fclose(file);
+            if (result == 0) success = true;
+        }
+        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
+#else
+    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
+#endif
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
+
+    return success;
+}
+
+// Export data to code (.h), returns true on success
+bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
+{
+    bool success = false;
+
+#ifndef TEXT_BYTES_PER_LINE
+    #define TEXT_BYTES_PER_LINE     20
+#endif
+
+    // NOTE: Text data buffer size is estimated considering raw data size in bytes
+    // and requiring 6 char bytes for every byte: "0x00, "
+    char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
+
+    int byteCount = 0;
+    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes                  //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
+    byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2026 Ramon Santamaria (@raysan5)                                //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
+
+    // Get file name from path
+    char varFileName[256] = { 0 };
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
+    for (int i = 0; varFileName[i] != '\0'; i++)
+    {
+        // Convert variable name to uppercase
+        if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
+        // Replace non valid character for C identifier with '_'
+        else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; }
+    }
+
+    byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE     %i\n\n", varFileName, dataSize);
+
+    byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName);
+    for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]);
+    byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]);
+
+    // NOTE: Text data size exported is determined by '\0' (NULL) character
+    success = SaveFileText(fileName, txtData);
+
+    RL_FREE(txtData);
+
+    if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
+    else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
+
+    return success;
+}
+
+// Load text data from file, returns a '\0' terminated string
+// NOTE: text chars array should be freed manually
+char *LoadFileText(const char *fileName)
+{
+    char *text = NULL;
+
+    if (fileName != NULL)
+    {
+        if (loadFileText)
+        {
+            text = loadFileText(fileName);
+            return text;
+        }
+#if defined(SUPPORT_STANDARD_FILEIO)
+        FILE *file = fopen(fileName, "rt");
+
+        if (file != NULL)
+        {
+            // WARNING: When reading a file as 'text' file,
+            // text mode causes carriage return-linefeed translation...
+            // ...but using fseek() should return correct byte-offset
+            fseek(file, 0, SEEK_END);
+            unsigned int size = (unsigned int)ftell(file);
+            fseek(file, 0, SEEK_SET);
+
+            if (size > 0)
+            {
+                text = (char *)RL_CALLOC(size + 1, sizeof(char));
+
+                if (text != NULL)
+                {
+                    unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
+
+                    // WARNING: \r\n is converted to \n on reading, so,
+                    // read bytes count gets reduced by the number of lines
+                    if (count < size) text = (char *)RL_REALLOC(text, count + 1);
+
+                    // Zero-terminate the string
+                    text[count] = '\0';
+
+                    TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
+                }
+                else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
+            }
+            else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
+
+            fclose(file);
+        }
+        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
+#else
+    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
+#endif
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
+
+    return text;
+}
+
+// Unload file text data allocated by LoadFileText()
+void UnloadFileText(char *text)
+{
+    RL_FREE(text);
+}
+
+// Save text data to file (write), string must be '\0' terminated
+bool SaveFileText(const char *fileName, const char *text)
+{
+    bool success = false;
+
+    if (fileName != NULL)
+    {
+        if (saveFileText)
+        {
+            return saveFileText(fileName, text);
+        }
+#if defined(SUPPORT_STANDARD_FILEIO)
+        FILE *file = fopen(fileName, "wt");
+
+        if (file != NULL)
+        {
+            int count = fprintf(file, "%s", text);
+
+            if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
+            else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
+
+            int result = fclose(file);
+            if (result == 0) success = true;
+        }
+        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
+#else
+    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
+#endif
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
+
+    return success;
+}
+
+// File access custom callbacks
+// WARNING: Callbacks setup is intended for advanced users
+
+// Set custom file binary data loader
+void SetLoadFileDataCallback(LoadFileDataCallback callback)
+{
+    loadFileData = callback;
+}
+
+// Set custom file binary data saver
+void SetSaveFileDataCallback(SaveFileDataCallback callback)
+{
+    saveFileData = callback;
+}
+
+// Set custom file text data loader
+void SetLoadFileTextCallback(LoadFileTextCallback callback)
+{
+    loadFileText = callback;
+}
+
+// Set custom file text data saver
+void SetSaveFileTextCallback(SaveFileTextCallback callback)
+{
+    saveFileText = callback;
+}
+
+// Rename file (if exists)
+// NOTE: Only rename file name required, not full path
+int FileRename(const char *fileName, const char *fileRename)
+{
+    int result = 0;
+
+    if (FileExists(fileName))
+    {
+        result = rename(fileName, fileRename);
+    }
+    else result = -1;
+
+    return result;
+}
+
+// Remove file (if exists)
+int FileRemove(const char *fileName)
+{
+    int result = 0;
+
+    if (FileExists(fileName))
+    {
+        result = remove(fileName);
+    }
+    else result = -1;
+
+    return result;
+}
+
+// Copy file from one path to another
+// NOTE: If destination path does not exist, it is created!
+int FileCopy(const char *srcPath, const char *dstPath)
+{
+    int result = 0;
+    int srcDataSize = 0;
+    unsigned char *srcFileData = LoadFileData(srcPath, &srcDataSize);
+
+    // Create required paths if they do not exist
+    if (!DirectoryExists(GetDirectoryPath(dstPath)))
+        result = MakeDirectory(GetDirectoryPath(dstPath));
+
+    if (result == 0) // Directory created successfully (or already exists)
+    {
+        if ((srcFileData != NULL) && (srcDataSize > 0))
+            result = SaveFileData(dstPath, srcFileData, srcDataSize);
+    }
+
+    UnloadFileData(srcFileData);
+
+    return result;
+}
+
+// Move file from one directory to another
+// NOTE: If dst directories do not exists they are created
+int FileMove(const char *srcPath, const char *dstPath)
+{
+    int result = 0;
+
+    if (FileExists(srcPath))
+    {
+        FileCopy(srcPath, dstPath);
+        FileRemove(srcPath);
+    }
+    else result = -1;
+
+    return result;
+}
+
+// Replace text in an existing file
+// WARNING: DEPENDENCY: [rtext] module
+int FileTextReplace(const char *fileName, const char *search, const char *replacement)
+{
+    int result = 0;
+    char *fileText = NULL;
+    char *fileTextUpdated = { 0 };
+
+#if defined(SUPPORT_MODULE_RTEXT)
+    if (FileExists(fileName))
+    {
+        fileText = LoadFileText(fileName);
+        fileTextUpdated = TextReplace(fileText, search, replacement);
+        result = SaveFileText(fileName, fileTextUpdated);
+        MemFree(fileTextUpdated);
+        UnloadFileText(fileText);
+    }
+#else
+    TRACELOG(LOG_WARNING, "FILE: File text replace requires [rtext] module");
+#endif
+
+    return result;
+}
+
+// Find text index position in existing file
+// WARNING: DEPENDENCY: [rtext] module
+int FileTextFindIndex(const char *fileName, const char *search)
+{
+    int result = -1;
+
+    if (FileExists(fileName))
+    {
+        char *fileText = LoadFileText(fileName);
+        char *ptr = strstr(fileText, search);
+        if (ptr != NULL) result = (int)(ptr - fileText);
+        UnloadFileText(fileText);
+    }
+
+    return result;
+}
+
+// Check if the file exists
+bool FileExists(const char *fileName)
+{
+    bool result = false;
+
+    if (ACCESS(fileName) != -1) result = true;
+
+    // NOTE: Alternatively, stat() can be used instead of access()
+    //#include <sys/stat.h>
+    //struct stat statbuf;
+    //if (stat(filename, &statbuf) == 0) result = true;
+
+    return result;
+}
+
+// Check file extension
+bool IsFileExtension(const char *fileName, const char *ext)
+{
+    #define MAX_FILE_EXTENSIONS  32
+
+    bool result = false;
+    const char *fileExt = GetFileExtension(fileName);
+
+    // WARNING: fileExt points to last '.' on fileName string but it could happen
+    // that fileName is not correct: "myfile.png more text following\n"
+
+    if (fileExt != NULL)
+    {
+        int fileExtLength = (int)strlen(fileExt);
+        char fileExtLower[16] = { 0 };
+        char *fileExtLowerPtr = fileExtLower;
+        for (int i = 0; (i < fileExtLength) && (i < 16); i++)
+        {
+            // Copy and convert to lower-case
+            if ((fileExt[i] >= 'A') && (fileExt[i] <= 'Z')) fileExtLower[i] =  fileExt[i] + 32;
+            else fileExtLower[i] =  fileExt[i];
+        }
+
+        int extCount = 1;
+        int extLength = (int)strlen(ext);
+        char *extList = (char *)RL_CALLOC(extLength + 1, 1);
+        char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 };
+        strncpy(extList, ext, extLength);
+        extListPtrs[0] = extList;
+
+        for (int i = 0; i < extLength; i++)
+        {
+            // Convert to lower-case if extension is upper-case
+            if ((extList[i] >= 'A') && (extList[i] <= 'Z')) extList[i] += 32;
+
+            // Get pointer to next extension and add null-terminator
+            if ((extList[i] == ';') && (extCount < (MAX_FILE_EXTENSIONS - 1)))
+            {
+                extList[i] = '\0';
+                extListPtrs[extCount] = extList + i + 1;
+                extCount++;
+            }
+        }
+
+        for (int i = 0; i < extCount; i++)
+        {
+            // Consider the case where extension provided
+            // does not start with the '.'
+            fileExtLowerPtr = fileExtLower;
+            if (extListPtrs[i][0] != '.') fileExtLowerPtr++;
+
+            if (strcmp(fileExtLowerPtr, extListPtrs[i]) == 0)
+            {
+                result = true;
+                break;
+            }
+        }
+
+        RL_FREE(extList);
+    }
+
+    return result;
+}
+
+// Check if a directory path exists
+bool DirectoryExists(const char *dirPath)
+{
+    bool result = false;
+    DIR *dir = opendir(dirPath);
+
+    if (dir != NULL)
+    {
+        result = true;
+        closedir(dir);
+    }
+
+    return result;
+}
+
+// Get file length in bytes
+// NOTE: GetFileSize() conflicts with windows.h
+int GetFileLength(const char *fileName)
+{
+    int size = 0;
+
+    // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(),
+    // but depending on the platform that call could not be available
+    //struct stat result = { 0 };
+    //stat(fileName, &result);
+    //return result.st_size;
+
+    FILE *file = fopen(fileName, "rb");
+
+    if (file != NULL)
+    {
+        fseek(file, 0L, SEEK_END);
+        long int fileSize = ftell(file);
+
+        // Check for size overflow (INT_MAX)
+        if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName);
+        else size = (int)fileSize;
+
+        fclose(file);
+    }
+
+    return size;
+}
+
+// Get file modification time (last write time)
+long GetFileModTime(const char *fileName)
+{
+    struct stat result = { 0 };
+    long modTime = 0;
+
+    if (stat(fileName, &result) == 0)
+    {
+        time_t mod = result.st_mtime;
+        modTime = (long)mod;
+    }
+
+    return modTime;
+}
+
+// Get pointer to extension for a filename string (includes the dot: .png)
+// WARNING: We just get the ptr but not the extension as a separate string
+const char *GetFileExtension(const char *fileName)
+{
+    const char *dot = strrchr(fileName, '.');
+
+    if (!dot || (dot == fileName)) return NULL;
+
+    return dot;
+}
+
+// String pointer reverse break: returns right-most occurrence of charset in s
+static const char *strprbrk(const char *text, const char *charset)
+{
+    const char *latestMatch = NULL;
+
+    for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
+
+    return latestMatch;
+}
+
+// Get pointer to filename for a path string
+const char *GetFileName(const char *filePath)
+{
+    const char *fileName = NULL;
+
+    if (filePath != NULL) fileName = strprbrk(filePath, "\\/");
+
+    if (fileName == NULL) return filePath;
+
+    return fileName + 1;
+}
+
+// Get filename string without extension (uses static string)
+const char *GetFileNameWithoutExt(const char *filePath)
+{
+    #define MAX_FILENAME_LENGTH     256
+
+    static char fileName[MAX_FILENAME_LENGTH] = { 0 };
+    memset(fileName, 0, MAX_FILENAME_LENGTH);
+
+    if (filePath != NULL)
+    {
+        strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
+        int fileNameLenght = (int)strlen(fileName); // Get size in bytes
+
+        for (int i = fileNameLenght; i > 0; i--) // Reverse search '.'
+        {
+            if (fileName[i] == '.')
+            {
+                // NOTE: We break on first '.' found
+                fileName[i] = '\0';
+                break;
+            }
+        }
+    }
+
+    return fileName;
+}
+
+// Get directory for a given filePath
+const char *GetDirectoryPath(const char *filePath)
+{
+    /*
+    // NOTE: Directory separator is different in Windows and other platforms,
+    // fortunately, Windows also support the '/' separator, that's the one should be used
+    #if defined(_WIN32)
+        char separator = '\\';
+    #else
+        char separator = '/';
+    #endif
+    */
+    const char *lastSlash = NULL;
+    static char dirPath[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(dirPath, 0, MAX_FILEPATH_LENGTH);
+
+    // In case provided path does not contain a root drive letter (C:\, D:\) nor leading path separator (\, /),
+    // we add the current directory path to dirPath
+    if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))
+    {
+        // For security, we set starting path to current directory,
+        // obtained path will be concatenated to this
+        dirPath[0] = '.';
+        dirPath[1] = '/';
+    }
+
+    lastSlash = strprbrk(filePath, "\\/");
+    if (lastSlash)
+    {
+        if (lastSlash == filePath)
+        {
+            // The last and only slash is the leading one: path is in a root directory
+            dirPath[0] = filePath[0];
+            dirPath[1] = '\0';
+        }
+        else
+        {
+            // NOTE: Be careful, strncpy() is not safe, it does not care about '\0'
+            char *dirPathPtr = dirPath;
+            if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2;     // Skip drive letter, "C:"
+            memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1));
+            dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0';  // Add '\0' manually
+        }
+    }
+
+    return dirPath;
+}
+
+// Get previous directory path for a given path
+const char *GetPrevDirectoryPath(const char *dirPath)
+{
+    static char prevDirPath[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
+    int dirPathLength = (int)strlen(dirPath);
+
+    if (dirPathLength <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH  - 1);
+
+    for (int i = (dirPathLength - 1); (i >= 0) && (dirPathLength > 3); i--)
+    {
+        if ((dirPath[i] == '\\') || (dirPath[i] == '/'))
+        {
+            // Check for root: "C:\" or "/"
+            if (((i == 2) && (dirPath[1] ==':')) || (i == 0)) i++;
+
+            strncpy(prevDirPath, dirPath, i);
+            break;
+        }
+    }
+
+    return prevDirPath;
+}
+
+// Get current working directory
+const char *GetWorkingDirectory(void)
+{
+    static char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(currentDir, 0, MAX_FILEPATH_LENGTH);
+
+    char *path = GETCWD(currentDir, MAX_FILEPATH_LENGTH - 1);
+
+    return path;
+}
+
+const char *GetApplicationDirectory(void)
+{
+    static char appDir[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(appDir, 0, MAX_FILEPATH_LENGTH);
+
+#if defined(_WIN32)
+    int len = 0;
+
+    #if defined(UNICODE)
+    unsigned short widePath[MAX_PATH];
+    len = GetModuleFileNameW(NULL, (wchar_t *)widePath, MAX_PATH);
+    len = WideCharToMultiByte(0, 0, (wchar_t *)widePath, len, appDir, MAX_PATH, NULL, NULL);
+    #else
+    len = GetModuleFileNameA(NULL, appDir, MAX_PATH);
+    #endif
+
+    if (len > 0)
+    {
+        for (int i = len; i >= 0; --i)
+        {
+            if (appDir[i] == '\\')
+            {
+                appDir[i + 1] = '\0';
+                break;
+            }
+        }
+    }
+    else
+    {
+        appDir[0] = '.';
+        appDir[1] = '\\';
+    }
+
+#elif defined(__linux__)
+
+    unsigned int size = sizeof(appDir);
+    ssize_t len = readlink("/proc/self/exe", appDir, size);
+
+    if (len > 0)
+    {
+        for (int i = len; i >= 0; --i)
+        {
+            if (appDir[i] == '/')
+            {
+                appDir[i + 1] = '\0';
+                break;
+            }
+        }
+    }
+    else
+    {
+        appDir[0] = '.';
+        appDir[1] = '/';
+    }
+
+#elif defined(__APPLE__)
+
+    uint32_t size = sizeof(appDir);
+
+    if (_NSGetExecutablePath(appDir, &size) == 0)
+    {
+        int appDirLength = (int)strlen(appDir);
+        for (int i = appDirLength; i >= 0; --i)
+        {
+            if (appDir[i] == '/')
+            {
+                appDir[i + 1] = '\0';
+                break;
+            }
+        }
+    }
+    else
+    {
+        appDir[0] = '.';
+        appDir[1] = '/';
+    }
+
+#elif defined(__FreeBSD__)
+
+    size_t size = sizeof(appDir);
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+
+    if (sysctl(mib, 4, appDir, &size, NULL, 0) == 0)
+    {
+        int appDirLength = (int)strlen(appDir);
+        for (int i = appDirLength; i >= 0; --i)
+        {
+            if (appDir[i] == '/')
+            {
+                appDir[i + 1] = '\0';
+                break;
+            }
+        }
+    }
+    else
+    {
+        appDir[0] = '.';
+        appDir[1] = '/';
+    }
+
+#elif defined(__wasm__)
+
+    appDir[0] = '/';
+#endif
+
+    return appDir;
+}
+
+// Load directory filepaths
+// NOTE: Base path is prepended to the scanned filepaths
+// WARNING: Directory is scanned twice, first time to get files count
+// No recursive scanning is done!
+FilePathList LoadDirectoryFiles(const char *dirPath)
+{
+    FilePathList files = { 0 };
+    unsigned int fileCounter = 0;
+
+    struct dirent *entity;
+    DIR *dir = opendir(dirPath);
+
+    if (dir != NULL) // It's a directory
+    {
+        // SCAN 1: Count files
+        while ((entity = readdir(dir)) != NULL)
+        {
+            // NOTE: We skip '.' (current dir) and '..' (parent dir) filepaths
+            if ((strcmp(entity->d_name, ".") != 0) && (strcmp(entity->d_name, "..") != 0)) fileCounter++;
+        }
+
+        // Memory allocation for dirFileCount
+        files.capacity = fileCounter;
+        files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
+        for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
+
+        closedir(dir);
+
+        // SCAN 2: Read filepaths
+        // NOTE: Directory paths are also registered
+        ScanDirectoryFiles(dirPath, &files, NULL);
+
+        // Security check: read files.count should match fileCounter
+        if (files.count != files.capacity) TRACELOG(LOG_WARNING, "FILEIO: Read files count do not match capacity allocated");
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: Failed to open requested directory");  // Maybe it's a file...
+
+    return files;
+}
+
+// Load directory filepaths with extension filtering and recursive directory scan
+// NOTE: On recursive loading we do not pre-scan for file count, we use MAX_FILEPATH_CAPACITY
+FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs)
+{
+    FilePathList files = { 0 };
+
+    files.capacity = MAX_FILEPATH_CAPACITY;
+    files.paths = (char **)RL_CALLOC(files.capacity, sizeof(char *));
+    for (unsigned int i = 0; i < files.capacity; i++) files.paths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char));
+
+    // WARNING: basePath is always prepended to scanned paths
+    if (scanSubdirs) ScanDirectoryFilesRecursively(basePath, &files, filter);
+    else ScanDirectoryFiles(basePath, &files, filter);
+
+    return files;
+}
+
+// Unload directory filepaths
+// WARNING: files.count is not reseted to 0 after unloading
+void UnloadDirectoryFiles(FilePathList files)
+{
+    if (files.paths != NULL)
+    {
+        for (unsigned int i = 0; i < files.capacity; i++) RL_FREE(files.paths[i]);
+
+        RL_FREE(files.paths);
+    }
+}
+
+// Create directories (including full path requested), returns 0 on success
+int MakeDirectory(const char *dirPath)
+{
+    if ((dirPath == NULL) || (dirPath[0] == '\0')) return -1; // Path is not valid
+    if (DirectoryExists(dirPath)) return 0; // Path already exists (is valid)
+
+    // Copy path string to avoid modifying original
+    int dirPathLength = (int)strlen(dirPath) + 1;
+    char *pathcpy = (char *)RL_CALLOC(dirPathLength, 1);
+    memcpy(pathcpy, dirPath, dirPathLength);
+
+    // Iterate over pathcpy, create each subdirectory as needed
+    for (int i = 0; (i < dirPathLength) && (pathcpy[i] != '\0'); i++)
+    {
+        if (pathcpy[i] == ':') i++;
+        else
+        {
+            if ((pathcpy[i] == '\\') || (pathcpy[i] == '/'))
+            {
+                pathcpy[i] = '\0';
+                if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
+                pathcpy[i] = '/';
+            }
+        }
+    }
+
+    // Create final directory
+    if (!DirectoryExists(pathcpy)) MKDIR(pathcpy);
+    RL_FREE(pathcpy);
+
+    // In case something failed and requested directory
+    // was not successfully created, return -1
+    if (!DirectoryExists(dirPath)) return -1;
+
+    return 0;
+}
+
+// Change working directory, returns true on success
+bool ChangeDirectory(const char *dirPath)
+{
+    bool result = CHDIR(dirPath);
+
+    if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dirPath);
+    else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dirPath);
+
+    return (result == 0);
+}
+
+// Check if a given path point to a file
+bool IsPathFile(const char *path)
+{
+    struct stat result = { 0 };
+    stat(path, &result);
+
+    return S_ISREG(result.st_mode);
+}
+
+// Check if fileName is valid for the platform/OS
+bool IsFileNameValid(const char *fileName)
+{
+    bool valid = true;
+
+    if ((fileName != NULL) && (fileName[0] != '\0'))
+    {
+        int fileNameLength = (int)strlen(fileName);
+        bool allPeriods = true;
+
+        for (int i = 0; i < fileNameLength; i++)
+        {
+            // Check invalid characters
+            if ((fileName[i] == '<') ||
+                (fileName[i] == '>') ||
+                (fileName[i] == ':') ||
+                (fileName[i] == '\"') ||
+                (fileName[i] == '/') ||
+                (fileName[i] == '\\') ||
+                (fileName[i] == '|') ||
+                (fileName[i] == '?') ||
+                (fileName[i] == '*')) { valid = false; break; }
+
+            // Check non-glyph characters
+            if ((unsigned char)fileName[i] < 32) { valid = false; break; }
+
+            // Check if filename is not all periods
+            if (fileName[i] != '.') allPeriods = false;
+        }
+
+        if (allPeriods) valid = false;
+
+/*
+        if (valid)
+        {
+            // Check invalid DOS names
+            if (fileNameLength >= 3)
+            {
+                if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'N')) ||   // CON
+                    ((fileName[0] == 'P') && (fileName[1] == 'R') && (fileName[2] == 'N')) ||   // PRN
+                    ((fileName[0] == 'A') && (fileName[1] == 'U') && (fileName[2] == 'X')) ||   // AUX
+                    ((fileName[0] == 'N') && (fileName[1] == 'U') && (fileName[2] == 'L'))) valid = false; // NUL
+            }
+
+            if (fileNameLength >= 4)
+            {
+                if (((fileName[0] == 'C') && (fileName[1] == 'O') && (fileName[2] == 'M') && ((fileName[3] >= '0') && (fileName[3] <= '9'))) ||  // COM0-9
+                    ((fileName[0] == 'L') && (fileName[1] == 'P') && (fileName[2] == 'T') && ((fileName[3] >= '0') && (fileName[3] <= '9')))) valid = false; // LPT0-9
+            }
+        }
+*/
+    }
+
+    return valid;
+}
+
+// Check if a file has been dropped into window
+bool IsFileDropped(void)
+{
+    bool result = false;
+
+    if (CORE.Window.dropFileCount > 0) result = true;
+
+    return result;
+}
+
+// Load dropped filepaths
+FilePathList LoadDroppedFiles(void)
+{
+    FilePathList files = { 0 };
+
+    files.count = CORE.Window.dropFileCount;
+    files.paths = CORE.Window.dropFilepaths;
+
+    return files;
+}
+
+// Unload dropped filepaths
+void UnloadDroppedFiles(FilePathList files)
+{
+    // WARNING: files pointers are the same as internal ones
+
+    if (files.count > 0)
+    {
+        for (unsigned int i = 0; i < files.count; i++) RL_FREE(files.paths[i]);
+
+        RL_FREE(files.paths);
+
+        CORE.Window.dropFileCount = 0;
+        CORE.Window.dropFilepaths = NULL;
+    }
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Compression and Encoding
+//----------------------------------------------------------------------------------
+
+// Compress data (DEFLATE algorithm)
+unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize)
+{
+    #define COMPRESSION_QUALITY_DEFLATE  8
+
+    unsigned char *compData = NULL;
+
+#if defined(SUPPORT_COMPRESSION_API)
+    // Compress data and generate a valid DEFLATE stream
+    struct sdefl *sdefl = (struct sdefl *)RL_CALLOC(1, sizeof(struct sdefl));   // WARNING: Possible stack overflow, struct sdefl is almost 1MB
+    int bounds = sdefl_bound(dataSize);
+    compData = (unsigned char *)RL_CALLOC(bounds, 1);
+
+    *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE);   // Compression level 8, same as stbiw
+    RL_FREE(sdefl);
+
+    TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize);
+#endif
+
+    return compData;
+}
+
+// Decompress data (DEFLATE algorithm)
+unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize)
+{
+    unsigned char *data = NULL;
+
+#if defined(SUPPORT_COMPRESSION_API)
+    // Decompress data from a valid DEFLATE stream
+    unsigned char *data0 = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1);
+    int size = sinflate(data0, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize);
+
+    // WARNING: RL_REALLOC can make (and leave) data copies in memory,
+    // that can be a security concern in case of compression of sensitive data
+    // So, we use a second buffer to copy data manually, wiping original buffer memory
+    data = (unsigned char *)RL_CALLOC(size, 1);
+    memcpy(data, data0, size);
+    memset(data0, 0, MAX_DECOMPRESSION_SIZE*1024*1024); // Wipe memory, is memset() safe?
+    RL_FREE(data0);
+
+    TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, size);
+
+    *dataSize = size;
+#endif
+
+    return data;
+}
+
+// Encode data to Base64 string
+// NOTE: Returned string includes NULL terminator, considered on outputSize
+char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize)
+{
+    // Base64 conversion table from RFC 4648 [0..63]
+    // NOTE: They represent 64 values (6 bits), to encode 3 bytes of data into 4 "sixtets" (6bit characters)
+    static const char base64EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    // Compute expected size and padding
+    int paddedSize = dataSize;
+    while (paddedSize%3 != 0) paddedSize++; // Padding bytes to round 4*(dataSize/3) to 4 bytes
+    int estimatedOutputSize = 4*(paddedSize/3);
+    int padding = paddedSize - dataSize;
+
+    // Adding null terminator to string
+    estimatedOutputSize += 1;
+
+    // Load some memory to store encoded string
+    char *encodedData = (char *)RL_CALLOC(estimatedOutputSize, 1);
+    if (encodedData == NULL) return NULL;
+
+    int outputCount = 0;
+    for (int i = 0; i < dataSize;)
+    {
+        unsigned int octetA = 0;
+        unsigned int octetB = 0;
+        unsigned int octetC = 0;
+        unsigned int octetPack = 0;
+
+        octetA = data[i]; // Generates 2 sextets
+        octetB = ((i + 1) < dataSize)? data[i + 1] : 0; // Generates 3 sextets
+        octetC = ((i + 2) < dataSize)? data[i + 2] : 0; // Generates 4 sextets
+
+        octetPack = (octetA << 16) | (octetB << 8) | octetC;
+
+        encodedData[outputCount + 0] = (unsigned char)(base64EncodeTable[(octetPack >> 18) & 0x3f]);
+        encodedData[outputCount + 1] = (unsigned char)(base64EncodeTable[(octetPack >> 12) & 0x3f]);
+        encodedData[outputCount + 2] = (unsigned char)(base64EncodeTable[(octetPack >> 6) & 0x3f]);
+        encodedData[outputCount + 3] = (unsigned char)(base64EncodeTable[octetPack & 0x3f]);
+        outputCount += 4;
+        i += 3;
+    }
+
+    // Add required padding bytes
+    for (int p = 0; p < padding; p++) encodedData[outputCount - p - 1] = '=';
+
+    // Add null terminator to string
+    encodedData[outputCount] = '\0';
+    outputCount++;
+
+    if (outputCount != estimatedOutputSize) TRACELOG(LOG_WARNING, "BASE64: Output size differs from estimation");
+
+    *outputSize = estimatedOutputSize;
+    return encodedData;
+}
+
+// Decode Base64 string (expected NULL terminated)
+unsigned char *DecodeDataBase64(const char *text, int *outputSize)
+{
+    // Base64 decode table
+    // NOTE: Following ASCII order [0..255] assigning the expected sixtet value to
+    // every character in the corresponding ASCII position
+    static const unsigned char base64DecodeTable[256] = {
+        ['A'] =  0, ['B'] =  1, ['C'] =  2, ['D'] =  3, ['E'] =  4, ['F'] =  5, ['G'] =  6, ['H'] =  7,
+        ['I'] =  8, ['J'] =  9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15,
+        ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25,
+        ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33,
+        ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
+        ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
+        ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
+        ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
+    };
+
+    *outputSize = 0;
+    if (text == NULL) return NULL;
+
+    // Compute expected size and padding
+    int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
+    int ending = dataSize - 1;
+    int padding = 0;
+    while (text[ending] == '=') { padding++; ending--; }
+    int estimatedOutputSize = 3*(dataSize/4) - padding;
+    int maxOutputSize = 3*(dataSize/4);
+
+    // Load some memory to store decoded data
+    // NOTE: Allocated enough size to include padding
+    unsigned char *decodedData = (unsigned char *)RL_CALLOC(maxOutputSize, 1);
+    if (decodedData == NULL) return NULL;
+
+    int outputCount = 0;
+    for (int i = 0; i < dataSize;)
+    {
+        // Every 4 sixtets must generate 3 octets
+        if ((i + 2) >= dataSize)
+        {
+            TRACELOG(LOG_WARNING, "BASE64: Decoding error: Input data size is not valid");
+            break;
+        }
+
+        unsigned int sixtetA = base64DecodeTable[(unsigned char)text[i]];
+        unsigned int sixtetB = base64DecodeTable[(unsigned char)text[i + 1]];
+        unsigned int sixtetC = (((i + 2) < dataSize) && (unsigned char)text[i + 2] != '=')? base64DecodeTable[(unsigned char)text[i + 2]] : 0;
+        unsigned int sixtetD = (((i + 3) < dataSize) && (unsigned char)text[i + 3] != '=')? base64DecodeTable[(unsigned char)text[i + 3]] : 0;
+
+        unsigned int octetPack = (sixtetA << 18) | (sixtetB << 12)  | (sixtetC << 6) | sixtetD;
+
+        if ((outputCount + 3) > maxOutputSize)
+        {
+            TRACELOG(LOG_WARNING, "BASE64: Decoding error: Output data size is too small");
+            break;
+        }
+
+        decodedData[outputCount + 0] = (octetPack >> 16) & 0xff;
+        decodedData[outputCount + 1] = (octetPack >> 8) & 0xff;
+        decodedData[outputCount + 2] = octetPack & 0xff;
+        outputCount += 3;
+        i += 4;
+    }
+
+    if (estimatedOutputSize != (outputCount - padding)) TRACELOG(LOG_WARNING, "BASE64: Decoded size differs from estimation");
+
+    *outputSize = estimatedOutputSize;
+    return decodedData;
+}
+
+// Compute CRC32 hash code
+unsigned int ComputeCRC32(unsigned char *data, int dataSize)
+{
+    static unsigned int crcTable[256] = {
+        0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+        0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+        0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+        0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+        0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+        0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+        0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+        0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+        0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+        0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+        0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+        0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+        0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+        0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+        0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+        0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+        0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+        0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+        0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+        0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+        0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+        0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+        0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+        0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+        0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+        0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+        0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+        0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+        0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+        0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+        0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+        0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+    };
+
+    unsigned int crc = ~0u;
+
+    for (int i = 0; i < dataSize; i++) crc = (crc >> 8) ^ crcTable[data[i] ^ (crc & 0xff)];
+
+    return ~crc;
+}
+
+// Compute MD5 hash code
+// NOTE: Returns a static int[4] array (16 bytes)
+unsigned int *ComputeMD5(unsigned char *data, int dataSize)
+{
+    #define ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
+
+    static unsigned int hash[4] = { 0 };  // Hash to be returned
+
+    // WARNING: All variables are unsigned 32 bit and wrap modulo 2^32 when calculating
+
+    // NOTE: r specifies the per-round shift amounts
+    unsigned int r[] = {
+        7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
+        5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
+        4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
+        6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
+    };
+
+    // Using binary integer part of the sines of integers (in radians) as constants
+    unsigned int k[] = {
+        0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+        0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+        0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+        0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+        0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+        0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
+        0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+        0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+        0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+        0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+        0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
+        0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+        0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+        0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+        0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+        0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+    };
+
+    hash[0] = 0x67452301;
+    hash[1] = 0xefcdab89;
+    hash[2] = 0x98badcfe;
+    hash[3] = 0x10325476;
+
+    // Pre-processing: adding a single 1 bit
+    // Append '1' bit to message
+    // NOTE: The input bytes are considered as bits strings,
+    // where the first bit is the most significant bit of the byte
+
+    // Pre-processing: padding with zeros
+    // Append '0' bit until message length in bit 448 (mod 512)
+    // Append length mod (2 pow 64) to message
+
+    int newDataSize = ((((dataSize + 8)/64) + 1)*64) - 8;
+
+    unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize + 64, 1); // Initialize with '0' bits, allocating 64 extra bytes
+    memcpy(msg, data, dataSize);
+    msg[dataSize] = 128; // Write the '1' bit
+
+    unsigned int bitsLen = 8*dataSize;
+    memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
+
+    // Process the message in successive 512-bit chunks for each 512-bit chunk of message
+    for (int offset = 0; offset < newDataSize; offset += (512/8))
+    {
+        // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
+        unsigned int *w = (unsigned int *)(msg + offset);
+
+        // Initialize hash value for this chunk
+        unsigned int a = hash[0];
+        unsigned int b = hash[1];
+        unsigned int c = hash[2];
+        unsigned int d = hash[3];
+
+        for (int i = 0; i < 64; i++)
+        {
+            unsigned int f = 0;
+            unsigned int g = 0;
+
+            if (i < 16)
+            {
+                f = (b & c) | ((~b) & d);
+                g = i;
+            }
+            else if (i < 32)
+            {
+                f = (d & b) | ((~d) & c);
+                g = (5*i + 1)%16;
+            }
+            else if (i < 48)
+            {
+                f = b ^ c ^ d;
+                g = (3*i + 5)%16;
+            }
+            else
+            {
+                f = c ^ (b | (~d));
+                g = (7*i)%16;
+            }
+
+            unsigned int temp = d;
+            d = c;
+            c = b;
+            b = b + ROTATE_LEFT((a + f + k[i] + w[g]), r[i]);
+            a = temp;
+        }
+
+        // Add chunk's hash to result so far
+        hash[0] += a;
+        hash[1] += b;
+        hash[2] += c;
+        hash[3] += d;
+    }
+
+    RL_FREE(msg);
+
+    return hash;
+}
+
+// Compute SHA-1 hash code
+// NOTE: Returns a static int[5] array (20 bytes)
+unsigned int *ComputeSHA1(unsigned char *data, int dataSize)
+{
+    #define SHA1_ROTATE_LEFT(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
+
+    static unsigned int hash[5] = { 0 };  // Hash to be returned
+
+    // Initialize hash values
+    hash[0] = 0x67452301;
+    hash[1] = 0xEFCDAB89;
+    hash[2] = 0x98BADCFE;
+    hash[3] = 0x10325476;
+    hash[4] = 0xC3D2E1F0;
+
+    // Pre-processing: adding a single 1 bit
+    // Append '1' bit to message
+    // NOTE: The input bytes are considered as bits strings,
+    // where the first bit is the most significant bit of the byte
+
+    // Pre-processing: padding with zeros
+    // Append '0' bit until message length in bit 448 (mod 512)
+    // Append length mod (2 pow 64) to message
+
+    int newDataSize = ((((dataSize + 8)/64) + 1)*64);
+
+    unsigned char *msg = (unsigned char *)RL_CALLOC(newDataSize, 1); // Initialize with '0' bits
+    memcpy(msg, data, dataSize);
+    msg[dataSize] = 128; // Write the '1' bit
+
+    unsigned long long bitsLen = 8ULL * dataSize;
+    msg[newDataSize-1] = (unsigned char)(bitsLen);
+    msg[newDataSize-2] = (unsigned char)(bitsLen >> 8);
+    msg[newDataSize-3] = (unsigned char)(bitsLen >> 16);
+    msg[newDataSize-4] = (unsigned char)(bitsLen >> 24);
+    msg[newDataSize-5] = (unsigned char)(bitsLen >> 32);
+    msg[newDataSize-6] = (unsigned char)(bitsLen >> 40);
+    msg[newDataSize-7] = (unsigned char)(bitsLen >> 48);
+    msg[newDataSize-8] = (unsigned char)(bitsLen >> 56);
+
+    // Process the message in successive 512-bit chunks
+    for (int offset = 0; offset < newDataSize; offset += (512/8))
+    {
+        // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
+        unsigned int w[80] = { 0 };
+        for (int i = 0; i < 16; i++)
+        {
+            w[i] = (msg[offset + (i*4) + 0] << 24) |
+                   (msg[offset + (i*4) + 1] << 16) |
+                   (msg[offset + (i*4) + 2] << 8) |
+                   (msg[offset + (i*4) + 3]);
+        }
+
+        // Message schedule: extend the sixteen 32-bit words into eighty 32-bit words:
+        for (int i = 16; i < 80; i++) w[i] = SHA1_ROTATE_LEFT(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1);
+
+        // Initialize hash value for this chunk
+        unsigned int a = hash[0];
+        unsigned int b = hash[1];
+        unsigned int c = hash[2];
+        unsigned int d = hash[3];
+        unsigned int e = hash[4];
+
+        for (int i = 0; i < 80; i++)
+        {
+            unsigned int f = 0;
+            unsigned int k = 0;
+
+            if (i < 20)
+            {
+                f = (b & c) | ((~b) & d);
+                k = 0x5A827999;
+            }
+            else if (i < 40)
+            {
+                f = b ^ c ^ d;
+                k = 0x6ED9EBA1;
+            }
+            else if (i < 60)
+            {
+                f = (b & c) | (b & d) | (c & d);
+                k = 0x8F1BBCDC;
+            }
+            else
+            {
+                f = b ^ c ^ d;
+                k = 0xCA62C1D6;
+            }
+
+            unsigned int temp = SHA1_ROTATE_LEFT(a, 5) + f + e + k + w[i];
+            e = d;
+            d = c;
+            c = SHA1_ROTATE_LEFT(b, 30);
+            b = a;
+            a = temp;
+        }
+
+        // Add this chunk's hash to result so far
+        hash[0] += a;
+        hash[1] += b;
+        hash[2] += c;
+        hash[3] += d;
+        hash[4] += e;
+    }
+
+    RL_FREE(msg);
+
+    return hash;
+}
+
+// Compute SHA-256 hash code
+// NOTE: Returns a static int[8] array (32 bytes)
+unsigned int *ComputeSHA256(unsigned char *data, int dataSize)
+{
+    #define SHA256_ROTATE_RIGHT(x, c) ((x >> c) | (x << ((sizeof(unsigned int)*8) - c)))
+    #define SHA256_A0(x) (SHA256_ROTATE_RIGHT(x, 7) ^ SHA256_ROTATE_RIGHT(x, 18) ^ (x >> 3))
+    #define SHA256_A1(x) (SHA256_ROTATE_RIGHT(x, 17) ^ SHA256_ROTATE_RIGHT(x, 19) ^ (x >> 10))
+
+    static const unsigned int k[64] = {
+        0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+        0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+        0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+        0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+        0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+        0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+        0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+        0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+        0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+        0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+        0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+        0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+        0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+        0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+        0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+        0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+    };
+
+    static unsigned int hash[8] = { 0 };
+    hash[0] = 0x6A09e667;
+    hash[1] = 0xbb67ae85;
+    hash[2] = 0x3c6ef372;
+    hash[3] = 0xa54ff53a;
+    hash[4] = 0x510e527f;
+    hash[5] = 0x9b05688c;
+    hash[6] = 0x1f83d9ab;
+    hash[7] = 0x5be0cd19;
+
+    const unsigned long long int bitLen = ((unsigned long long int)dataSize)*8;
+    unsigned long long int paddedSize = dataSize + sizeof(dataSize);
+    paddedSize += (64 - (paddedSize%64));
+    unsigned char *buffer = (unsigned char *)RL_CALLOC(paddedSize, sizeof(unsigned char));
+
+    memcpy(buffer, data, dataSize);
+    buffer[dataSize] = 0x80;
+    for (int i = 1; i <= sizeof(bitLen); i++)
+    {
+        buffer[(paddedSize - sizeof(bitLen)) + (i - 1)] = (bitLen >> (8*(sizeof(bitLen) - i))) & 0xFF;
+    }
+
+    for (unsigned long long int blockN = 0; blockN < paddedSize/64; blockN++)
+    {
+        unsigned int a = hash[0];
+        unsigned int b = hash[1];
+        unsigned int c = hash[2];
+        unsigned int d = hash[3];
+        unsigned int e = hash[4];
+        unsigned int f = hash[5];
+        unsigned int g = hash[6];
+        unsigned int h = hash[7];
+
+        unsigned char *block = buffer + (blockN*64);
+        unsigned int w[64] = { 0 };
+        for (int i = 0; i < 16; i++)
+        {
+            w[i] = ((unsigned int)block[i*4 + 0] << 24) |
+                   ((unsigned int)block[i*4 + 1] << 16) |
+                   ((unsigned int)block[i*4 + 2] << 8)  |
+                   ((unsigned int)block[i*4 + 3]);
+        }
+        for (int t = 16; t < 64; t++) w[t] = SHA256_A1(w[t - 2]) + w[t - 7] + SHA256_A0(w[t - 15]) + w[t - 16];
+
+        for (unsigned long long int t = 0; t < 64; t++)
+        {
+            unsigned int e1 = (SHA256_ROTATE_RIGHT(e, 6) ^ SHA256_ROTATE_RIGHT(e, 11) ^ SHA256_ROTATE_RIGHT(e, 25));
+            unsigned int ch = ((e & f) ^ (~e & g));
+            unsigned int t1 = (h + e1 + ch + k[t] + w[t]);
+            unsigned int e0 = (SHA256_ROTATE_RIGHT(a, 2) ^ SHA256_ROTATE_RIGHT(a, 13) ^ SHA256_ROTATE_RIGHT(a, 22));
+            unsigned int maj = ((a & b) ^ (a & c) ^ (b & c));
+            unsigned int t2 = e0 + maj;
+
+            h = g;
+            g = f;
+            f = e;
+            e = d + t1;
+            d = c;
+            c = b;
+            b = a;
+            a = t1 + t2;
+        }
+
+        hash[0] += a;
+        hash[1] += b;
+        hash[2] += c;
+        hash[3] += d;
+        hash[4] += e;
+        hash[5] += f;
+        hash[6] += g;
+        hash[7] += h;
+    }
+
+    RL_FREE(buffer);
+
+    return hash;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Automation Events Recording and Playing
+//----------------------------------------------------------------------------------
+
+// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+AutomationEventList LoadAutomationEventList(const char *fileName)
+{
+    AutomationEventList list = { 0 };
+
+    // Allocate and empty automation event list, ready to record new events
+    list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
+    list.capacity = MAX_AUTOMATION_EVENTS;
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
+    else
+    {
+        // Load automation events file (binary)
+        /*
+        //int dataSize = 0;
+        //unsigned char *data = LoadFileData(fileName, &dataSize);
+
+        FILE *raeFile = fopen(fileName, "rb");
+        unsigned char fileId[4] = { 0 };
+
+        fread(fileId, 1, 4, raeFile);
+
+        if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
+        {
+            fread(&eventCount, sizeof(int), 1, raeFile);
+            TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
+            fread(events, sizeof(AutomationEvent), eventCount, raeFile);
+        }
+
+        fclose(raeFile);
+        */
+
+        // Load events file (text)
+        //unsigned char *buffer = LoadFileText(fileName);
+        FILE *raeFile = fopen(fileName, "rt");
+
+        if (raeFile != NULL)
+        {
+            unsigned int counter = 0;
+            char buffer[256] = { 0 };
+            char eventDesc[64] = { 0 };
+
+            char *result = fgets(buffer, 256, raeFile);
+            if (result != buffer) TRACELOG(LOG_WARNING, "AUTOMATION: [%s] Issue reading line to buffer", fileName);
+
+            while (!feof(raeFile))
+            {
+                switch (buffer[0])
+                {
+                    case 'c': sscanf(buffer, "c %i", &list.count); break;
+                    case 'e':
+                    {
+                        sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
+                               &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
+
+                        counter++;
+                    } break;
+                    default: break;
+                }
+
+                result = fgets(buffer, 256, raeFile);
+                if (result != buffer) TRACELOG(LOG_WARNING, "AUTOMATION: [%s] Issue reading line to buffer", fileName);
+            }
+
+            if (counter != list.count)
+            {
+                TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
+                list.count = counter;
+            }
+
+            fclose(raeFile);
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
+        }
+
+        TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
+    }
+#endif
+    return list;
+}
+
+// Unload automation events list from file
+void UnloadAutomationEventList(AutomationEventList list)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    RL_FREE(list.events);
+#endif
+}
+
+// Export automation events list as text file
+bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
+{
+    bool success = false;
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    // Export events as binary file
+    // NOTE: Code not used, only for reference if required in the future
+    /*
+    if (list.count > 0)
+    {
+        int binarySize = 4 + sizeof(int) + sizeof(AutomationEvent)*list.count;
+        unsigned char *binBuffer = (unsigned char *)RL_CALLOC(binarySize, 1);
+        int offset = 0;
+        memcpy(binBuffer + offset, "rAE ", 4); 
+        offset += 4;
+        memcpy(binBuffer + offset, &list.count, sizeof(int)); 
+        offset += sizeof(int);
+        memcpy(binBuffer + offset, list.events, sizeof(AutomationEvent)*list.count);
+        offset += sizeof(AutomationEvent)*list.count;
+        
+        success = SaveFileData(TextFormat("%s.rae",fileName), binBuffer, binarySize);
+        RL_FREE(binBuffer);
+    }
+    */
+
+    // Export events as text
+    // NOTE: Save to memory buffer and SaveFileText()
+    char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
+
+    int byteCount = 0;
+    byteCount += sprintf(txtData + byteCount, "#\n");
+    byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
+    byteCount += sprintf(txtData + byteCount, "#\n");
+    byteCount += sprintf(txtData + byteCount, "#    c <events_count>\n");
+    byteCount += sprintf(txtData + byteCount, "#    e <frame> <event_type> <param0> <param1> <param2> <param3> // <event_type_name>\n");
+    byteCount += sprintf(txtData + byteCount, "#\n");
+    byteCount += sprintf(txtData + byteCount, "# more info and bugs-report:  github.com/raysan5/raylib\n");
+    byteCount += sprintf(txtData + byteCount, "# feedback and support:       ray[at]raylib.com\n");
+    byteCount += sprintf(txtData + byteCount, "#\n");
+    byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2026 Ramon Santamaria (@raysan5)\n");
+    byteCount += sprintf(txtData + byteCount, "#\n\n");
+
+    // Add events data
+    byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
+    for (unsigned int i = 0; i < list.count; i++)
+    {
+        byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
+            list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]);
+    }
+
+    // NOTE: Text data size exported is determined by '\0' (NULL) character
+    success = SaveFileText(fileName, txtData);
+
+    RL_FREE(txtData);
+#endif
+
+    return success;
+}
+
+// Setup automation event list to record to
+void SetAutomationEventList(AutomationEventList *list)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    currentEventList = list;
+#endif
+}
+
+// Set automation event internal base frame to start recording
+void SetAutomationEventBaseFrame(int frame)
+{
+    CORE.Time.frameCounter = frame;
+}
+
+// Start recording automation events (AutomationEventList must be set)
+void StartAutomationEventRecording(void)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    automationEventRecording = true;
+#endif
+}
+
+// Stop recording automation events
+void StopAutomationEventRecording(void)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    automationEventRecording = false;
+#endif
+}
+
+// Play a recorded automation event
+void PlayAutomationEvent(AutomationEvent event)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+    // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
+
+    if (!automationEventRecording)
+    {
+        switch (event.type)
+        {
+            // Input event
+            case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break;             // param[0]: key
+            case INPUT_KEY_DOWN: {                                                                              // param[0]: key
+                CORE.Input.Keyboard.currentKeyState[event.params[0]] = true;
+
+                if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false)
+                {
+                    if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE)
+                    {
+                        // Add character to the queue
+                        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0];
+                        CORE.Input.Keyboard.keyPressedQueueCount++;
+                    }
+                }
+            } break;
+            case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break;    // param[0]: key
+            case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break;   // param[0]: key
+            case INPUT_MOUSE_POSITION:      // param[0]: x, param[1]: y
+            {
+                CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
+                CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
+            } break;
+            case INPUT_MOUSE_WHEEL_MOTION:  // param[0]: x delta, param[1]: y delta
+            {
+                CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0];
+                CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1];
+            } break;
+            case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break;            // param[0]: id
+            case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break;           // param[0]: id
+            case INPUT_TOUCH_POSITION:      // param[0]: id, param[1]: x, param[2]: y
+            {
+                CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
+                CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
+            } break;
+            case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break;                // param[0]: gamepad
+            case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break;            // param[0]: gamepad
+            case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break;    // param[0]: gamepad, param[1]: button
+            case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break;   // param[0]: gamepad, param[1]: button
+            case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
+            {
+                CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
+            } break;
+    #if defined(SUPPORT_GESTURES_SYSTEM)
+            case INPUT_GESTURE: GESTURES.current = event.params[0]; break;     // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
+    #endif
+            // Window event
+            case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
+            case WINDOW_MAXIMIZE: MaximizeWindow(); break;
+            case WINDOW_MINIMIZE: MinimizeWindow(); break;
+            case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
+
+            // Custom event
+    #if defined(SUPPORT_SCREEN_CAPTURE)
+            case ACTION_TAKE_SCREENSHOT:
+            {
+                TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
+                screenshotCounter++;
+            } break;
+    #endif
+            case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
+            default: break;
+        }
+
+        TRACELOG(LOG_INFO, "AUTOMATION PLAY: Frame: %i | Event type: %i | Event parameters: %i, %i, %i", event.frame, event.type, event.params[0], event.params[1], event.params[2]);
+    }
+#endif
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Input Handling: Keyboard
+//----------------------------------------------------------------------------------
+
+// Check if a key has been pressed once
+bool IsKeyPressed(int key)
+{
+
+    bool pressed = false;
+
+    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
+    {
+        if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true;
+    }
+
+    return pressed;
+}
+
+// Check if a key has been pressed again
+bool IsKeyPressedRepeat(int key)
+{
+    bool repeat = false;
+
+    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
+    {
+        if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true;
+    }
+
+    return repeat;
+}
+
+// Check if a key is being pressed (key held down)
+bool IsKeyDown(int key)
+{
+    bool down = false;
+
+    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
+    {
+        if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true;
+    }
+
+    return down;
+}
+
+// Check if a key has been released once
+bool IsKeyReleased(int key)
+{
+    bool released = false;
+
+    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
+    {
+        if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true;
+    }
+
+    return released;
+}
+
+// Check if a key is NOT being pressed (key not held down)
+bool IsKeyUp(int key)
+{
+    bool up = false;
+
+    if ((key > 0) && (key < MAX_KEYBOARD_KEYS))
+    {
+        if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true;
+    }
+
+    return up;
+}
+
+// Get the last key pressed
+int GetKeyPressed(void)
+{
+    int value = 0;
+
+    if (CORE.Input.Keyboard.keyPressedQueueCount > 0)
+    {
+        // Get character from the queue head
+        value = CORE.Input.Keyboard.keyPressedQueue[0];
+
+        // Shift elements 1 step toward the head
+        for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++)
+            CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1];
+
+        // Reset last character in the queue
+        CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount - 1] = 0;
+        CORE.Input.Keyboard.keyPressedQueueCount--;
+    }
+
+    return value;
+}
+
+// Get the last char pressed
+int GetCharPressed(void)
+{
+    int value = 0;
+
+    if (CORE.Input.Keyboard.charPressedQueueCount > 0)
+    {
+        // Get character from the queue head
+        value = CORE.Input.Keyboard.charPressedQueue[0];
+
+        // Shift elements 1 step toward the head
+        for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++)
+            CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1];
+
+        // Reset last character in the queue
+        CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount - 1] = 0;
+        CORE.Input.Keyboard.charPressedQueueCount--;
+    }
+
+    return value;
+}
+
+// Set a custom key to exit program
+// NOTE: default exitKey is set to ESCAPE
+void SetExitKey(int key)
+{
+    CORE.Input.Keyboard.exitKey = key;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Input Handling: Gamepad
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//int SetGamepadMappings(const char *mappings)
+
+// Check if a gamepad is available
+bool IsGamepadAvailable(int gamepad)
+{
+    bool result = false;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad]) result = true;
+
+    return result;
+}
+
+// Get gamepad internal name id
+const char *GetGamepadName(int gamepad)
+{
+    return CORE.Input.Gamepad.name[gamepad];
+}
+
+// Check if a gamepad button has been pressed once
+bool IsGamepadButtonPressed(int gamepad, int button)
+{
+    bool pressed = false;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+        (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 0) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) pressed = true;
+
+    return pressed;
+}
+
+// Check if a gamepad button is being pressed
+bool IsGamepadButtonDown(int gamepad, int button)
+{
+    bool down = false;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+        (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true;
+
+    return down;
+}
+
+// Check if a gamepad button has NOT been pressed once
+bool IsGamepadButtonReleased(int gamepad, int button)
+{
+    bool released = false;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+        (CORE.Input.Gamepad.previousButtonState[gamepad][button] == 1) && (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) released = true;
+
+    return released;
+}
+
+// Check if a gamepad button is NOT being pressed
+bool IsGamepadButtonUp(int gamepad, int button)
+{
+    bool up = false;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+        (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true;
+
+    return up;
+}
+
+// Get the last gamepad button pressed
+int GetGamepadButtonPressed(void)
+{
+    return CORE.Input.Gamepad.lastButtonPressed;
+}
+
+// Get gamepad axis count
+int GetGamepadAxisCount(int gamepad)
+{
+    return CORE.Input.Gamepad.axisCount[gamepad];
+}
+
+// Get axis movement vector for a gamepad
+float GetGamepadAxisMovement(int gamepad, int axis)
+{
+    float value = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
+
+    if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXES))
+    {
+        float movement = (value < 0.0f)? CORE.Input.Gamepad.axisState[gamepad][axis] : fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]);
+
+        if (movement > value) value = CORE.Input.Gamepad.axisState[gamepad][axis];
+    }
+
+    return value;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Input Handling: Mouse
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//void SetMousePosition(int x, int y)
+//void SetMouseCursor(int cursor)
+
+// Check if a mouse button has been pressed once
+bool IsMouseButtonPressed(int button)
+{
+    bool pressed = false;
+
+    if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) pressed = true;
+
+    // Map touches to mouse buttons checking
+    if ((CORE.Input.Touch.currentTouchState[button] == 1) && (CORE.Input.Touch.previousTouchState[button] == 0)) pressed = true;
+
+    return pressed;
+}
+
+// Check if a mouse button is being pressed
+bool IsMouseButtonDown(int button)
+{
+    bool down = false;
+
+    if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true;
+
+    // NOTE: Touches are considered like mouse buttons
+    if (CORE.Input.Touch.currentTouchState[button] == 1) down = true;
+
+    return down;
+}
+
+// Check if a mouse button has been released once
+bool IsMouseButtonReleased(int button)
+{
+    bool released = false;
+
+    if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) released = true;
+
+    // Map touches to mouse buttons checking
+    if ((CORE.Input.Touch.currentTouchState[button] == 0) && (CORE.Input.Touch.previousTouchState[button] == 1)) released = true;
+
+    return released;
+}
+
+// Check if a mouse button is NOT being pressed
+bool IsMouseButtonUp(int button)
+{
+    bool up = false;
+
+    if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true;
+
+    // NOTE: Touches are considered like mouse buttons
+    if (CORE.Input.Touch.currentTouchState[button] == 0) up = true;
+
+    return up;
+}
+
+// Get mouse position X
+int GetMouseX(void)
+{
+    int mouseX = (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x);
+    return mouseX;
+}
+
+// Get mouse position Y
+int GetMouseY(void)
+{
+    int mouseY = (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y);
+    return mouseY;
+}
+
+// Get mouse position XY
+Vector2 GetMousePosition(void)
+{
+    Vector2 position = { 0 };
+
+    position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x;
+    position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y;
+
+    return position;
+}
+
+// Get mouse delta between frames
+Vector2 GetMouseDelta(void)
+{
+    Vector2 delta = { 0 };
+
+    delta.x = CORE.Input.Mouse.currentPosition.x - CORE.Input.Mouse.previousPosition.x;
+    delta.y = CORE.Input.Mouse.currentPosition.y - CORE.Input.Mouse.previousPosition.y;
+
+    return delta;
+}
+
+// Set mouse offset
+// NOTE: Useful when rendering to different size targets
+void SetMouseOffset(int offsetX, int offsetY)
+{
+    CORE.Input.Mouse.offset = (Vector2){ (float)offsetX, (float)offsetY };
+}
+
+// Set mouse scaling
+// NOTE: Useful when rendering to different size targets
+void SetMouseScale(float scaleX, float scaleY)
+{
+    CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY };
+}
+
+// Get mouse wheel movement Y
+float GetMouseWheelMove(void)
+{
+    float result = 0.0f;
+
+    if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x;
+    else result = (float)CORE.Input.Mouse.currentWheelMove.y;
+
+    return result;
+}
+
+// Get mouse wheel movement X/Y as a vector
+Vector2 GetMouseWheelMoveV(void)
+{
+    Vector2 result = { 0 };
+
+    result = CORE.Input.Mouse.currentWheelMove;
+
+    return result;
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Input Handling: Touch
+//----------------------------------------------------------------------------------
+
+// Get touch position X for touch point 0 (relative to screen size)
+int GetTouchX(void)
+{
+    int touchX = (int)CORE.Input.Touch.position[0].x;
+    return touchX;
+}
+
+// Get touch position Y for touch point 0 (relative to screen size)
+int GetTouchY(void)
+{
+    int touchY = (int)CORE.Input.Touch.position[0].y;
+    return touchY;
+}
+
+// Get touch position XY for a touch point index (relative to screen size)
+Vector2 GetTouchPosition(int index)
+{
+    Vector2 position = { -1.0f, -1.0f };
+
+    if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index];
+    else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS);
+
+    return position;
+}
+
+// Get touch point identifier for given index
+int GetTouchPointId(int index)
+{
+    int id = -1;
+
+    if (index < MAX_TOUCH_POINTS) id = CORE.Input.Touch.pointId[index];
+
+    return id;
+}
+
+// Get number of touch points
+int GetTouchPointCount(void)
+{
+    return CORE.Input.Touch.pointCount;
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+
+// NOTE: Functions with a platform-specific implementation on rcore_<platform>.c
+//int InitPlatform(void)
+//void ClosePlatform(void)
+
+// Initialize hi-resolution timer
+void InitTimer(void)
+{
+    // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions
+    // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often
+    // High resolutions can also prevent the CPU power management system from entering power-saving modes
+    // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter
+#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL)
+    timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
+#endif
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
+    struct timespec now = { 0 };
+
+    if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success
+    {
+        CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec;
+    }
+    else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available");
+#endif
+
+    CORE.Time.previous = GetTime(); // Get time as double
+}
+
+// Set viewport for a provided width and height
+void SetupViewport(int width, int height)
+{
+    CORE.Window.render.width = width;
+    CORE.Window.render.height = height;
+
+    // Set viewport width and height
+    rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height);
+
+    rlMatrixMode(RL_PROJECTION);        // Switch to projection matrix
+    rlLoadIdentity();                   // Reset current matrix (projection)
+
+    // Set orthographic projection to current framebuffer size
+    // NOTE: Configured top-left corner as (0, 0)
+    rlOrtho(0, CORE.Window.render.width, CORE.Window.render.height, 0, 0.0f, 1.0f);
+
+    rlMatrixMode(RL_MODELVIEW);         // Switch back to modelview matrix
+    rlLoadIdentity();                   // Reset current matrix (modelview)
+}
+
+// Scan all files and directories in a base path
+// WARNING: files.paths[] must be previously allocated and
+// contain enough space to store all required paths
+static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const char *filter)
+{
+    static char path[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(path, 0, MAX_FILEPATH_LENGTH);
+
+    struct dirent *dp = NULL;
+    DIR *dir = opendir(basePath);
+
+    if (dir != NULL)
+    {
+        while ((dp = readdir(dir)) != NULL)
+        {
+            if ((strcmp(dp->d_name, ".") != 0) &&
+                (strcmp(dp->d_name, "..") != 0))
+            {
+                // Construct new path from our base path
+            #if defined(_WIN32)
+                int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name);
+            #else
+                int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name);
+            #endif
+
+                if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH))
+                {
+                    TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath);
+                }
+                else if (filter != NULL)
+                {
+                    if (IsPathFile(path))
+                    {
+                        if (IsFileExtension(path, filter))
+                        {
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                            files->count++;
+                        }
+                    }
+                    else
+                    {
+                        if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)
+                        {
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                            files->count++;
+                        }
+                    }
+                }
+                else
+                {
+                    strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                    files->count++;
+                }
+            }
+        }
+
+        closedir(dir);
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
+}
+
+// Scan all files and directories recursively from a base path
+static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *files, const char *filter)
+{
+    // WARNING: Path can not be static or it will be reused between recursive function calls!
+    char path[MAX_FILEPATH_LENGTH] = { 0 };
+    memset(path, 0, MAX_FILEPATH_LENGTH);
+
+    struct dirent *dp = NULL;
+    DIR *dir = opendir(basePath);
+
+    if (dir != NULL)
+    {
+        while (((dp = readdir(dir)) != NULL) && (files->count < files->capacity))
+        {
+            if ((strcmp(dp->d_name, ".") != 0) && (strcmp(dp->d_name, "..") != 0))
+            {
+                // Construct new path from our base path
+            #if defined(_WIN32)
+                int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s\\%s", basePath, dp->d_name);
+            #else
+                int pathLength = snprintf(path, MAX_FILEPATH_LENGTH - 1, "%s/%s", basePath, dp->d_name);
+            #endif
+
+                if ((pathLength < 0) || (pathLength >= MAX_FILEPATH_LENGTH))
+                {
+                    TRACELOG(LOG_WARNING, "FILEIO: Path longer than %d characters (%s...)", MAX_FILEPATH_LENGTH, basePath);
+                }
+                else if (IsPathFile(path))
+                {
+                    if (filter != NULL)
+                    {
+                        if (IsFileExtension(path, filter))
+                        {
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                            files->count++;
+                        }
+                    }
+                    else
+                    {
+                        strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                        files->count++;
+                    }
+
+                    if (files->count >= files->capacity)
+                    {
+                        TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
+                        break;
+                    }
+                }
+                else
+                {
+                    if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL))
+                    {
+                        strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
+                        files->count++;
+                    }
+
+                    if (files->count >= files->capacity)
+                    {
+                        TRACELOG(LOG_WARNING, "FILEIO: Maximum filepath scan capacity reached (%i files)", files->capacity);
+                        break;
+                    }
+
+                    ScanDirectoryFilesRecursively(path, files, filter);
+                }
+            }
+        }
+
+        closedir(dir);
+    }
+    else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
+}
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+// Automation event recording
+// Checking events in current frame and save them into currentEventList
+// NOTE: Recording is by default done at EndDrawing(), before PollInputEvents()
+static void RecordAutomationEvent(void)
+{
+    if (currentEventList->count == currentEventList->capacity) return;
+
+    // Keyboard input events recording
+    //-------------------------------------------------------------------------------------
+    for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
+    {
+        // Event type: INPUT_KEY_UP (only saved once)
+        if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
+            currentEventList->events[currentEventList->count].params[0] = key;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+
+        // Event type: INPUT_KEY_DOWN
+        if (CORE.Input.Keyboard.currentKeyState[key])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
+            currentEventList->events[currentEventList->count].params[0] = key;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+    //-------------------------------------------------------------------------------------
+
+    // Mouse input currentEventList->events recording
+    //-------------------------------------------------------------------------------------
+    for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
+    {
+        // Event type: INPUT_MOUSE_BUTTON_UP
+        if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
+            currentEventList->events[currentEventList->count].params[0] = button;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+
+        // Event type: INPUT_MOUSE_BUTTON_DOWN
+        if (CORE.Input.Mouse.currentButtonState[button])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
+            currentEventList->events[currentEventList->count].params[0] = button;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+
+    // Event type: INPUT_MOUSE_POSITION (only saved if changed)
+    if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
+        ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
+    {
+        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+        currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
+        currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
+        currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
+        currentEventList->events[currentEventList->count].params[2] = 0;
+
+        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+        currentEventList->count++;
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+
+    // Event type: INPUT_MOUSE_WHEEL_MOTION
+    if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
+        ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
+    {
+        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+        currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
+        currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
+        currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;
+        currentEventList->events[currentEventList->count].params[2] = 0;
+
+        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+        currentEventList->count++;
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+    //-------------------------------------------------------------------------------------
+
+    // Touch input currentEventList->events recording
+    //-------------------------------------------------------------------------------------
+    for (int id = 0; id < MAX_TOUCH_POINTS; id++)
+    {
+        // Event type: INPUT_TOUCH_UP
+        if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
+            currentEventList->events[currentEventList->count].params[0] = id;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+
+        // Event type: INPUT_TOUCH_DOWN
+        if (CORE.Input.Touch.currentTouchState[id])
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
+            currentEventList->events[currentEventList->count].params[0] = id;
+            currentEventList->events[currentEventList->count].params[1] = 0;
+            currentEventList->events[currentEventList->count].params[2] = 0;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+
+        // Event type: INPUT_TOUCH_POSITION         
+        if (((int)CORE.Input.Touch.position[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
+            ((int)CORE.Input.Touch.position[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
+        {
+            currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+            currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
+            currentEventList->events[currentEventList->count].params[0] = id;
+            currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.position[id].x;
+            currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.position[id].y;
+
+            TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+            currentEventList->count++;
+        }
+        
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+    //-------------------------------------------------------------------------------------
+
+    // Gamepad input currentEventList->events recording
+    //-------------------------------------------------------------------------------------
+    for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
+    {
+        // Event type: INPUT_GAMEPAD_CONNECT
+        /*
+        if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
+            (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
+        {
+            // TODO: Save gamepad connect event
+        }
+        */
+
+        // Event type: INPUT_GAMEPAD_DISCONNECT
+        /*
+        if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
+            (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
+        {
+            // TODO: Save gamepad disconnect event
+        }
+        */
+
+        for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
+        {
+            // Event type: INPUT_GAMEPAD_BUTTON_UP
+            if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
+            {
+                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
+                currentEventList->events[currentEventList->count].params[0] = gamepad;
+                currentEventList->events[currentEventList->count].params[1] = button;
+                currentEventList->events[currentEventList->count].params[2] = 0;
+
+                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+                currentEventList->count++;
+            }
+
+            if (currentEventList->count == currentEventList->capacity) return;    // Security check
+
+            // Event type: INPUT_GAMEPAD_BUTTON_DOWN
+            if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
+            {
+                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
+                currentEventList->events[currentEventList->count].params[0] = gamepad;
+                currentEventList->events[currentEventList->count].params[1] = button;
+                currentEventList->events[currentEventList->count].params[2] = 0;
+
+                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+                currentEventList->count++;
+            }
+
+            if (currentEventList->count == currentEventList->capacity) return;    // Security check
+        }
+
+        for (int axis = 0; axis < MAX_GAMEPAD_AXES; axis++)
+        {
+            // Event type: INPUT_GAMEPAD_AXIS_MOTION
+            float defaultMovement = ((axis == GAMEPAD_AXIS_LEFT_TRIGGER) || (axis == GAMEPAD_AXIS_RIGHT_TRIGGER))? -1.0f : 0.0f;
+            if (GetGamepadAxisMovement(gamepad, axis) != defaultMovement)
+            {
+                currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+                currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
+                currentEventList->events[currentEventList->count].params[0] = gamepad;
+                currentEventList->events[currentEventList->count].params[1] = axis;
+                currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
+
+                TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+                currentEventList->count++;
+            }
+
+            if (currentEventList->count == currentEventList->capacity) return;    // Security check
+        }
+    }
+    //-------------------------------------------------------------------------------------
+
+#if defined(SUPPORT_GESTURES_SYSTEM)
+    // Gestures input currentEventList->events recording
+    //-------------------------------------------------------------------------------------
+    if (GESTURES.current != GESTURE_NONE)
+    {
+        // Event type: INPUT_GESTURE
+        currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+        currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
+        currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
+        currentEventList->events[currentEventList->count].params[1] = 0;
+        currentEventList->events[currentEventList->count].params[2] = 0;
+
+        TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+        currentEventList->count++;
+
+        if (currentEventList->count == currentEventList->capacity) return;    // Security check
+    }
+    //-------------------------------------------------------------------------------------
+#endif
+}
+#endif
+
+#if !defined(SUPPORT_MODULE_RTEXT)
+// Formatting of text with variables to 'embed'
+// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
+const char *TextFormat(const char *text, ...)
+{
+#ifndef MAX_TEXTFORMAT_BUFFERS
+    #define MAX_TEXTFORMAT_BUFFERS      4        // Maximum number of static buffers for text formatting
+#endif
+#ifndef MAX_TEXT_BUFFER_LENGTH
+    #define MAX_TEXT_BUFFER_LENGTH   1024        // Maximum size of static text buffer
+#endif
+
+    // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
+    static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
+    static int index = 0;
+
+    char *currentBuffer = buffers[index];
+    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
+
+    if (text != NULL)
+    {
+        va_list args;
+        va_start(args, text);
+        int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+        va_end(args);
+
+        // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+        if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+        {
+            // Inserting "..." at the end of the string to mark as truncated
+            char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+            snprintf(truncBuffer, 4, "...");
+        }
+
+        index += 1; // Move to next buffer for next function call
+        if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+    }
 
     return currentBuffer;
 }
diff --git a/raylib/src/rgestures.h b/raylib/src/rgestures.h
--- a/raylib/src/rgestures.h
+++ b/raylib/src/rgestures.h
@@ -21,7 +21,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -232,13 +232,13 @@
 // Global Variables Definition
 //----------------------------------------------------------------------------------
 static GesturesData GESTURES = {
-    .Touch.firstId = -1,
     .current = GESTURE_NONE,        // No current gesture detected
+    .Touch.firstId = -1,
     .enabledFlags = 0b0000001111111111  // All gestures supported by default
 };
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static float rgVector2Angle(Vector2 initialPosition, Vector2 finalPosition);
 static float rgVector2Distance(Vector2 v1, Vector2 v2);
@@ -481,7 +481,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 // Get angle from two-points vector with X-axis
 static float rgVector2Angle(Vector2 v1, Vector2 v2)
diff --git a/raylib/src/rglfw.c b/raylib/src/rglfw.c
--- a/raylib/src/rglfw.c
+++ b/raylib/src/rglfw.c
@@ -7,7 +7,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2017-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2017-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
diff --git a/raylib/src/rlgl.h b/raylib/src/rlgl.h
--- a/raylib/src/rlgl.h
+++ b/raylib/src/rlgl.h
@@ -3,24 +3,25 @@
 *   rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API
 *
 *   DESCRIPTION:
-*       An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0)
+*       An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0, ES 3.0)
 *       that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...)
 *
 *   ADDITIONAL NOTES:
 *       When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are
-*       initialized on rlglInit() to accumulate vertex data.
+*       initialized on rlglInit() to accumulate vertex data
 *
-*       When an internal state change is required all the stored vertex data is renderer in batch,
-*       additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch.
+*       When an internal state change is required all the stored vertex data is rendered in batch,
+*       additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch
 *
 *       Some resources are also loaded for convenience, here the complete list:
 *          - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data
 *          - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8
 *          - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs)
 *
-*       Internal buffer (and resources) must be manually unloaded calling rlglClose().
+*       Internal buffer (and resources) must be manually unloaded calling rlglClose()
 *
 *   CONFIGURATION:
+*       #define GRAPHICS_API_OPENGL_11_SOFTWARE
 *       #define GRAPHICS_API_OPENGL_11
 *       #define GRAPHICS_API_OPENGL_21
 *       #define GRAPHICS_API_OPENGL_33
@@ -32,13 +33,9 @@
 *           required by any other module, use rlGetVersion() to check it
 *
 *       #define RLGL_IMPLEMENTATION
-*           Generates the implementation of the library into the included file.
+*           Generates the implementation of the library into the included file
 *           If not defined, the library is in header only mode and can be included in other headers
-*           or source files without problems. But only ONE file should hold the implementation.
-*
-*       #define RLGL_RENDER_TEXTURES_HINT
-*           Enable framebuffer objects (fbo) support (enabled by default)
-*           Some GPUs could not support them despite the OpenGL version
+*           or source files without problems. But only ONE file should hold the implementation
 *
 *       #define RLGL_SHOW_GL_DETAILS_INFO
 *           Show OpenGL extensions and capabilities detailed logs on init
@@ -56,8 +53,8 @@
 *
 *       #define RL_MAX_MATRIX_STACK_SIZE             32    // Maximum size of internal Matrix stack
 *       #define RL_MAX_SHADER_LOCATIONS              32    // Maximum number of shader locations supported
-*       #define RL_CULL_DISTANCE_NEAR              0.01    // Default projection matrix near cull distance
-*       #define RL_CULL_DISTANCE_FAR             1000.0    // Default projection matrix far cull distance
+*       #define RL_CULL_DISTANCE_NEAR              0.05    // Default projection matrix near cull distance
+*       #define RL_CULL_DISTANCE_FAR             4000.0    // Default projection matrix far cull distance
 *
 *       When loading a shader, the following vertex attributes and uniform
 *       location names are tried to be set automatically:
@@ -88,7 +85,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2014-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -131,7 +128,6 @@
 // Support TRACELOG macros
 #ifndef TRACELOG
     #define TRACELOG(level, ...) (void)0
-    #define TRACELOGD(...) (void)0
 #endif
 
 // Allow custom memory allocators
@@ -149,7 +145,8 @@
 #endif
 
 // Security check in case no GRAPHICS_API_OPENGL_* defined
-#if !defined(GRAPHICS_API_OPENGL_11) && \
+#if !defined(GRAPHICS_API_OPENGL_11_SOFTWARE) && \
+    !defined(GRAPHICS_API_OPENGL_11) && \
     !defined(GRAPHICS_API_OPENGL_21) && \
     !defined(GRAPHICS_API_OPENGL_33) && \
     !defined(GRAPHICS_API_OPENGL_43) && \
@@ -159,7 +156,7 @@
 #endif
 
 // Security check in case multiple GRAPHICS_API_OPENGL_* defined
-#if defined(GRAPHICS_API_OPENGL_11)
+#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
     #if defined(GRAPHICS_API_OPENGL_21)
         #undef GRAPHICS_API_OPENGL_21
     #endif
@@ -174,6 +171,11 @@
     #endif
 #endif
 
+// Software implementation uses OpenGL 1.1 functionality
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    #define GRAPHICS_API_OPENGL_11
+#endif
+
 // OpenGL 2.1 uses most of OpenGL 3.3 Core functionality
 // WARNING: Specific parts are checked with #if defines
 #if defined(GRAPHICS_API_OPENGL_21)
@@ -190,10 +192,6 @@
     #define GRAPHICS_API_OPENGL_ES2
 #endif
 
-// Support framebuffer objects by default
-// NOTE: Some driver implementation do not support it, despite they should
-#define RLGL_RENDER_TEXTURES_HINT
-
 //----------------------------------------------------------------------------------
 // Defines and Macros
 //----------------------------------------------------------------------------------
@@ -234,10 +232,10 @@
 
 // Projection matrix culling
 #ifndef RL_CULL_DISTANCE_NEAR
-    #define RL_CULL_DISTANCE_NEAR                 0.01      // Default near cull distance
+    #define RL_CULL_DISTANCE_NEAR                 0.05      // Default near cull distance
 #endif
 #ifndef RL_CULL_DISTANCE_FAR
-    #define RL_CULL_DISTANCE_FAR                1000.0      // Default far cull distance
+    #define RL_CULL_DISTANCE_FAR                4000.0      // Default far cull distance
 #endif
 
 // Texture parameters (equivalent to OpenGL defines)
@@ -355,6 +353,9 @@
     #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS 8
 #endif
 #endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX
+    #define RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX 9
+#endif
 
 //----------------------------------------------------------------------------------
 // Types and Structures Definition
@@ -424,7 +425,8 @@
 
 // OpenGL version
 typedef enum {
-    RL_OPENGL_11 = 1,           // OpenGL 1.1
+    RL_OPENGL_11_SOFTWARE = 0,  // Software rendering
+    RL_OPENGL_11,               // OpenGL 1.1
     RL_OPENGL_21,               // OpenGL 2.1 (GLSL 120)
     RL_OPENGL_33,               // OpenGL 3.3 (GLSL 330)
     RL_OPENGL_43,               // OpenGL 4.3 (using GLSL 330)
@@ -641,10 +643,8 @@
 RLAPI void rlDisableVertexBufferElement(void);          // Disable vertex buffer element (VBO element)
 RLAPI void rlEnableVertexAttribute(unsigned int index); // Enable vertex attribute index
 RLAPI void rlDisableVertexAttribute(unsigned int index); // Disable vertex attribute index
-#if defined(GRAPHICS_API_OPENGL_11)
 RLAPI void rlEnableStatePointer(int vertexAttribType, void *buffer); // Enable attribute state pointer
 RLAPI void rlDisableStatePointer(int vertexAttribType); // Disable attribute state pointer
-#endif
 
 // Textures state
 RLAPI void rlActiveTextureSlot(int slot);               // Select and active a texture slot
@@ -681,9 +681,12 @@
 RLAPI void rlEnableScissorTest(void);                   // Enable scissor test
 RLAPI void rlDisableScissorTest(void);                  // Disable scissor test
 RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test
-RLAPI void rlEnableWireMode(void);                      // Enable wire mode
 RLAPI void rlEnablePointMode(void);                     // Enable point mode
-RLAPI void rlDisableWireMode(void);                     // Disable wire (and point) mode
+RLAPI void rlDisablePointMode(void);                    // Disable point mode
+RLAPI void rlSetPointSize(float size);                  // Set the point drawing size
+RLAPI float rlGetPointSize(void);                       // Get the point drawing size
+RLAPI void rlEnableWireMode(void);                      // Enable wire mode
+RLAPI void rlDisableWireMode(void);                     // Disable wire mode
 RLAPI void rlSetLineWidth(float width);                 // Set the line drawing width
 RLAPI float rlGetLineWidth(void);                       // Get the line drawing width
 RLAPI void rlEnableSmoothLines(void);                   // Enable line aliasing
@@ -706,6 +709,7 @@
 RLAPI void rlglInit(int width, int height);             // Initialize rlgl (buffers, shaders, textures, states)
 RLAPI void rlglClose(void);                             // De-initialize rlgl (buffers, shaders, textures)
 RLAPI void rlLoadExtensions(void *loader);              // Load OpenGL extensions (loader function required)
+RLAPI void *rlGetProcAddress(const char *procName);     // Get OpenGL procedure address
 RLAPI int rlGetVersion(void);                           // Get current OpenGL version
 RLAPI void rlSetFramebufferWidth(int width);            // Set current framebuffer width
 RLAPI int rlGetFramebufferWidth(void);                  // Get default framebuffer width
@@ -749,7 +753,7 @@
 // Textures management
 RLAPI unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount); // Load texture data
 RLAPI unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer); // Load depth texture/renderbuffer (to be attached to fbo)
-RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format); // Load texture cubemap data
+RLAPI unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount); // Load texture cubemap data
 RLAPI void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int height, int format, const void *data); // Update texture with new data on GPU
 RLAPI void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned int *glFormat, unsigned int *glType); // Get OpenGL internal formats
 RLAPI const char *rlGetPixelFormatName(unsigned int format);              // Get name string for pixel format
@@ -763,14 +767,17 @@
 RLAPI void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel); // Attach texture/renderbuffer to a framebuffer
 RLAPI bool rlFramebufferComplete(unsigned int id);                        // Verify framebuffer is complete
 RLAPI void rlUnloadFramebuffer(unsigned int id);                          // Delete framebuffer from GPU
+// WARNING: Copy and resize framebuffer functionality only defined for software backend
+RLAPI void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels); // Copy framebuffer pixel data to internal buffer
+RLAPI void rlResizeFramebuffer(int width, int height);                    // Resize internal framebuffer
 
 // Shaders management
 RLAPI unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode);    // Load shader from code strings
 RLAPI unsigned int rlCompileShader(const char *shaderCode, int type);           // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER)
 RLAPI unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId); // Load custom shader program
 RLAPI void rlUnloadShaderProgram(unsigned int id);                              // Unload shader program
-RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform
-RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName);   // Get shader location attribute
+RLAPI int rlGetLocationUniform(unsigned int shaderId, const char *uniformName); // Get shader location uniform, requires shader program id
+RLAPI int rlGetLocationAttrib(unsigned int shaderId, const char *attribName);   // Get shader location attribute, requires shader program id
 RLAPI void rlSetUniform(int locIndex, const void *value, int uniformType, int count); // Set shader value uniform
 RLAPI void rlSetUniformMatrix(int locIndex, Matrix mat);                        // Set shader value matrix
 RLAPI void rlSetUniformMatrices(int locIndex, const Matrix *mat, int count);    // Set shader value matrices
@@ -829,24 +836,32 @@
 #endif
 
 #if defined(GRAPHICS_API_OPENGL_11)
-    #if defined(__APPLE__)
-        #include <OpenGL/gl.h>          // OpenGL 1.1 library for OSX
-        #include <OpenGL/glext.h>       // OpenGL extensions library
+    #if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+        #define RLSW_IMPLEMENTATION
+        #define SW_MALLOC(sz) RL_MALLOC(sz)
+        #define SW_REALLOC(ptr, newSz) RL_REALLOC(ptr, newSz)
+        #define SW_FREE(ptr) RL_FREE(ptr)
+        #include "external/rlsw.h"          // OpenGL 1.1 software implementation
     #else
-        // APIENTRY for OpenGL function pointer declarations is required
-        #if !defined(APIENTRY)
-            #if defined(_WIN32)
-                #define APIENTRY __stdcall
-            #else
-                #define APIENTRY
+        #if defined(__APPLE__)
+            #include <OpenGL/gl.h>          // OpenGL 1.1 library for OSX
+            #include <OpenGL/glext.h>       // OpenGL extensions library
+        #else
+            // APIENTRY for OpenGL function pointer declarations is required
+            #if !defined(APIENTRY)
+                #if defined(_WIN32)
+                    #define APIENTRY __stdcall
+                #else
+                    #define APIENTRY
+                #endif
             #endif
-        #endif
-        // WINGDIAPI definition. Some Windows OpenGL headers need it
-        #if !defined(WINGDIAPI) && defined(_WIN32)
-            #define WINGDIAPI __declspec(dllimport)
-        #endif
+            // WINGDIAPI definition. Some Windows OpenGL headers need it
+            #if !defined(WINGDIAPI) && defined(_WIN32)
+                #define WINGDIAPI __declspec(dllimport)
+            #endif
 
-        #include <GL/gl.h>              // OpenGL 1.1 library
+            #include <GL/gl.h>              // OpenGL 1.1 library
+        #endif
     #endif
 #endif
 
@@ -865,6 +880,7 @@
 #elif defined(GRAPHICS_API_OPENGL_ES2)
     // NOTE: OpenGL ES 2.0 can be enabled on Desktop platforms,
     // in that case, functions are loaded from a custom glad for OpenGL ES 2.0
+    // TODO: OpenGL ES 2.0 support shouldn't be platform-dependant, neither require GLAD
     #if defined(PLATFORM_DESKTOP_GLFW) || defined(PLATFORM_DESKTOP_SDL)
         #define GLAD_GLES2_IMPLEMENTATION
         #include "external/glad_gles2.h"
@@ -877,6 +893,7 @@
 
     // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi
     // provided headers (despite being defined in official Khronos GLES2 headers)
+    // TODO: Avoid raylib platform-dependant code on rlgl, it should be a completely portable library
     #if defined(PLATFORM_DRM)
     typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount);
     typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
@@ -884,7 +901,7 @@
     #endif
 #endif
 
-#include <stdlib.h>                     // Required for: malloc(), free()
+#include <stdlib.h>                     // Required for: calloc(), free()
 #include <string.h>                     // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading]
 #include <math.h>                       // Required for: sqrtf(), sinf(), cosf(), floor(), log()
 
@@ -998,6 +1015,9 @@
 #ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
     #define RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS  "vertexBoneWeights" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS
 #endif
+#ifndef RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
+    #define RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX  "instanceTransform" // Bound by default to shader location: RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX
+#endif
 
 #ifndef RL_DEFAULT_SHADER_UNIFORM_NAME_MVP
     #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP         "mvp"               // model-view-projection matrix
@@ -1031,13 +1051,18 @@
 #endif
 
 //----------------------------------------------------------------------------------
-// Types and Structures Definition
+// Module Types and Structures Definition
 //----------------------------------------------------------------------------------
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+
+typedef void *(*rlglLoadProc)(const char *name);   // OpenGL extension functions loader signature (same as GLADloadproc)
+
 typedef struct rlglData {
     rlRenderBatch *currentBatch;            // Current render batch
     rlRenderBatch defaultBatch;             // Default internal render batch
 
+    rlglLoadProc loader;                    // OpenGL function loader
+
     struct {
         int vertexCounter;                  // Current active render batch vertex counter (generic, used for all batches)
         float texcoordx, texcoordy;         // Current active texture coordinate (added on glVertex*())
@@ -1053,6 +1078,7 @@
         Matrix stack[RL_MAX_MATRIX_STACK_SIZE];// Matrix stack for push/pop
         int stackCounter;                   // Matrix stack counter
 
+        unsigned int currentTextureId;      // Current texture id to be used on glBegin
         unsigned int defaultTextureId;      // Default texture used on shapes/poly drawing (required by shader)
         unsigned int activeTextureId[RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS];    // Active texture ids to be enabled on batch drawing (0 active by default)
         unsigned int defaultVShaderId;      // Default vertex shader id (used by default shader program)
@@ -1107,8 +1133,6 @@
     } ExtSupported;     // Extensions supported flags
 } rlglData;
 
-typedef void *(*rlglLoadProc)(const char *name);   // OpenGL extension functions loader signature (same as GLADloadproc)
-
 #endif  // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
 
 //----------------------------------------------------------------------------------
@@ -1120,6 +1144,7 @@
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 static rlglData RLGL = { 0 };
 #endif  // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
+static bool isGpuReady = false;
 
 #if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3)
 // NOTE: VAO functionality is exposed through extensions (OES)
@@ -1134,7 +1159,7 @@
 #endif
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Functions Declaration
 //----------------------------------------------------------------------------------
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 static void rlLoadShaderDefault(void);      // Load default shader
@@ -1146,16 +1171,16 @@
 
 static int rlGetPixelDataSize(int width, int height, int format);   // Get pixel data size in bytes (image or texture)
 
+static Matrix rlMatrixIdentity(void);                       // Get identity matrix
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 // Auxiliar matrix math functions
-typedef struct rl_float16 {
-    float v[16];
-} rl_float16;
+typedef struct rl_float16 { float v[16]; } rl_float16;
 static rl_float16 rlMatrixToFloatV(Matrix mat);             // Get float array of matrix data
 #define rlMatrixToFloat(mat) (rlMatrixToFloatV(mat).v)      // Get float vector for Matrix
-static Matrix rlMatrixIdentity(void);                       // Get identity matrix
 static Matrix rlMatrixMultiply(Matrix left, Matrix right);  // Multiply two matrices
 static Matrix rlMatrixTranspose(Matrix mat);                // Transposes provided matrix
 static Matrix rlMatrixInvert(Matrix mat);                   // Invert provided matrix
+#endif
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Matrix operations
@@ -1219,7 +1244,7 @@
     RLGL.State.stackCounter++;
 }
 
-// Pop lattest inserted matrix from RLGL.State.stack
+// Pop latest inserted matrix from RLGL.State.stack
 void rlPopMatrix(void)
 {
     if (RLGL.State.stackCounter > 0)
@@ -1473,8 +1498,8 @@
         if (RLGL.currentBatch->drawCounter >= RL_DEFAULT_BATCH_DRAWCALLS) rlDrawRenderBatch(RLGL.currentBatch);
 
         RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = mode;
-        RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount = 0;
-        RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.defaultTextureId;
+        RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId = RLGL.State.currentTextureId;
+        RLGL.State.currentTextureId = RLGL.State.defaultTextureId;
     }
 }
 
@@ -1503,8 +1528,8 @@
         tz = RLGL.State.transform.m2*x + RLGL.State.transform.m6*y + RLGL.State.transform.m10*z + RLGL.State.transform.m14;
     }
 
-    // WARNING: We can't break primitives when launching a new batch.
-    // RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices.
+    // WARNING: We can't break primitives when launching a new batch
+    // RL_LINES comes in pairs, RL_TRIANGLES come in groups of 3 vertices and RL_QUADS come in groups of 4 vertices
     // We must check current draw.mode when a new vertex is required and finish the batch only if the draw.mode draw.vertexCount is %2, %3 or %4
     if (RLGL.State.vertexCounter > (RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].elementCount*4 - 4))
     {
@@ -1639,6 +1664,7 @@
         {
             rlDrawRenderBatch(RLGL.currentBatch);
         }
+        RLGL.State.currentTextureId = RLGL.State.defaultTextureId;
 #endif
     }
     else
@@ -1646,6 +1672,7 @@
 #if defined(GRAPHICS_API_OPENGL_11)
         rlEnableTexture(id);
 #else
+        RLGL.State.currentTextureId = id;
         if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].textureId != id)
         {
             if (RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexCount > 0)
@@ -1664,6 +1691,9 @@
                     RLGL.State.vertexCounter += RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].vertexAlignment;
 
                     RLGL.currentBatch->drawCounter++;
+
+                    RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 1].mode = RLGL.currentBatch->draws[RLGL.currentBatch->drawCounter - 2].mode;
+
                 }
             }
 
@@ -1723,11 +1753,6 @@
 {
     glBindTexture(GL_TEXTURE_2D, id);
 
-#if !defined(GRAPHICS_API_OPENGL_11)
-    // Reset anisotropy filter, in case it was set
-    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
-#endif
-
     switch (param)
     {
         case RL_TEXTURE_WRAP_S:
@@ -1741,13 +1766,15 @@
 #endif
             }
             else glTexParameteri(GL_TEXTURE_2D, param, value);
-
         } break;
         case RL_TEXTURE_MAG_FILTER:
         case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break;
         case RL_TEXTURE_FILTER_ANISOTROPIC:
         {
 #if !defined(GRAPHICS_API_OPENGL_11)
+            // Reset anisotropy filter, in case it was set
+            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
+
             if (value <= RLGL.ExtSupported.maxAnisotropyLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)value);
             else if (RLGL.ExtSupported.maxAnisotropyLevel > 0.0f)
             {
@@ -1786,7 +1813,6 @@
                 else TRACELOG(RL_LOG_WARNING, "GL: Clamp mirror wrap mode not supported (GL_MIRROR_CLAMP_EXT)");
             }
             else glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value);
-
         } break;
         case RL_TEXTURE_MAG_FILTER:
         case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_CUBE_MAP, param, value); break;
@@ -1829,7 +1855,7 @@
 // Enable rendering to texture (fbo)
 void rlEnableFramebuffer(unsigned int id)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glBindFramebuffer(GL_FRAMEBUFFER, id);
 #endif
 }
@@ -1838,7 +1864,7 @@
 unsigned int rlGetActiveFramebuffer(void)
 {
     GLint fboId = 0;
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3))
     glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboId);
 #endif
     return fboId;
@@ -1847,7 +1873,7 @@
 // Disable rendering to texture
 void rlDisableFramebuffer(void)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
 #endif
 }
@@ -1855,7 +1881,7 @@
 // Blit active framebuffer to main framebuffer
 void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3))
     glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST);
 #endif
 }
@@ -1863,7 +1889,7 @@
 // Bind framebuffer object (fbo)
 void rlBindFramebuffer(unsigned int target, unsigned int framebuffer)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glBindFramebuffer(target, framebuffer);
 #endif
 }
@@ -1872,7 +1898,7 @@
 // NOTE: One color buffer is always active by default
 void rlActiveDrawBuffers(int count)
 {
-#if ((defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT))
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3))
     // NOTE: Maximum number of draw buffers supported is implementation dependant,
     // it can be queried with glGet*() but it must be at least 8
     //GLint maxDrawBuffers = 0;
@@ -1880,20 +1906,10 @@
 
     if (count > 0)
     {
-        if (count > 8) TRACELOG(LOG_WARNING, "GL: Max color buffers limited to 8");
+        if (count > 8) TRACELOG(RL_LOG_WARNING, "GL: Max color buffers limited to 8");
         else
         {
             unsigned int buffers[8] = {
-#if defined(GRAPHICS_API_OPENGL_ES3)
-                GL_COLOR_ATTACHMENT0_EXT,
-                GL_COLOR_ATTACHMENT1_EXT,
-                GL_COLOR_ATTACHMENT2_EXT,
-                GL_COLOR_ATTACHMENT3_EXT,
-                GL_COLOR_ATTACHMENT4_EXT,
-                GL_COLOR_ATTACHMENT5_EXT,
-                GL_COLOR_ATTACHMENT6_EXT,
-                GL_COLOR_ATTACHMENT7_EXT,
-#else
                 GL_COLOR_ATTACHMENT0,
                 GL_COLOR_ATTACHMENT1,
                 GL_COLOR_ATTACHMENT2,
@@ -1902,17 +1918,12 @@
                 GL_COLOR_ATTACHMENT5,
                 GL_COLOR_ATTACHMENT6,
                 GL_COLOR_ATTACHMENT7,
-#endif
             };
 
-#if defined(GRAPHICS_API_OPENGL_ES3)
-            glDrawBuffersEXT(count, buffers);
-#else
             glDrawBuffers(count, buffers);
-#endif
         }
     }
-    else TRACELOG(LOG_WARNING, "GL: One color buffer active by default");
+    else TRACELOG(RL_LOG_WARNING, "GL: One color buffer active by default");
 #endif
 }
 
@@ -1976,6 +1987,15 @@
 #endif
 }
 
+// Disable wire mode
+void rlDisableWireMode(void)
+{
+#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
+    // NOTE: glPolygonMode() not available on OpenGL ES
+    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+#endif
+}
+
 // Enable point mode
 void rlEnablePointMode(void)
 {
@@ -1986,8 +2006,8 @@
 #endif
 }
 
-// Disable wire mode
-void rlDisableWireMode(void)
+// Disable point mode
+void rlDisablePointMode(void)
 {
 #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33)
     // NOTE: glPolygonMode() not available on OpenGL ES
@@ -2006,6 +2026,25 @@
     return width;
 }
 
+// Set the point drawing size
+void rlSetPointSize(float size)
+{
+#if defined(GRAPHICS_API_OPENGL_11)
+    glPointSize(size);
+#endif
+}
+
+// Get the point drawing size
+float rlGetPointSize(void)
+{
+    float size = 1;
+#if defined(GRAPHICS_API_OPENGL_11)
+    glGetFloatv(GL_POINT_SIZE, &size);
+#endif
+    return size;
+
+}
+
 // Enable line aliasing
 void rlEnableSmoothLines(void)
 {
@@ -2111,14 +2150,12 @@
             {
                 // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactors()
                 glBlendFunc(RLGL.State.glBlendSrcFactor, RLGL.State.glBlendDstFactor); glBlendEquation(RLGL.State.glBlendEquation);
-
             } break;
             case RL_BLEND_CUSTOM_SEPARATE:
             {
                 // NOTE: Using GL blend src/dst factors and GL equation configured with rlSetBlendFactorsSeparate()
                 glBlendFuncSeparate(RLGL.State.glBlendSrcFactorRGB, RLGL.State.glBlendDestFactorRGB, RLGL.State.glBlendSrcFactorAlpha, RLGL.State.glBlendDestFactorAlpha);
                 glBlendEquationSeparate(RLGL.State.glBlendEquationRGB, RLGL.State.glBlendEquationAlpha);
-
             } break;
             default: break;
         }
@@ -2222,10 +2259,10 @@
         default: break;
     }
 
-    TRACELOG(LOG_WARNING, "GL: OpenGL debug message: %s", message);
-    TRACELOG(LOG_WARNING, "    > Type: %s", msgType);
-    TRACELOG(LOG_WARNING, "    > Source = %s", msgSource);
-    TRACELOG(LOG_WARNING, "    > Severity = %s", msgSeverity);
+    TRACELOG(RL_LOG_WARNING, "GL: OpenGL debug message: %s", message);
+    TRACELOG(RL_LOG_WARNING, "    > Type: %s", msgType);
+    TRACELOG(RL_LOG_WARNING, "    > Source = %s", msgSource);
+    TRACELOG(RL_LOG_WARNING, "    > Severity = %s", msgSeverity);
 }
 #endif
 
@@ -2236,6 +2273,8 @@
 // Initialize rlgl: OpenGL extensions, default buffers/shaders/textures, OpenGL states
 void rlglInit(int width, int height)
 {
+    isGpuReady = true;
+
     // Enable OpenGL debug context if required
 #if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) && defined(GRAPHICS_API_OPENGL_43)
     if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL))
@@ -2255,6 +2294,7 @@
     // Init default white texture
     unsigned char pixels[4] = { 255, 255, 255, 255 };   // 1 pixel RGBA (4 bytes)
     RLGL.State.defaultTextureId = rlLoadTexture(pixels, 1, 1, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
+    RLGL.State.currentTextureId = RLGL.State.defaultTextureId;
 
     if (RLGL.State.defaultTextureId != 0) TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture loaded successfully", RLGL.State.defaultTextureId);
     else TRACELOG(RL_LOG_WARNING, "TEXTURE: Failed to load default texture");
@@ -2282,6 +2322,16 @@
     RLGL.State.currentMatrix = &RLGL.State.modelview;
 #endif  // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
 
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    // Initialize software renderer backend
+    int result = swInit(width, height);
+    if (result == 0)
+    {
+        TRACELOG(RL_LOG_ERROR, "RLSW: Software renderer initialization failed!");
+        exit(-1);
+    }
+#endif
+
     // Initialize OpenGL default states
     //----------------------------------------------------------
     // Init state: Depth test
@@ -2298,30 +2348,28 @@
     glFrontFace(GL_CCW);                                    // Front face are defined counter clockwise (default)
     glEnable(GL_CULL_FACE);                                 // Enable backface culling
 
-    // Init state: Cubemap seamless
-#if defined(GRAPHICS_API_OPENGL_33)
-    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);                 // Seamless cubemaps (not supported on OpenGL ES 2.0)
-#endif
-
 #if defined(GRAPHICS_API_OPENGL_11)
     // Init state: Color hints (deprecated in OpenGL 3.0+)
     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);      // Improve quality of color and texture coordinate interpolation
     glShadeModel(GL_SMOOTH);                                // Smooth shading between vertex (vertex colors interpolation)
 #endif
-
+#if defined(GRAPHICS_API_OPENGL_33)
+    // Init state: Cubemap seamless
+    glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);                 // Seamless cubemaps (not supported on OpenGL ES 2.0)
+#endif
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     // Store screen size into global variables
     RLGL.State.framebufferWidth = width;
     RLGL.State.framebufferHeight = height;
-
-    TRACELOG(RL_LOG_INFO, "RLGL: Default OpenGL state initialized successfully");
-    //----------------------------------------------------------
 #endif
 
     // Init state: Color/Depth buffers clear
     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);                   // Set clear color (black)
     glClearDepth(1.0f);                                     // Set clear depth value (default)
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear color and depth buffers (depth buffer required for 3D)
+
+    TRACELOG(RL_LOG_INFO, "RLGL: Default OpenGL state initialized successfully");
+    //----------------------------------------------------------
 }
 
 // Vertex Buffer Object deinitialization (memory free)
@@ -2330,11 +2378,16 @@
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     rlUnloadRenderBatch(RLGL.defaultBatch);
 
-    rlUnloadShaderDefault();          // Unload default shader
+    rlUnloadShaderDefault(); // Unload default shader
 
     glDeleteTextures(1, &RLGL.State.defaultTextureId); // Unload default texture
     TRACELOG(RL_LOG_INFO, "TEXTURE: [ID %i] Default texture unloaded successfully", RLGL.State.defaultTextureId);
 #endif
+
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    swClose(); // Unload sofware renderer resources
+#endif
+    isGpuReady = false;
 }
 
 // Load OpenGL extensions
@@ -2428,16 +2481,16 @@
 
     // Get supported extensions list
     GLint numExt = 0;
-    const char **extList = RL_MALLOC(512*sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
+    const char **extList = (const char **)RL_CALLOC(512, sizeof(const char *)); // Allocate 512 strings pointers (2 KB)
     const char *extensions = (const char *)glGetString(GL_EXTENSIONS);  // One big const string
 
     // NOTE: We have to duplicate string because glGetString() returns a const string
-    int size = strlen(extensions) + 1;      // Get extensions string size in bytes
-    char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char));
-    strcpy(extensionsDup, extensions);
+    int extensionsLength = (int)strlen(extensions); // Get extensions string size in bytes
+    char *extensionsDup = (char *)RL_CALLOC(extensionsLength + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
+    strncpy(extensionsDup, extensions, extensionsLength);
     extList[numExt] = extensionsDup;
 
-    for (int i = 0; i < size; i++)
+    for (int i = 0; i < extensionsLength; i++)
     {
         if (extensionsDup[i] == ' ')
         {
@@ -2472,7 +2525,7 @@
         }
 
         // Check instanced rendering support
-        if (strstr(extList[i], (const char*)"instanced_arrays") != NULL)   // Broad check for instanced_arrays
+        if (strstr(extList[i], (const char *)"instanced_arrays") != NULL)   // Broad check for instanced_arrays
         {
             // Specific check
             if (strcmp(extList[i], (const char *)"GL_ANGLE_instanced_arrays") == 0)      // ANGLE
@@ -2505,7 +2558,7 @@
                 glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedEXT");
                 glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedEXT");
             }
-            else if (strcmp(extList[i], (const char*)"GL_NV_draw_instanced") == 0)
+            else if (strcmp(extList[i], (const char *)"GL_NV_draw_instanced") == 0)
             {
                 glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawArraysInstancedNV");
                 glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)((rlglLoadProc)loader)("glDrawElementsInstancedNV");
@@ -2525,11 +2578,11 @@
 
         // Check depth texture support
         if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true;
-        if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true;    // WebGL requires unsized internal format
+        if (strcmp(extList[i], (const char *)"GL_WEBGL_depth_texture") == 0) RLGL.ExtSupported.texDepthWebGL = true; // WebGL requires unsized internal format
         if (RLGL.ExtSupported.texDepthWebGL) RLGL.ExtSupported.texDepth = true;
 
-        if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24;   // Not available on WebGL
-        if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32;   // Not available on WebGL
+        if (strcmp(extList[i], (const char *)"GL_OES_depth24") == 0) RLGL.ExtSupported.maxDepthBits = 24; // Not available on WebGL
+        if (strcmp(extList[i], (const char *)"GL_OES_depth32") == 0) RLGL.ExtSupported.maxDepthBits = 32; // Not available on WebGL
 
         // Check texture compression support: DXT
         if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) ||
@@ -2571,6 +2624,8 @@
     TRACELOG(RL_LOG_INFO, "    > GLSL:     %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    RLGL.loader = (rlglLoadProc)loader;
+
     // NOTE: Anisotropy levels capability is an extension
     #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
         #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
@@ -2628,11 +2683,24 @@
 #endif  // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2
 }
 
+// Get OpenGL procedure address
+void *rlGetProcAddress(const char *procName)
+{
+    void *func = NULL;
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    func = RLGL.loader(procName);
+#endif
+    return func;
+}
+
 // Get current OpenGL version
 int rlGetVersion(void)
 {
     int glVersion = 0;
-#if defined(GRAPHICS_API_OPENGL_11)
+
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    glVersion = RL_OPENGL_11_SOFTWARE;
+#elif defined(GRAPHICS_API_OPENGL_11)
     glVersion = RL_OPENGL_11;
 #endif
 #if defined(GRAPHICS_API_OPENGL_21)
@@ -2724,25 +2792,26 @@
 rlRenderBatch rlLoadRenderBatch(int numBuffers, int bufferElements)
 {
     rlRenderBatch batch = { 0 };
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return batch; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     // Initialize CPU (RAM) vertex buffers (position, texcoord, color data and indexes)
     //--------------------------------------------------------------------------------------------
-    batch.vertexBuffer = (rlVertexBuffer *)RL_MALLOC(numBuffers*sizeof(rlVertexBuffer));
+    batch.vertexBuffer = (rlVertexBuffer *)RL_CALLOC(numBuffers, sizeof(rlVertexBuffer));
 
     for (int i = 0; i < numBuffers; i++)
     {
         batch.vertexBuffer[i].elementCount = bufferElements;
 
-        batch.vertexBuffer[i].vertices = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float));        // 3 float by vertex, 4 vertex by quad
-        batch.vertexBuffer[i].texcoords = (float *)RL_MALLOC(bufferElements*2*4*sizeof(float));       // 2 float by texcoord, 4 texcoord by quad
-        batch.vertexBuffer[i].normals = (float *)RL_MALLOC(bufferElements*3*4*sizeof(float));        // 3 float by vertex, 4 vertex by quad
-        batch.vertexBuffer[i].colors = (unsigned char *)RL_MALLOC(bufferElements*4*4*sizeof(unsigned char));   // 4 float by color, 4 colors by quad
+        batch.vertexBuffer[i].vertices = (float *)RL_CALLOC(bufferElements*3*4, sizeof(float));     // 3 float by vertex, 4 vertex by quad
+        batch.vertexBuffer[i].texcoords = (float *)RL_CALLOC(bufferElements*2*4, sizeof(float));    // 2 float by texcoord, 4 texcoord by quad
+        batch.vertexBuffer[i].normals = (float *)RL_CALLOC(bufferElements*3*4, sizeof(float));      // 3 float by vertex, 4 vertex by quad
+        batch.vertexBuffer[i].colors = (unsigned char *)RL_CALLOC(bufferElements*4*4, sizeof(unsigned char));   // 4 float by color, 4 colors by quad
 #if defined(GRAPHICS_API_OPENGL_33)
-        batch.vertexBuffer[i].indices = (unsigned int *)RL_MALLOC(bufferElements*6*sizeof(unsigned int));      // 6 int by quad (indices)
+        batch.vertexBuffer[i].indices = (unsigned int *)RL_CALLOC(bufferElements*6, sizeof(unsigned int));      // 6 int by quad (indices)
 #endif
 #if defined(GRAPHICS_API_OPENGL_ES2)
-        batch.vertexBuffer[i].indices = (unsigned short *)RL_MALLOC(bufferElements*6*sizeof(unsigned short));  // 6 int by quad (indices)
+        batch.vertexBuffer[i].indices = (unsigned short *)RL_CALLOC(bufferElements*6, sizeof(unsigned short));  // 6 int by quad (indices)
 #endif
 
         for (int j = 0; j < (3*4*bufferElements); j++) batch.vertexBuffer[i].vertices[j] = 0.0f;
@@ -2830,7 +2899,7 @@
 
     // Init draw calls tracking system
     //--------------------------------------------------------------------------------------------
-    batch.draws = (rlDrawCall *)RL_MALLOC(RL_DEFAULT_BATCH_DRAWCALLS*sizeof(rlDrawCall));
+    batch.draws = (rlDrawCall *)RL_CALLOC(RL_DEFAULT_BATCH_DRAWCALLS, sizeof(rlDrawCall));
 
     for (int i = 0; i < RL_DEFAULT_BATCH_DRAWCALLS; i++)
     {
@@ -2846,7 +2915,7 @@
 
     batch.bufferCount = numBuffers;    // Record buffer count
     batch.drawCounter = 1;             // Reset draws counter
-    batch.currentDepth = -1.0f;         // Reset depth value
+    batch.currentDepth = -1.0f;        // Reset depth value
     //--------------------------------------------------------------------------------------------
 #endif
 
@@ -2907,7 +2976,8 @@
     // Update batch vertex buffers
     //------------------------------------------------------------------------------------------------------------
     // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0)
-    // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (use a change detector flag?)
+    // TODO: If no data changed on the CPU arrays there is no need to re-upload data to GPU,
+    // a flag can be used to detect changes but it would imply keeping a copy buffer and memcmp() both, does it worth it?
     if (RLGL.State.vertexCounter > 0)
     {
         // Activate elements VAO
@@ -2933,11 +3003,11 @@
         glBufferSubData(GL_ARRAY_BUFFER, 0, RLGL.State.vertexCounter*4*sizeof(unsigned char), batch->vertexBuffer[batch->currentBuffer].colors);
         //glBufferData(GL_ARRAY_BUFFER, sizeof(float)*4*4*batch->vertexBuffer[batch->currentBuffer].elementCount, batch->vertexBuffer[batch->currentBuffer].colors, GL_DYNAMIC_DRAW);    // Update all buffer
 
-        // NOTE: glMapBuffer() causes sync issue.
-        // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job.
-        // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer().
+        // NOTE: glMapBuffer() causes sync issue
+        // If GPU is working with this buffer, glMapBuffer() will wait(stall) until GPU to finish its job
+        // To avoid waiting (idle), you can call first glBufferData() with NULL pointer before glMapBuffer()
         // If you do that, the previous data in PBO will be discarded and glMapBuffer() returns a new
-        // allocated pointer immediately even if GPU is still working with the previous data.
+        // allocated pointer immediately even if GPU is still working with the previous data
 
         // Another option: map the buffer object into client's memory
         // Probably this code could be moved somewhere else...
@@ -2990,7 +3060,7 @@
             }
 
             // WARNING: For the following setup of the view, model, and normal matrices, it is expected that
-            // transformations and rendering occur between rlPushMatrix and rlPopMatrix.
+            // transformations and rendering occur between rlPushMatrix() and rlPopMatrix()
 
             if (RLGL.State.currentShaderLocs[RL_SHADER_LOC_MATRIX_VIEW] != -1)
             {
@@ -3054,21 +3124,21 @@
 
             for (int i = 0, vertexOffset = 0; i < batch->drawCounter; i++)
             {
-                // Bind current draw call texture, activated as GL_TEXTURE0 and Bound to sampler2D texture0 by default
+                // Bind current draw call texture, activated as GL_TEXTURE0 and bound to sampler2D texture0 by default
                 glBindTexture(GL_TEXTURE_2D, batch->draws[i].textureId);
 
                 if ((batch->draws[i].mode == RL_LINES) || (batch->draws[i].mode == RL_TRIANGLES)) glDrawArrays(batch->draws[i].mode, vertexOffset, batch->draws[i].vertexCount);
                 else
                 {
-#if defined(GRAPHICS_API_OPENGL_33)
+    #if defined(GRAPHICS_API_OPENGL_33)
                     // We need to define the number of indices to be processed: elementCount*6
                     // NOTE: The final parameter tells the GPU the offset in bytes from the
                     // start of the index buffer to the location of the first index to process
                     glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_INT, (GLvoid *)(vertexOffset/4*6*sizeof(GLuint)));
-#endif
-#if defined(GRAPHICS_API_OPENGL_ES2)
+    #endif
+    #if defined(GRAPHICS_API_OPENGL_ES2)
                     glDrawElements(GL_TRIANGLES, batch->draws[i].vertexCount/4*6, GL_UNSIGNED_SHORT, (GLvoid *)(vertexOffset/4*6*sizeof(GLushort)));
-#endif
+    #endif
                 }
 
                 vertexOffset += (batch->draws[i].vertexCount + batch->draws[i].vertexAlignment);
@@ -3177,6 +3247,7 @@
 unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
     glBindTexture(GL_TEXTURE_2D, 0);    // Free any old binding
 
@@ -3230,6 +3301,7 @@
     int mipWidth = width;
     int mipHeight = height;
     int mipOffset = 0;          // Mipmap data offset, only used for tracelog
+    (void)mipOffset;            // Used to avoid gcc warnings about unused variable
 
     // NOTE: Added pointer math separately from function to avoid UBSAN complaining
     unsigned char *dataPtr = NULL;
@@ -3243,7 +3315,7 @@
         unsigned int glInternalFormat, glFormat, glType;
         rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType);
 
-        TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset);
+        TRACELOG(RL_LOG_DEBUG, "TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset);
 
         if (glInternalFormat != 0)
         {
@@ -3272,8 +3344,8 @@
 
         mipWidth /= 2;
         mipHeight /= 2;
-        mipOffset += mipSize;       // Increment offset position to next mipmap
-        if (data != NULL) dataPtr += mipSize;         // Increment data pointer to next mipmap
+        mipOffset += mipSize; // Increment offset position to next mipmap
+        if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap
 
         // Security check for NPOT textures
         if (mipWidth < 1) mipWidth = 1;
@@ -3307,9 +3379,18 @@
 #if defined(GRAPHICS_API_OPENGL_33)
     if (mipmapCount > 1)
     {
-        // Activate Trilinear filtering if mipmaps are available
+        // Activate trilinear filtering if mipmaps are available
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+
+        // Define the maximum number of mipmap levels to be used, 0 is base texture size
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapCount - 1);
+
+        // Check if the loaded texture with mipmaps is complete,
+        // uncomplete textures will draw in black if mipmap filtering is required
+        //GLint complete = 0;
+        //glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_IMMUTABLE_FORMAT, &complete);
     }
 #endif
 
@@ -3331,6 +3412,7 @@
 unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     // In case depth textures not supported, we force renderbuffer usage
@@ -3386,11 +3468,18 @@
 // Load texture cubemap
 // NOTE: Cubemap data is expected to be 6 images in a single data array (one after the other),
 // expected the following convention: +X, -X, +Y, -Y, +Z, -Z
-unsigned int rlLoadTextureCubemap(const void *data, int size, int format)
+unsigned int rlLoadTextureCubemap(const void *data, int size, int format, int mipmapCount)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    int mipSize = size;
+
+    // NOTE: Added pointer math separately from function to avoid UBSAN complaining
+    unsigned char *dataPtr = NULL;
+    if (data != NULL) dataPtr = (unsigned char *)data;
+
     unsigned int dataSize = rlGetPixelDataSize(size, size, format);
 
     glGenTextures(1, &id);
@@ -3401,24 +3490,28 @@
 
     if (glInternalFormat != 0)
     {
-        // Load cubemap faces
-        for (unsigned int i = 0; i < 6; i++)
+        // Load cubemap faces/mipmaps
+        for (int i = 0; i < 6*mipmapCount; i++)
         {
+            int mipmapLevel = i/6;
+            int face = i%6;
+
             if (data == NULL)
             {
                 if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)
                 {
-                    if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)
-                            || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
-                        TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported");
-                    else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL);
+                    if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) ||
+                        (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32) ||
+                        (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) ||
+                        (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported");
+                    else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, NULL);
                 }
                 else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format");
             }
             else
             {
-                if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, (unsigned char *)data + i*dataSize);
-                else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, dataSize, (unsigned char *)data + i*dataSize);
+                if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, glFormat, glType, (unsigned char *)dataPtr + face*dataSize);
+                else glCompressedTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, mipmapLevel, glInternalFormat, mipSize, mipSize, 0, dataSize, (unsigned char *)dataPtr + face*dataSize);
             }
 
 #if defined(GRAPHICS_API_OPENGL_33)
@@ -3437,11 +3530,23 @@
                 glTexParameteriv(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
             }
 #endif
+            if (face == 5)
+            {
+                mipSize /= 2;
+                if (data != NULL) dataPtr += dataSize*6; // Increment data pointer to next mipmap
+
+                // Security check for NPOT textures
+                if (mipSize < 1) mipSize = 1;
+
+                dataSize = rlGetPixelDataSize(mipSize, mipSize, format);
+            }
         }
     }
 
     // Set cubemap texture sampling parameters
-    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    if (mipmapCount > 1) glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    else glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
@@ -3600,8 +3705,8 @@
     //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
     //glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &format);
 
-    // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding.
-    // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting.
+    // NOTE: Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4 byte boundary by default, which may add some padding
+    // Use glPixelStorei to modify padding with the GL_[UN]PACK_ALIGNMENT setting
     // GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.)
     // GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.)
     glPixelStorei(GL_PACK_ALIGNMENT, 1);
@@ -3612,7 +3717,7 @@
 
     if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB))
     {
-        pixels = RL_MALLOC(size);
+        pixels = RL_CALLOC(size, 1);
         glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels);
     }
     else TRACELOG(RL_LOG_WARNING, "TEXTURE: [ID %i] Data retrieval not suported for pixel format (%i)", id, format);
@@ -3622,7 +3727,7 @@
 
 #if defined(GRAPHICS_API_OPENGL_ES2)
     // glGetTexImage() is not available on OpenGL ES 2.0
-    // Texture width and height are required on OpenGL ES 2.0. There is no way to get it from texture id.
+    // Texture width and height are required on OpenGL ES 2.0, there is no way to get it from texture id
     // Two possible Options:
     // 1 - Bind texture to color fbo attachment and glReadPixels()
     // 2 - Create an fbo, activate it, render quad with texture, glReadPixels()
@@ -3637,7 +3742,7 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0);
 
     // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format
-    pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8));
+    pixels = RL_CALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8), 1);
     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -3649,32 +3754,58 @@
     return pixels;
 }
 
+// Copy framebuffer pixel data to internal buffer
+void rlCopyFramebuffer(int x, int y, int width, int height, int format, void *pixels)
+{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    unsigned int glInternalFormat, glFormat, glType;
+    rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); // Get OpenGL texture format
+    swCopyFramebuffer(x, y, width, height, glFormat, glType, pixels);
+#endif
+}
+
+// Resize internal framebuffer
+void rlResizeFramebuffer(int width, int height)
+{
+#if defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    swResizeFramebuffer(width, height);
+#endif
+}
+
 // Read screen pixel data (color buffer)
 unsigned char *rlReadScreenPixels(int width, int height)
 {
-    unsigned char *screenData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char));
+    unsigned char *imgData = (unsigned char *)RL_CALLOC(width*height*4, sizeof(unsigned char));
 
     // NOTE 1: glReadPixels returns image flipped vertically -> (0,0) is the bottom left corner of the framebuffer
     // NOTE 2: We are getting alpha channel! Be careful, it can be transparent if not cleared properly!
-    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, screenData);
+    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, imgData);
 
     // Flip image vertically!
-    unsigned char *imgData = (unsigned char *)RL_MALLOC(width*height*4*sizeof(unsigned char));
-
-    for (int y = height - 1; y >= 0; y--)
+    // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it!
+    for (int y = height - 1; y >= height/2; y--)
     {
-        for (int x = 0; x < (width*4); x++)
+        for (int x = 0; x < (width*4); x += 4)
         {
-            imgData[((height - 1) - y)*width*4 + x] = screenData[(y*width*4) + x];  // Flip line
+            unsigned int s = ((height - 1) - y)*width*4 + x;
+            unsigned int e = y*width*4 + x;
 
-            // Set alpha component value to 255 (no trasparent image retrieval)
-            // NOTE: Alpha value has already been applied to RGB in framebuffer, we don't need it!
-            if (((x + 1)%4) == 0) imgData[((height - 1) - y)*width*4 + x] = 255;
+            unsigned char r = imgData[s];
+            unsigned char g = imgData[s+1];
+            unsigned char b = imgData[s+2];
+
+            imgData[s] = imgData[e];
+            imgData[s+1] = imgData[e+1];
+            imgData[s+2] = imgData[e+2];
+            imgData[s+3] = 255; // Set alpha component value to 255 (no trasparent image retrieval)
+
+            imgData[e] = r;
+            imgData[e+1] = g;
+            imgData[e+2] = b;
+            imgData[e+3] = 255; // Ditto
         }
     }
 
-    RL_FREE(screenData);
-
     return imgData;     // NOTE: image data should be freed
 }
 
@@ -3685,8 +3816,9 @@
 unsigned int rlLoadFramebuffer(void)
 {
     unsigned int fboId = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return fboId; }
 
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glGenFramebuffers(1, &fboId);       // Create the framebuffer object
     glBindFramebuffer(GL_FRAMEBUFFER, 0);   // Unbind any framebuffer
 #endif
@@ -3698,7 +3830,7 @@
 // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture
 void rlFramebufferAttach(unsigned int fboId, unsigned int texId, int attachType, int texType, int mipLevel)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glBindFramebuffer(GL_FRAMEBUFFER, fboId);
 
     switch (attachType)
@@ -3715,19 +3847,16 @@
             if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_2D, texId, mipLevel);
             else if (texType == RL_ATTACHMENT_RENDERBUFFER) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_RENDERBUFFER, texId);
             else if (texType >= RL_ATTACHMENT_CUBEMAP_POSITIVE_X) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachType, GL_TEXTURE_CUBE_MAP_POSITIVE_X + texType, texId, mipLevel);
-
         } break;
         case RL_ATTACHMENT_DEPTH:
         {
             if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel);
             else if (texType == RL_ATTACHMENT_RENDERBUFFER)  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, texId);
-
         } break;
         case RL_ATTACHMENT_STENCIL:
         {
             if (texType == RL_ATTACHMENT_TEXTURE2D) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texId, mipLevel);
             else if (texType == RL_ATTACHMENT_RENDERBUFFER)  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, texId);
-
         } break;
         default: break;
     }
@@ -3741,7 +3870,7 @@
 {
     bool result = false;
 
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     glBindFramebuffer(GL_FRAMEBUFFER, id);
 
     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
@@ -3772,7 +3901,7 @@
 // NOTE: All attached textures/cubemaps/renderbuffers are also deleted
 void rlUnloadFramebuffer(unsigned int id)
 {
-#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT)
+#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2))
     // Query depth attachment to automatically delete texture/renderbuffer
     int depthType = 0, depthId = 0;
     glBindFramebuffer(GL_FRAMEBUFFER, id);   // Bind framebuffer to query depth texture type
@@ -3780,7 +3909,7 @@
 
     // TODO: Review warning retrieving object name in WebGL
     // WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name
-    // https://registry.khronos.org/webgl/specs/latest/1.0/
+    // REF: https://registry.khronos.org/webgl/specs/latest/1.0/
     glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId);
 
     unsigned int depthIdU = (unsigned int)depthId;
@@ -3788,7 +3917,7 @@
     else if (depthType == GL_TEXTURE) glDeleteTextures(1, &depthIdU);
 
     // NOTE: If a texture object is deleted while its image is attached to the *currently bound* framebuffer,
-    // the texture image is automatically detached from the currently bound framebuffer.
+    // the texture image is automatically detached from the currently bound framebuffer
 
     glBindFramebuffer(GL_FRAMEBUFFER, 0);
     glDeleteFramebuffers(1, &id);
@@ -3803,6 +3932,7 @@
 unsigned int rlLoadVertexBuffer(const void *buffer, int size, bool dynamic)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     glGenBuffers(1, &id);
@@ -3817,6 +3947,7 @@
 unsigned int rlLoadVertexBufferElement(const void *buffer, int size, bool dynamic)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     glGenBuffers(1, &id);
@@ -3937,7 +4068,7 @@
 void rlDrawVertexArrayInstanced(int offset, int count, int instances)
 {
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    glDrawArraysInstanced(GL_TRIANGLES, 0, count, instances);
+    glDrawArraysInstanced(GL_TRIANGLES, offset, count, instances);
 #endif
 }
 
@@ -3953,10 +4084,10 @@
 #endif
 }
 
-#if defined(GRAPHICS_API_OPENGL_11)
 // Enable vertex state pointer
 void rlEnableStatePointer(int vertexAttribType, void *buffer)
 {
+#if defined(GRAPHICS_API_OPENGL_11)
     if (buffer != NULL) glEnableClientState(vertexAttribType);
     switch (vertexAttribType)
     {
@@ -3967,25 +4098,27 @@
         //case GL_INDEX_ARRAY: if (buffer != NULL) glIndexPointer(GL_SHORT, 0, buffer); break; // Indexed colors
         default: break;
     }
+#endif
 }
 
 // Disable vertex state pointer
 void rlDisableStatePointer(int vertexAttribType)
 {
+#if defined(GRAPHICS_API_OPENGL_11)
     glDisableClientState(vertexAttribType);
-}
 #endif
+}
 
 // Load vertex array object (VAO)
 unsigned int rlLoadVertexArray(void)
 {
     unsigned int vaoId = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return vaoId; }
+
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    if (RLGL.ExtSupported.vao)
-    {
-        glGenVertexArrays(1, &vaoId);
-    }
+    if (RLGL.ExtSupported.vao) glGenVertexArrays(1, &vaoId);
 #endif
+
     return vaoId;
 }
 
@@ -4040,6 +4173,7 @@
 unsigned int rlLoadShaderCode(const char *vsCode, const char *fsCode)
 {
     unsigned int id = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return id; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     unsigned int vertexShaderId = 0;
@@ -4103,7 +4237,7 @@
                 glGetActiveUniform(id, i, sizeof(name) - 1, &namelen, &num, &type, name);
 
                 name[namelen] = 0;
-                TRACELOGD("SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name));
+                TRACELOG(RL_LOG_DEBUG, "SHADER: [ID %i] Active uniform (%s) set at location: %i", id, name, glGetUniformLocation(id, name));
             }
         }
         */
@@ -4116,129 +4250,134 @@
 // Compile custom shader and return shader id
 unsigned int rlCompileShader(const char *shaderCode, int type)
 {
-    unsigned int shader = 0;
+    unsigned int shaderId = 0;
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    shader = glCreateShader(type);
-    glShaderSource(shader, 1, &shaderCode, NULL);
+    shaderId = glCreateShader(type);
+    glShaderSource(shaderId, 1, &shaderCode, NULL);
 
     GLint success = 0;
-    glCompileShader(shader);
-    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
+    glCompileShader(shaderId);
+    glGetShaderiv(shaderId, GL_COMPILE_STATUS, &success);
 
     if (success == GL_FALSE)
     {
         switch (type)
         {
-            case GL_VERTEX_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile vertex shader code", shader); break;
-            case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile fragment shader code", shader); break;
+            case GL_VERTEX_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile vertex shader code", shaderId); break;
+            case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile fragment shader code", shaderId); break;
             //case GL_GEOMETRY_SHADER:
         #if defined(GRAPHICS_API_OPENGL_43)
-            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shader); break;
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to compile compute shader code", shaderId); break;
         #elif defined(GRAPHICS_API_OPENGL_33)
-            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shaderId); break;
         #endif
             default: break;
         }
 
         int maxLength = 0;
-        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+        glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &maxLength);
 
         if (maxLength > 0)
         {
             int length = 0;
             char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
-            glGetShaderInfoLog(shader, maxLength, &length, log);
-            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shader, log);
+            glGetShaderInfoLog(shaderId, maxLength, &length, log);
+            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Compile error: %s", shaderId, log);
             RL_FREE(log);
         }
 
-        shader = 0;
+        // Unload object allocated by glCreateShader(),
+        // despite failing in the compilation process
+        glDeleteShader(shaderId);
+        shaderId = 0;
     }
     else
     {
         switch (type)
         {
-            case GL_VERTEX_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Vertex shader compiled successfully", shader); break;
-            case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Fragment shader compiled successfully", shader); break;
+            case GL_VERTEX_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Vertex shader compiled successfully", shaderId); break;
+            case GL_FRAGMENT_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Fragment shader compiled successfully", shaderId); break;
             //case GL_GEOMETRY_SHADER:
         #if defined(GRAPHICS_API_OPENGL_43)
-            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shader); break;
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader compiled successfully", shaderId); break;
         #elif defined(GRAPHICS_API_OPENGL_33)
-            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shader); break;
+            case GL_COMPUTE_SHADER: TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43", shaderId); break;
         #endif
             default: break;
         }
     }
 #endif
 
-    return shader;
+    return shaderId;
 }
 
 // Load custom shader strings and return program id
 unsigned int rlLoadShaderProgram(unsigned int vShaderId, unsigned int fShaderId)
 {
-    unsigned int program = 0;
+    unsigned int programId = 0;
+    if (!isGpuReady) { TRACELOG(RL_LOG_WARNING, "GL: GPU is not ready to load data, trying to load before InitWindow()?"); return programId; }
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
     GLint success = 0;
-    program = glCreateProgram();
+    programId = glCreateProgram();
 
-    glAttachShader(program, vShaderId);
-    glAttachShader(program, fShaderId);
+    glAttachShader(programId, vShaderId);
+    glAttachShader(programId, fShaderId);
 
     // NOTE: Default attribute shader locations must be Bound before linking
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_INSTANCE_TX, RL_DEFAULT_SHADER_ATTRIB_NAME_INSTANCE_TX);
 
 #ifdef RL_SUPPORT_MESH_GPU_SKINNING
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
-    glBindAttribLocation(program, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEIDS);
+    glBindAttribLocation(programId, RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, RL_DEFAULT_SHADER_ATTRIB_NAME_BONEWEIGHTS);
 #endif
 
     // NOTE: If some attrib name is no found on the shader, it locations becomes -1
 
-    glLinkProgram(program);
+    glLinkProgram(programId);
 
     // NOTE: All uniform variables are intitialised to 0 when a program links
 
-    glGetProgramiv(program, GL_LINK_STATUS, &success);
+    glGetProgramiv(programId, GL_LINK_STATUS, &success);
 
     if (success == GL_FALSE)
     {
-        TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link shader program", program);
+        TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link shader program", programId);
 
         int maxLength = 0;
-        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
+        glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
 
         if (maxLength > 0)
         {
             int length = 0;
             char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
-            glGetProgramInfoLog(program, maxLength, &length, log);
-            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log);
+            glGetProgramInfoLog(programId, maxLength, &length, log);
+            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log);
             RL_FREE(log);
         }
 
-        glDeleteProgram(program);
+        glDeleteProgram(programId);
 
-        program = 0;
+        programId = 0;
     }
     else
     {
         // Get the size of compiled shader program (not available on OpenGL ES 2.0)
-        // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero.
+        // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero
         //GLint binarySize = 0;
         //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize);
 
-        TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully", program);
+        TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Program shader loaded successfully", programId);
     }
 #endif
-    return program;
+    return programId;
 }
 
 // Unload shader program
@@ -4252,6 +4391,7 @@
 }
 
 // Get shader location uniform
+// NOTE: First parameter refers to shader program id
 int rlGetLocationUniform(unsigned int shaderId, const char *uniformName)
 {
     int location = -1;
@@ -4265,6 +4405,7 @@
 }
 
 // Get shader location attribute
+// NOTE: First parameter refers to shader program id
 int rlGetLocationAttrib(unsigned int shaderId, const char *attribName)
 {
     int location = -1;
@@ -4299,8 +4440,6 @@
     #endif
         case RL_SHADER_UNIFORM_SAMPLER2D: glUniform1iv(locIndex, count, (int *)value); break;
         default: TRACELOG(RL_LOG_WARNING, "SHADER: Failed to set uniform value, data type not recognized");
-
-        // TODO: Support glUniform1uiv(), glUniform2uiv(), glUniform3uiv(), glUniform4uiv()
     }
 #endif
 }
@@ -4337,8 +4476,12 @@
 // Set shader value uniform matrix
 void rlSetUniformMatrices(int locIndex, const Matrix *matrices, int count)
 {
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    glUniformMatrix4fv(locIndex, count, true, (const float*)matrices);
+#if defined(GRAPHICS_API_OPENGL_33)
+    glUniformMatrix4fv(locIndex, count, true, (const float *)matrices);
+#elif defined(GRAPHICS_API_OPENGL_ES2)
+    // WARNING: WebGL does not support Matrix transpose ("true" parameter)
+    // REF: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/uniformMatrix
+    glUniformMatrix4fv(locIndex, count, false, (const float *)matrices);
 #endif
 }
 
@@ -4386,52 +4529,52 @@
 // Load compute shader program
 unsigned int rlLoadComputeShaderProgram(unsigned int shaderId)
 {
-    unsigned int program = 0;
+    unsigned int programId = 0;
 
 #if defined(GRAPHICS_API_OPENGL_43)
     GLint success = 0;
-    program = glCreateProgram();
-    glAttachShader(program, shaderId);
-    glLinkProgram(program);
+    programId = glCreateProgram();
+    glAttachShader(programId, shaderId);
+    glLinkProgram(programId);
 
     // NOTE: All uniform variables are intitialised to 0 when a program links
 
-    glGetProgramiv(program, GL_LINK_STATUS, &success);
+    glGetProgramiv(programId, GL_LINK_STATUS, &success);
 
     if (success == GL_FALSE)
     {
-        TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", program);
+        TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to link compute shader program", programId);
 
         int maxLength = 0;
-        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
+        glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &maxLength);
 
         if (maxLength > 0)
         {
             int length = 0;
             char *log = (char *)RL_CALLOC(maxLength, sizeof(char));
-            glGetProgramInfoLog(program, maxLength, &length, log);
-            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", program, log);
+            glGetProgramInfoLog(programId, maxLength, &length, log);
+            TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Link error: %s", programId, log);
             RL_FREE(log);
         }
 
-        glDeleteProgram(program);
+        glDeleteProgram(programId);
 
-        program = 0;
+        programId = 0;
     }
     else
     {
         // Get the size of compiled shader program (not available on OpenGL ES 2.0)
-        // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero.
+        // NOTE: If GL_LINK_STATUS is GL_FALSE, program binary length is zero
         //GLint binarySize = 0;
         //glGetProgramiv(id, GL_PROGRAM_BINARY_LENGTH, &binarySize);
 
-        TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", program);
+        TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Compute shader program loaded successfully", programId);
     }
 #else
     TRACELOG(RL_LOG_WARNING, "SHADER: Compute shaders not enabled. Define GRAPHICS_API_OPENGL_43");
 #endif
 
-    return program;
+    return programId;
 }
 
 // Dispatch compute shader (equivalent to *draw* for graphics pilepine)
@@ -4814,7 +4957,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Functions Definition
 //----------------------------------------------------------------------------------
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 // Load default shader (just vertex positioning and texture coloring)
@@ -4942,7 +5085,7 @@
         RLGL.State.defaultShaderLocs[RL_SHADER_LOC_VERTEX_COLOR] = glGetAttribLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR);
 
         // Set default shader locations: uniform locations
-        RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP]  = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
+        RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MATRIX_MVP] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_MVP);
         RLGL.State.defaultShaderLocs[RL_SHADER_LOC_COLOR_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR);
         RLGL.State.defaultShaderLocs[RL_SHADER_LOC_MAP_DIFFUSE] = glGetUniformLocation(RLGL.State.defaultShaderId, RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0);
     }
@@ -5072,31 +5215,56 @@
         case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB:
         case RL_PIXELFORMAT_COMPRESSED_ETC2_RGB:
         case RL_PIXELFORMAT_COMPRESSED_PVRT_RGB:
-        case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
+        case RL_PIXELFORMAT_COMPRESSED_PVRT_RGBA: // 8 bytes per each 4x4 block
+        {
+            int blockWidth = (width + 3)/4;
+            int blockHeight = (height + 3)/4;
+            dataSize = blockWidth*blockHeight*8;
+        } break;
         case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA:
         case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA:
         case RL_PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
-        case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
-        case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
+        case RL_PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: // 16 bytes per each 4x4 block
+        {
+            int blockWidth = (width + 3)/4;
+            int blockHeight = (height + 3)/4;
+            dataSize = blockWidth*blockHeight*16;
+        } break;
+        case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block
+        {
+            int blockWidth = (width + 3)/4;
+            int blockHeight = (height + 3)/4;
+            dataSize = blockWidth*blockHeight*4;
+        } break;
         default: break;
     }
 
-    double bytesPerPixel = (double)bpp/8.0;
-    dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
-
-    // Most compressed formats works on 4x4 blocks,
-    // if texture is smaller, minimum dataSize is 8 or 16
-    if ((width < 4) && (height < 4))
+    // Compute dataSize for uncompressed texture data (no blocks)
+    if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) &&
+        (format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
     {
-        if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
-        else if ((format >= RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
+        double bytesPerPixel = (double)bpp/8.0;
+        dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
     }
 
     return dataSize;
 }
 
 // Auxiliar math functions
+//-------------------------------------------------------------------------------
+// Get identity matrix
+static Matrix rlMatrixIdentity(void)
+{
+    Matrix result = {
+        1.0f, 0.0f, 0.0f, 0.0f,
+        0.0f, 1.0f, 0.0f, 0.0f,
+        0.0f, 0.0f, 1.0f, 0.0f,
+        0.0f, 0.0f, 0.0f, 1.0f
+    };
 
+    return result;
+}
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 // Get float array of matrix data
 static rl_float16 rlMatrixToFloatV(Matrix mat)
 {
@@ -5122,19 +5290,6 @@
     return result;
 }
 
-// Get identity matrix
-static Matrix rlMatrixIdentity(void)
-{
-    Matrix result = {
-        1.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 1.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 1.0f, 0.0f,
-        0.0f, 0.0f, 0.0f, 1.0f
-    };
-
-    return result;
-}
-
 // Get two matrix multiplication
 // NOTE: When multiplying matrices... the order matters!
 static Matrix rlMatrixMultiply(Matrix left, Matrix right)
@@ -5232,5 +5387,6 @@
 
     return result;
 }
+#endif
 
 #endif  // RLGL_IMPLEMENTATION
diff --git a/raylib/src/rmodels.c b/raylib/src/rmodels.c
--- a/raylib/src/rmodels.c
+++ b/raylib/src/rmodels.c
@@ -12,6776 +12,7071 @@
 *       #define SUPPORT_FILEFORMAT_GLTF
 *       #define SUPPORT_FILEFORMAT_VOX
 *       #define SUPPORT_FILEFORMAT_M3D
-*           Selected desired fileformats to be supported for model data loading.
-*
-*       #define SUPPORT_MESH_GENERATION
-*           Support procedural mesh generation functions, uses external par_shapes.h library
-*           NOTE: Some generated meshes DO NOT include generated texture coordinates
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#include "raylib.h"         // Declares module functions
-
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"     // Defines module configuration flags
-#endif
-
-#if defined(SUPPORT_MODULE_RMODELS)
-
-#include "utils.h"          // Required for: TRACELOG(), LoadFileData(), LoadFileText(), SaveFileText()
-#include "rlgl.h"           // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
-#include "raymath.h"        // Required for: Vector3, Quaternion and Matrix functionality
-
-#include <stdio.h>          // Required for: sprintf()
-#include <stdlib.h>         // Required for: malloc(), calloc(), free()
-#include <string.h>         // Required for: memcmp(), strlen(), strncpy()
-#include <math.h>           // Required for: sinf(), cosf(), sqrtf(), fabsf()
-
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
-    #define TINYOBJ_MALLOC RL_MALLOC
-    #define TINYOBJ_CALLOC RL_CALLOC
-    #define TINYOBJ_REALLOC RL_REALLOC
-    #define TINYOBJ_FREE RL_FREE
-
-    #define TINYOBJ_LOADER_C_IMPLEMENTATION
-    #include "external/tinyobj_loader_c.h"      // OBJ/MTL file formats loading
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_GLTF)
-    #define CGLTF_MALLOC RL_MALLOC
-    #define CGLTF_FREE RL_FREE
-
-    #define CGLTF_IMPLEMENTATION
-    #include "external/cgltf.h"         // glTF file format loading
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_VOX)
-    #define VOX_MALLOC RL_MALLOC
-    #define VOX_CALLOC RL_CALLOC
-    #define VOX_REALLOC RL_REALLOC
-    #define VOX_FREE RL_FREE
-
-    #define VOX_LOADER_IMPLEMENTATION
-    #include "external/vox_loader.h"    // VOX file format loading (MagikaVoxel)
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_M3D)
-    #define M3D_MALLOC RL_MALLOC
-    #define M3D_REALLOC RL_REALLOC
-    #define M3D_FREE RL_FREE
-
-    #define M3D_IMPLEMENTATION
-    #include "external/m3d.h"           // Model3D file format loading
-#endif
-
-#if defined(SUPPORT_MESH_GENERATION)
-    #define PAR_MALLOC(T, N) ((T*)RL_MALLOC(N*sizeof(T)))
-    #define PAR_CALLOC(T, N) ((T*)RL_CALLOC(N*sizeof(T), 1))
-    #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N)))
-    #define PAR_FREE RL_FREE
-
-    #if defined(_MSC_VER)           // Disable some MSVC warning
-        #pragma warning(push)
-        #pragma warning(disable : 4244)
-        #pragma warning(disable : 4305)
-    #endif
-
-    #define PAR_SHAPES_IMPLEMENTATION
-    #include "external/par_shapes.h"    // Shapes 3d parametric generation
-
-    #if defined(_MSC_VER)
-        #pragma warning(pop)        // Disable MSVC warning suppression
-    #endif
-#endif
-
-#if defined(_WIN32)
-    #include <direct.h>     // Required for: _chdir() [Used in LoadOBJ()]
-    #define CHDIR _chdir
-#else
-    #include <unistd.h>     // Required for: chdir() (POSIX) [Used in LoadOBJ()]
-    #define CHDIR chdir
-#endif
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#ifndef MAX_MATERIAL_MAPS
-    #define MAX_MATERIAL_MAPS       12    // Maximum number of maps supported
-#endif
-#ifndef MAX_MESH_VERTEX_BUFFERS
-    #define MAX_MESH_VERTEX_BUFFERS  9    // Maximum vertex buffers (VBO) per mesh
-#endif
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-// ...
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-// ...
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
-//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_OBJ)
-static Model LoadOBJ(const char *fileName);     // Load OBJ mesh data
-#endif
-#if defined(SUPPORT_FILEFORMAT_IQM)
-static Model LoadIQM(const char *fileName);     // Load IQM mesh data
-static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount);   // Load IQM animation data
-#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
-static Model LoadGLTF(const char *fileName);    // Load GLTF mesh data
-static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount);  // Load GLTF animation data
-#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
-static Model LoadVOX(const char *filename);     // Load VOX mesh data
-#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
-static Model LoadM3D(const char *filename);     // Load M3D mesh data
-static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount);   // Load M3D animation data
-#endif
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
-static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount);  // Process obj materials
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition
-//----------------------------------------------------------------------------------
-
-// Draw a line in 3D world space
-void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
-{
-    rlBegin(RL_LINES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-        rlVertex3f(startPos.x, startPos.y, startPos.z);
-        rlVertex3f(endPos.x, endPos.y, endPos.z);
-    rlEnd();
-}
-
-// Draw a point in 3D space, actually a small line
-void DrawPoint3D(Vector3 position, Color color)
-{
-    rlPushMatrix();
-        rlTranslatef(position.x, position.y, position.z);
-        rlBegin(RL_LINES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-            rlVertex3f(0.0f, 0.0f, 0.0f);
-            rlVertex3f(0.0f, 0.0f, 0.1f);
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a circle in 3D world space
-void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
-{
-    rlPushMatrix();
-        rlTranslatef(center.x, center.y, center.z);
-        rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
-
-        rlBegin(RL_LINES);
-            for (int i = 0; i < 360; i += 10)
-            {
-                rlColor4ub(color.r, color.g, color.b, color.a);
-
-                rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f);
-                rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f);
-            }
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a color-filled triangle (vertex in counter-clockwise order!)
-void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color)
-{
-    rlBegin(RL_TRIANGLES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-        rlVertex3f(v1.x, v1.y, v1.z);
-        rlVertex3f(v2.x, v2.y, v2.z);
-        rlVertex3f(v3.x, v3.y, v3.z);
-    rlEnd();
-}
-
-// Draw a triangle strip defined by points
-void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color)
-{
-    if (pointCount < 3) return; // Security check
-
-    rlBegin(RL_TRIANGLES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        for (int i = 2; i < pointCount; i++)
-        {
-            if ((i%2) == 0)
-            {
-                rlVertex3f(points[i].x, points[i].y, points[i].z);
-                rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
-                rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
-            }
-            else
-            {
-                rlVertex3f(points[i].x, points[i].y, points[i].z);
-                rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
-                rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
-            }
-        }
-    rlEnd();
-}
-
-// Draw cube
-// NOTE: Cube position is the center position
-void DrawCube(Vector3 position, float width, float height, float length, Color color)
-{
-    float x = 0.0f;
-    float y = 0.0f;
-    float z = 0.0f;
-
-    rlPushMatrix();
-        // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
-        rlTranslatef(position.x, position.y, position.z);
-        //rlRotatef(45, 0, 1, 0);
-        //rlScalef(1.0f, 1.0f, 1.0f);   // NOTE: Vertices are directly scaled on definition
-
-        rlBegin(RL_TRIANGLES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            // Front face
-            rlNormal3f(0.0f, 0.0f, 1.0f);
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
-
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Right
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
-
-            // Back face
-            rlNormal3f(0.0f, 0.0f, -1.0f);
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Left
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
-
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
-
-            // Top face
-            rlNormal3f(0.0f, 1.0f, 0.0f);
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Bottom Left
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right
-
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right
-
-            // Bottom face
-            rlNormal3f(0.0f, -1.0f, 0.0f);
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Left
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
-
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top Right
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Left
-
-            // Right face
-            rlNormal3f(1.0f, 0.0f, 0.0f);
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left
-
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left
-
-            // Left face
-            rlNormal3f(-1.0f, 0.0f, 0.0f);
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Right
-
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw cube (Vector version)
-void DrawCubeV(Vector3 position, Vector3 size, Color color)
-{
-    DrawCube(position, size.x, size.y, size.z, color);
-}
-
-// Draw cube wires
-void DrawCubeWires(Vector3 position, float width, float height, float length, Color color)
-{
-    float x = 0.0f;
-    float y = 0.0f;
-    float z = 0.0f;
-
-    rlPushMatrix();
-        rlTranslatef(position.x, position.y, position.z);
-
-        rlBegin(RL_LINES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            // Front face
-            //------------------------------------------------------------------
-            // Bottom line
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom left
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom right
-
-            // Left line
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom right
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right
-
-            // Top line
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left
-
-            // Right line
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom left
-
-            // Back face
-            //------------------------------------------------------------------
-            // Bottom line
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom left
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom right
-
-            // Left line
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom right
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right
-
-            // Top line
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left
-
-            // Right line
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom left
-
-            // Top face
-            //------------------------------------------------------------------
-            // Left line
-            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left front
-            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left back
-
-            // Right line
-            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right front
-            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right back
-
-            // Bottom face
-            //------------------------------------------------------------------
-            // Left line
-            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Top left front
-            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top left back
-
-            // Right line
-            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Top right front
-            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top right back
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw cube wires (vector version)
-void DrawCubeWiresV(Vector3 position, Vector3 size, Color color)
-{
-    DrawCubeWires(position, size.x, size.y, size.z, color);
-}
-
-// Draw sphere
-void DrawSphere(Vector3 centerPos, float radius, Color color)
-{
-    DrawSphereEx(centerPos, radius, 16, 16, color);
-}
-
-// Draw sphere with extended parameters
-void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color)
-{
-#if 0
-    // Basic implementation, do not use it!
-    // For a sphere with 16 rings and 16 slices it requires 8640 cos()/sin() function calls!
-    // New optimized version below only requires 4 cos()/sin() calls
-
-    rlPushMatrix();
-        // NOTE: Transformation is applied in inverse order (scale -> translate)
-        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        rlScalef(radius, radius, radius);
-
-        rlBegin(RL_TRIANGLES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            for (int i = 0; i < (rings + 2); i++)
-            {
-                for (int j = 0; j < slices; j++)
-                {
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
-
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
-                }
-            }
-        rlEnd();
-    rlPopMatrix();
-#endif
-
-    rlPushMatrix();
-        // NOTE: Transformation is applied in inverse order (scale -> translate)
-        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        rlScalef(radius, radius, radius);
-
-        rlBegin(RL_TRIANGLES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            float ringangle = DEG2RAD*(180.0f/(rings + 1)); // Angle between latitudinal parallels
-            float sliceangle = DEG2RAD*(360.0f/slices); // Angle between longitudinal meridians
-
-            float cosring = cosf(ringangle);
-            float sinring = sinf(ringangle);
-            float cosslice = cosf(sliceangle);
-            float sinslice = sinf(sliceangle);
-
-            Vector3 vertices[4] = { 0 }; // Required to store face vertices
-            vertices[2] = (Vector3){ 0, 1, 0 };
-            vertices[3] = (Vector3){ sinring, cosring, 0 };
-
-            for (int i = 0; i < rings + 1; i++)
-            {
-                for (int j = 0; j < slices; j++)
-                {
-                    vertices[0] = vertices[2]; // Rotate around y axis to set up vertices for next face
-                    vertices[1] = vertices[3];
-                    vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis
-                    vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z };
-
-                    rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
-                    rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
-                    rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z);
-
-                    rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
-                    rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z);
-                    rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
-                }
-
-                vertices[2] = vertices[3]; // Rotate around z axis to set up  starting vertices for next ring
-                vertices[3] = (Vector3){ cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z }; // Rotation matrix around z axis
-            }
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw sphere wires
-void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
-{
-    rlPushMatrix();
-        // NOTE: Transformation is applied in inverse order (scale -> translate)
-        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        rlScalef(radius, radius, radius);
-
-        rlBegin(RL_LINES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            for (int i = 0; i < (rings + 2); i++)
-            {
-                for (int j = 0; j < slices; j++)
-                {
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
-
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
-
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
-                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
-                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
-                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
-                }
-            }
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a cylinder
-// NOTE: It could be also used for pyramid and cone
-void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
-{
-    if (sides < 3) sides = 3;
-
-    const float angleStep = 360.0f/sides;
-
-    rlPushMatrix();
-        rlTranslatef(position.x, position.y, position.z);
-
-        rlBegin(RL_TRIANGLES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            if (radiusTop > 0)
-            {
-                // Draw Body -------------------------------------------------------------------------------------
-                for (int i = 0; i < sides; i++)
-                {
-                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left
-                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); //Bottom Right
-                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right
-
-                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); //Top Left
-                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left
-                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right
-                }
-
-                // Draw Cap --------------------------------------------------------------------------------------
-                for (int i = 0; i < sides; i++)
-                {
-                    rlVertex3f(0, height, 0);
-                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
-                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
-                }
-            }
-            else
-            {
-                // Draw Cone -------------------------------------------------------------------------------------
-                for (int i = 0; i < sides; i++)
-                {
-                    rlVertex3f(0, height, 0);
-                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
-                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
-                }
-            }
-
-            // Draw Base -----------------------------------------------------------------------------------------
-            for (int i = 0; i < sides; i++)
-            {
-                rlVertex3f(0, 0, 0);
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
-            }
-
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a cylinder with base at startPos and top at endPos
-// NOTE: It could be also used for pyramid and cone
-void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color)
-{
-    if (sides < 3) sides = 3;
-
-    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
-    if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check
-
-    // Construct a basis of the base and the top face:
-    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
-    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
-
-    float baseAngle = (2.0f*PI)/sides;
-
-    rlBegin(RL_TRIANGLES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        for (int i = 0; i < sides; i++)
-        {
-            // Compute the four vertices
-            float s1 = sinf(baseAngle*(i + 0))*startRadius;
-            float c1 = cosf(baseAngle*(i + 0))*startRadius;
-            Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z };
-            float s2 = sinf(baseAngle*(i + 1))*startRadius;
-            float c2 = cosf(baseAngle*(i + 1))*startRadius;
-            Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z };
-            float s3 = sinf(baseAngle*(i + 0))*endRadius;
-            float c3 = cosf(baseAngle*(i + 0))*endRadius;
-            Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z };
-            float s4 = sinf(baseAngle*(i + 1))*endRadius;
-            float c4 = cosf(baseAngle*(i + 1))*endRadius;
-            Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z };
-
-            if (startRadius > 0)
-            {
-                rlVertex3f(startPos.x, startPos.y, startPos.z); // |
-                rlVertex3f(w2.x, w2.y, w2.z);                   // T0
-                rlVertex3f(w1.x, w1.y, w1.z);                   // |
-            }
-                                                                //          w2 x.-----------x startPos
-            rlVertex3f(w1.x, w1.y, w1.z);                       // |           |\'.  T0    /
-            rlVertex3f(w2.x, w2.y, w2.z);                       // T1          | \ '.     /
-            rlVertex3f(w3.x, w3.y, w3.z);                       // |           |T \  '.  /
-                                                                //             | 2 \ T 'x w1
-            rlVertex3f(w2.x, w2.y, w2.z);                       // |        w4 x.---\-1-|---x endPos
-            rlVertex3f(w4.x, w4.y, w4.z);                       // T2            '.  \  |T3/
-            rlVertex3f(w3.x, w3.y, w3.z);                       // |               '. \ | /
-                                                                //                   '.\|/
-            if (endRadius > 0)                                  //                     'x w3
-            {
-                rlVertex3f(endPos.x, endPos.y, endPos.z);       // |
-                rlVertex3f(w3.x, w3.y, w3.z);                   // T3
-                rlVertex3f(w4.x, w4.y, w4.z);                   // |
-            }                                                   //
-        }
-    rlEnd();
-}
-
-// Draw a wired cylinder
-// NOTE: It could be also used for pyramid and cone
-void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
-{
-    if (sides < 3) sides = 3;
-
-    const float angleStep = 360.0f/sides;
-
-    rlPushMatrix();
-        rlTranslatef(position.x, position.y, position.z);
-
-        rlBegin(RL_LINES);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-
-            for (int i = 0; i < sides; i++)
-            {
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
-                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
-
-                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
-                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
-
-                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
-
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
-                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
-            }
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a wired cylinder with base at startPos and top at endPos
-// NOTE: It could be also used for pyramid and cone
-void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color)
-{
-    if (sides < 3) sides = 3;
-
-    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
-    if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check
-
-    // Construct a basis of the base and the top face:
-    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
-    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
-
-    float baseAngle = (2.0f*PI)/sides;
-
-    rlBegin(RL_LINES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        for (int i = 0; i < sides; i++)
-        {
-            // Compute the four vertices
-            float s1 = sinf(baseAngle*(i + 0))*startRadius;
-            float c1 = cosf(baseAngle*(i + 0))*startRadius;
-            Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z };
-            float s2 = sinf(baseAngle*(i + 1))*startRadius;
-            float c2 = cosf(baseAngle*(i + 1))*startRadius;
-            Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z };
-            float s3 = sinf(baseAngle*(i + 0))*endRadius;
-            float c3 = cosf(baseAngle*(i + 0))*endRadius;
-            Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z };
-            float s4 = sinf(baseAngle*(i + 1))*endRadius;
-            float c4 = cosf(baseAngle*(i + 1))*endRadius;
-            Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z };
-
-            rlVertex3f(w1.x, w1.y, w1.z);
-            rlVertex3f(w2.x, w2.y, w2.z);
-
-            rlVertex3f(w1.x, w1.y, w1.z);
-            rlVertex3f(w3.x, w3.y, w3.z);
-
-            rlVertex3f(w3.x, w3.y, w3.z);
-            rlVertex3f(w4.x, w4.y, w4.z);
-        }
-    rlEnd();
-}
-
-// Draw a capsule with the center of its sphere caps at startPos and endPos
-void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color)
-{
-    if (slices < 3) slices = 3;
-
-    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
-
-    // draw a sphere if start and end points are the same
-    bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0);
-    if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f};
-
-    // Construct a basis of the base and the caps:
-    Vector3 b0 = Vector3Normalize(direction);
-    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
-    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
-    Vector3 capCenter = endPos;
-
-    float baseSliceAngle = (2.0f*PI)/slices;
-    float baseRingAngle  = PI*0.5f/rings;
-
-    rlBegin(RL_TRIANGLES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        // render both caps
-        for (int c = 0; c < 2; c++)
-        {
-            for (int i = 0; i < rings; i++)
-            {
-                for (int j = 0; j < slices; j++)
-                {
-
-                    // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
-
-                    // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
-                    // as we iterate through the rings they must get smaller by the cos(angle(i))
-
-                    // compute the four vertices
-                    float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
-                    float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
-                    Vector3 w1 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius
-                    };
-                    float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
-                    float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
-                    Vector3 w2 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius
-                    };
-
-                    float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
-                    float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
-                    Vector3 w3 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius
-                    };
-                    float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
-                    float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
-                    Vector3 w4 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius
-                    };
-
-                    // Make sure cap triangle normals are facing outwards
-                    if (c == 0)
-                    {
-                        rlVertex3f(w1.x, w1.y, w1.z);
-                        rlVertex3f(w2.x, w2.y, w2.z);
-                        rlVertex3f(w3.x, w3.y, w3.z);
-
-                        rlVertex3f(w2.x, w2.y, w2.z);
-                        rlVertex3f(w4.x, w4.y, w4.z);
-                        rlVertex3f(w3.x, w3.y, w3.z);
-                    }
-                    else
-                    {
-                        rlVertex3f(w1.x, w1.y, w1.z);
-                        rlVertex3f(w3.x, w3.y, w3.z);
-                        rlVertex3f(w2.x, w2.y, w2.z);
-
-                        rlVertex3f(w2.x, w2.y, w2.z);
-                        rlVertex3f(w3.x, w3.y, w3.z);
-                        rlVertex3f(w4.x, w4.y, w4.z);
-                    }
-                }
-            }
-            capCenter = startPos;
-            b0 = Vector3Scale(b0, -1.0f);
-        }
-        // render middle
-        if (!sphereCase)
-        {
-            for (int j = 0; j < slices; j++)
-            {
-                // compute the four vertices
-                float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius;
-                float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius;
-                Vector3 w1 = {
-                    startPos.x + ringSin1*b1.x + ringCos1*b2.x,
-                    startPos.y + ringSin1*b1.y + ringCos1*b2.y,
-                    startPos.z + ringSin1*b1.z + ringCos1*b2.z
-                };
-                float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius;
-                float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius;
-                Vector3 w2 = {
-                    startPos.x + ringSin2*b1.x + ringCos2*b2.x,
-                    startPos.y + ringSin2*b1.y + ringCos2*b2.y,
-                    startPos.z + ringSin2*b1.z + ringCos2*b2.z
-                };
-
-                float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius;
-                float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius;
-                Vector3 w3 = {
-                    endPos.x + ringSin3*b1.x + ringCos3*b2.x,
-                    endPos.y + ringSin3*b1.y + ringCos3*b2.y,
-                    endPos.z + ringSin3*b1.z + ringCos3*b2.z
-                };
-                float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius;
-                float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius;
-                Vector3 w4 = {
-                    endPos.x + ringSin4*b1.x + ringCos4*b2.x,
-                    endPos.y + ringSin4*b1.y + ringCos4*b2.y,
-                    endPos.z + ringSin4*b1.z + ringCos4*b2.z
-                };
-                                                                        //          w2 x.-----------x startPos
-                rlVertex3f(w1.x, w1.y, w1.z);                         // |           |\'.  T0    /
-                rlVertex3f(w2.x, w2.y, w2.z);                         // T1          | \ '.     /
-                rlVertex3f(w3.x, w3.y, w3.z);                         // |           |T \  '.  /
-                                                                        //             | 2 \ T 'x w1
-                rlVertex3f(w2.x, w2.y, w2.z);                         // |        w4 x.---\-1-|---x endPos
-                rlVertex3f(w4.x, w4.y, w4.z);                         // T2            '.  \  |T3/
-                rlVertex3f(w3.x, w3.y, w3.z);                         // |               '. \ | /
-                                                                        //                   '.\|/
-                                                                        //                   'x w3
-            }
-        }
-    rlEnd();
-}
-
-// Draw capsule wires with the center of its sphere caps at startPos and endPos
-void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color)
-{
-    if (slices < 3) slices = 3;
-
-    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
-
-    // draw a sphere if start and end points are the same
-    bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0);
-    if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f};
-
-    // Construct a basis of the base and the caps:
-    Vector3 b0 = Vector3Normalize(direction);
-    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
-    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
-    Vector3 capCenter = endPos;
-
-    float baseSliceAngle = (2.0f*PI)/slices;
-    float baseRingAngle  = PI*0.5f/rings;
-
-    rlBegin(RL_LINES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        // render both caps
-        for (int c = 0; c < 2; c++)
-        {
-            for (int i = 0; i < rings; i++)
-            {
-                for (int j = 0; j < slices; j++)
-                {
-
-                    // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
-
-                    // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
-                    // as we iterate through the rings they must get smaller by the cos(angle(i))
-
-                    // compute the four vertices
-                    float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
-                    float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
-                    Vector3 w1 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius
-                    };
-                    float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
-                    float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
-                    Vector3 w2 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius
-                    };
-
-                    float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
-                    float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
-                    Vector3 w3 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius
-                    };
-                    float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
-                    float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
-                    Vector3 w4 = (Vector3){
-                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius,
-                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius,
-                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius
-                    };
-
-                    rlVertex3f(w1.x, w1.y, w1.z);
-                    rlVertex3f(w2.x, w2.y, w2.z);
-
-                    rlVertex3f(w2.x, w2.y, w2.z);
-                    rlVertex3f(w3.x, w3.y, w3.z);
-
-                    rlVertex3f(w1.x, w1.y, w1.z);
-                    rlVertex3f(w3.x, w3.y, w3.z);
-
-                    rlVertex3f(w2.x, w2.y, w2.z);
-                    rlVertex3f(w4.x, w4.y, w4.z);
-
-                    rlVertex3f(w3.x, w3.y, w3.z);
-                    rlVertex3f(w4.x, w4.y, w4.z);
-                }
-            }
-            capCenter = startPos;
-            b0 = Vector3Scale(b0, -1.0f);
-        }
-        // render middle
-        if (!sphereCase)
-        {
-            for (int j = 0; j < slices; j++)
-            {
-                // compute the four vertices
-                float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius;
-                float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius;
-                Vector3 w1 = {
-                    startPos.x + ringSin1*b1.x + ringCos1*b2.x,
-                    startPos.y + ringSin1*b1.y + ringCos1*b2.y,
-                    startPos.z + ringSin1*b1.z + ringCos1*b2.z
-                };
-                float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius;
-                float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius;
-                Vector3 w2 = {
-                    startPos.x + ringSin2*b1.x + ringCos2*b2.x,
-                    startPos.y + ringSin2*b1.y + ringCos2*b2.y,
-                    startPos.z + ringSin2*b1.z + ringCos2*b2.z
-                };
-
-                float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius;
-                float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius;
-                Vector3 w3 = {
-                    endPos.x + ringSin3*b1.x + ringCos3*b2.x,
-                    endPos.y + ringSin3*b1.y + ringCos3*b2.y,
-                    endPos.z + ringSin3*b1.z + ringCos3*b2.z
-                };
-                float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius;
-                float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius;
-                Vector3 w4 = {
-                    endPos.x + ringSin4*b1.x + ringCos4*b2.x,
-                    endPos.y + ringSin4*b1.y + ringCos4*b2.y,
-                    endPos.z + ringSin4*b1.z + ringCos4*b2.z
-                };
-
-                rlVertex3f(w1.x, w1.y, w1.z);
-                rlVertex3f(w3.x, w3.y, w3.z);
-
-                rlVertex3f(w2.x, w2.y, w2.z);
-                rlVertex3f(w4.x, w4.y, w4.z);
-
-                rlVertex3f(w2.x, w2.y, w2.z);
-                rlVertex3f(w3.x, w3.y, w3.z);
-            }
-        }
-    rlEnd();
-}
-
-// Draw a plane
-void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
-{
-    // NOTE: Plane is always created on XZ ground
-    rlPushMatrix();
-        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
-        rlScalef(size.x, 1.0f, size.y);
-
-        rlBegin(RL_QUADS);
-            rlColor4ub(color.r, color.g, color.b, color.a);
-            rlNormal3f(0.0f, 1.0f, 0.0f);
-
-            rlVertex3f(-0.5f, 0.0f, -0.5f);
-            rlVertex3f(-0.5f, 0.0f, 0.5f);
-            rlVertex3f(0.5f, 0.0f, 0.5f);
-            rlVertex3f(0.5f, 0.0f, -0.5f);
-        rlEnd();
-    rlPopMatrix();
-}
-
-// Draw a ray line
-void DrawRay(Ray ray, Color color)
-{
-    float scale = 10000;
-
-    rlBegin(RL_LINES);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-        rlColor4ub(color.r, color.g, color.b, color.a);
-
-        rlVertex3f(ray.position.x, ray.position.y, ray.position.z);
-        rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale);
-    rlEnd();
-}
-
-// Draw a grid centered at (0, 0, 0)
-void DrawGrid(int slices, float spacing)
-{
-    int halfSlices = slices/2;
-
-    rlBegin(RL_LINES);
-        for (int i = -halfSlices; i <= halfSlices; i++)
-        {
-            if (i == 0)
-            {
-                rlColor3f(0.5f, 0.5f, 0.5f);
-            }
-            else
-            {
-                rlColor3f(0.75f, 0.75f, 0.75f);
-            }
-
-            rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing);
-            rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing);
-
-            rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing);
-            rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing);
-        }
-    rlEnd();
-}
-
-// Load model from files (mesh and material)
-Model LoadModel(const char *fileName)
-{
-    Model model = { 0 };
-
-#if defined(SUPPORT_FILEFORMAT_OBJ)
-    if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
-#endif
-#if defined(SUPPORT_FILEFORMAT_IQM)
-    if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
-#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
-    if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName);
-#endif
-#if defined(SUPPORT_FILEFORMAT_VOX)
-    if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName);
-#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
-    if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName);
-#endif
-
-    // Make sure model transform is set to identity matrix!
-    model.transform = MatrixIdentity();
-
-    if ((model.meshCount != 0) && (model.meshes != NULL))
-    {
-        // Upload vertex data to GPU (static meshes)
-        for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName);
-
-    if (model.materialCount == 0)
-    {
-        TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName);
-
-        model.materialCount = 1;
-        model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
-        model.materials[0] = LoadMaterialDefault();
-
-        if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
-    }
-
-    return model;
-}
-
-// Load model from generated mesh
-// WARNING: A shallow copy of mesh is generated, passed by value,
-// as long as struct contains pointers to data and some values, we get a copy
-// of mesh pointing to same data as original version... be careful!
-Model LoadModelFromMesh(Mesh mesh)
-{
-    Model model = { 0 };
-
-    model.transform = MatrixIdentity();
-
-    model.meshCount = 1;
-    model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
-    model.meshes[0] = mesh;
-
-    model.materialCount = 1;
-    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
-    model.materials[0] = LoadMaterialDefault();
-
-    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
-    model.meshMaterial[0] = 0;  // First material index
-
-    return model;
-}
-
-// Check if a model is valid (loaded in GPU, VAO/VBOs)
-bool IsModelValid(Model model)
-{
-    bool result = false;
-
-    if ((model.meshes != NULL) &&           // Validate model contains some mesh
-        (model.materials != NULL) &&        // Validate model contains some material (at least default one)
-        (model.meshMaterial != NULL) &&     // Validate mesh-material linkage
-        (model.meshCount > 0) &&            // Validate mesh count
-        (model.materialCount > 0)) result = true; // Validate material count
-
-    // NOTE: Many elements could be validated from a model, including every model mesh VAO/VBOs
-    // but some VBOs could not be used, it depends on Mesh vertex data
-    for (int i = 0; i < model.meshCount; i++)
-    {
-        if ((model.meshes[i].vertices != NULL) && (model.meshes[i].vboId[0] == 0)) { result = false; break; }  // Vertex position buffer not uploaded to GPU
-        if ((model.meshes[i].texcoords != NULL) && (model.meshes[i].vboId[1] == 0)) { result = false; break; }  // Vertex textcoords buffer not uploaded to GPU
-        if ((model.meshes[i].normals != NULL) && (model.meshes[i].vboId[2] == 0)) { result = false; break; }  // Vertex normals buffer not uploaded to GPU
-        if ((model.meshes[i].colors != NULL) && (model.meshes[i].vboId[3] == 0)) { result = false; break; }  // Vertex colors buffer not uploaded to GPU
-        if ((model.meshes[i].tangents != NULL) && (model.meshes[i].vboId[4] == 0)) { result = false; break; }  // Vertex tangents buffer not uploaded to GPU
-        if ((model.meshes[i].texcoords2 != NULL) && (model.meshes[i].vboId[5] == 0)) { result = false; break; }  // Vertex texcoords2 buffer not uploaded to GPU
-        if ((model.meshes[i].indices != NULL) && (model.meshes[i].vboId[6] == 0)) { result = false; break; }  // Vertex indices buffer not uploaded to GPU
-        if ((model.meshes[i].boneIds != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; }  // Vertex boneIds buffer not uploaded to GPU
-        if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; }  // Vertex boneWeights buffer not uploaded to GPU
-
-        // NOTE: Some OpenGL versions do not support VAO, so we don't check it
-        //if (model.meshes[i].vaoId == 0) { result = false; break }
-    }
-
-    return result;
-}
-
-// Unload model (meshes/materials) from memory (RAM and/or VRAM)
-// NOTE: This function takes care of all model elements, for a detailed control
-// over them, use UnloadMesh() and UnloadMaterial()
-void UnloadModel(Model model)
-{
-    // Unload meshes
-    for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]);
-
-    // Unload materials maps
-    // NOTE: As the user could be sharing shaders and textures between models,
-    // we don't unload the material but just free its maps,
-    // the user is responsible for freeing models shaders and textures
-    for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
-
-    // Unload arrays
-    RL_FREE(model.meshes);
-    RL_FREE(model.materials);
-    RL_FREE(model.meshMaterial);
-
-    // Unload animation data
-    RL_FREE(model.bones);
-    RL_FREE(model.bindPose);
-
-    TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM");
-}
-
-// Compute model bounding box limits (considers all meshes)
-BoundingBox GetModelBoundingBox(Model model)
-{
-    BoundingBox bounds = { 0 };
-
-    if (model.meshCount > 0)
-    {
-        Vector3 temp = { 0 };
-        bounds = GetMeshBoundingBox(model.meshes[0]);
-
-        for (int i = 1; i < model.meshCount; i++)
-        {
-            BoundingBox tempBounds = GetMeshBoundingBox(model.meshes[i]);
-
-            temp.x = (bounds.min.x < tempBounds.min.x)? bounds.min.x : tempBounds.min.x;
-            temp.y = (bounds.min.y < tempBounds.min.y)? bounds.min.y : tempBounds.min.y;
-            temp.z = (bounds.min.z < tempBounds.min.z)? bounds.min.z : tempBounds.min.z;
-            bounds.min = temp;
-
-            temp.x = (bounds.max.x > tempBounds.max.x)? bounds.max.x : tempBounds.max.x;
-            temp.y = (bounds.max.y > tempBounds.max.y)? bounds.max.y : tempBounds.max.y;
-            temp.z = (bounds.max.z > tempBounds.max.z)? bounds.max.z : tempBounds.max.z;
-            bounds.max = temp;
-        }
-    }
-
-    // Apply model.transform to bounding box
-    // WARNING: Current BoundingBox structure design does not support rotation transformations,
-    // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed)
-    bounds.min = Vector3Transform(bounds.min, model.transform);
-    bounds.max = Vector3Transform(bounds.max, model.transform);
-
-    return bounds;
-}
-
-// Upload vertex data into a VAO (if supported) and VBO
-void UploadMesh(Mesh *mesh, bool dynamic)
-{
-    if (mesh->vaoId > 0)
-    {
-        // Check if mesh has already been loaded in GPU
-        TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId);
-        return;
-    }
-
-    mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int));
-
-    mesh->vaoId = 0;        // Vertex Array Object
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = 0;     // Vertex buffer: positions
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = 0;     // Vertex buffer: texcoords
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = 0;       // Vertex buffer: normals
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = 0;        // Vertex buffer: colors
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0;      // Vertex buffer: tangents
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0;    // Vertex buffer: texcoords2
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0;      // Vertex buffer: indices
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0;      // Vertex buffer: boneIds
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0;  // Vertex buffer: boneWeights
-#endif
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    mesh->vaoId = rlLoadVertexArray();
-    rlEnableVertexArray(mesh->vaoId);
-
-    // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data
-
-    // Enable vertex attributes: position (shader-location = 0)
-    void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices;
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
-    rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, RL_FLOAT, 0, 0, 0);
-    rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
-
-    // Enable vertex attributes: texcoords (shader-location = 1)
-    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic);
-    rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0);
-    rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
-
-    // WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
-    // is part of current state, and it is maintained even if a different program object is used
-
-    if (mesh->normals != NULL)
-    {
-        // Enable vertex attributes: normals (shader-location = 2)
-        void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals;
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
-    }
-    else
-    {
-        // Default vertex attribute: normal
-        // WARNING: Default value provided to shader if location available
-        float value[3] = { 1.0f, 1.0f, 1.0f };
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, value, SHADER_ATTRIB_VEC3, 3);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
-    }
-
-    if (mesh->colors != NULL)
-    {
-        // Enable vertex attribute: color (shader-location = 3)
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, 4, RL_UNSIGNED_BYTE, 1, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
-    }
-    else
-    {
-        // Default vertex attribute: color
-        // WARNING: Default value provided to shader if location available
-        float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };    // WHITE
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, value, SHADER_ATTRIB_VEC4, 4);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
-    }
-
-    if (mesh->tangents != NULL)
-    {
-        // Enable vertex attribute: tangent (shader-location = 4)
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
-    }
-    else
-    {
-        // Default vertex attribute: tangent
-        // WARNING: Default value provided to shader if location available
-        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, value, SHADER_ATTRIB_VEC4, 4);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
-    }
-
-    if (mesh->texcoords2 != NULL)
-    {
-        // Enable vertex attribute: texcoord2 (shader-location = 5)
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, 2, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
-    }
-    else
-    {
-        // Default vertex attribute: texcoord2
-        // WARNING: Default value provided to shader if location available
-        float value[2] = { 0.0f, 0.0f };
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, value, SHADER_ATTRIB_VEC2, 2);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
-    }
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-    if (mesh->boneIds != NULL)
-    {
-        // Enable vertex attribute: boneIds (shader-location = 7)
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
-    }
-    else
-    {
-        // Default vertex attribute: boneIds
-        // WARNING: Default value provided to shader if location available
-        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
-    }
-
-    if (mesh->boneWeights != NULL)
-    {
-        // Enable vertex attribute: boneWeights (shader-location = 8)
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = rlLoadVertexBuffer(mesh->boneWeights, mesh->vertexCount*4*sizeof(float), dynamic);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, 4, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
-    }
-    else
-    {
-        // Default vertex attribute: boneWeights
-        // WARNING: Default value provided to shader if location available
-        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
-        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, value, SHADER_ATTRIB_VEC4, 2);
-        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
-    }
-#endif
-
-    if (mesh->indices != NULL)
-    {
-        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
-    }
-
-    if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId);
-    else TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)");
-
-    rlDisableVertexArray();
-#endif
-}
-
-// Update mesh vertex data in GPU for a specific buffer index
-void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset)
-{
-    rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset);
-}
-
-// Draw a 3d mesh with material and transform
-void DrawMesh(Mesh mesh, Material material, Matrix transform)
-{
-#if defined(GRAPHICS_API_OPENGL_11)
-    #define GL_VERTEX_ARRAY         0x8074
-    #define GL_NORMAL_ARRAY         0x8075
-    #define GL_COLOR_ARRAY          0x8076
-    #define GL_TEXTURE_COORD_ARRAY  0x8078
-
-    rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
-
-    rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
-    rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords);
-    rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
-    rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
-
-    rlPushMatrix();
-        rlMultMatrixf(MatrixToFloat(transform));
-        rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r,
-                   material.maps[MATERIAL_MAP_DIFFUSE].color.g,
-                   material.maps[MATERIAL_MAP_DIFFUSE].color.b,
-                   material.maps[MATERIAL_MAP_DIFFUSE].color.a);
-
-        if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, mesh.indices);
-        else rlDrawVertexArray(0, mesh.vertexCount);
-    rlPopMatrix();
-
-    rlDisableStatePointer(GL_VERTEX_ARRAY);
-    rlDisableStatePointer(GL_TEXTURE_COORD_ARRAY);
-    rlDisableStatePointer(GL_NORMAL_ARRAY);
-    rlDisableStatePointer(GL_COLOR_ARRAY);
-
-    rlDisableTexture();
-#endif
-
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    // Bind shader program
-    rlEnableShader(material.shader.id);
-
-    // Send required data to shader (matrices, values)
-    //-----------------------------------------------------
-    // Upload to shader material.colDiffuse
-    if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1)
-    {
-        float values[4] = {
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f
-        };
-
-        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1);
-    }
-
-    // Upload to shader material.colSpecular (if location available)
-    if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1)
-    {
-        float values[4] = {
-            (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f,
-            (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f,
-            (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f,
-            (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f
-        };
-
-        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1);
-    }
-
-    // Get a copy of current matrices to work with,
-    // just in case stereo render is required, and we need to modify them
-    // NOTE: At this point the modelview matrix just contains the view matrix (camera)
-    // That's because BeginMode3D() sets it and there is no model-drawing function
-    // that modifies it, all use rlPushMatrix() and rlPopMatrix()
-    Matrix matModel = MatrixIdentity();
-    Matrix matView = rlGetMatrixModelview();
-    Matrix matModelView = MatrixIdentity();
-    Matrix matProjection = rlGetMatrixProjection();
-
-    // Upload view and projection matrices (if locations available)
-    if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
-    if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
-
-    // Accumulate several model transformations:
-    //    transform: model transformation provided (includes DrawModel() params combined with model.transform)
-    //    rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack
-    matModel = MatrixMultiply(transform, rlGetMatrixTransform());
-
-    // Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL
-    if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], matModel);
-
-    // Get model-view matrix
-    matModelView = MatrixMultiply(matModel, matView);
-
-    // Upload model normal matrix (if locations available)
-    if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-    // Upload Bone Transforms
-    if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
-    {
-        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
-    }
-#endif
-    //-----------------------------------------------------
-
-    // Bind active texture maps (if available)
-    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
-    {
-        if (material.maps[i].texture.id > 0)
-        {
-            // Select current shader texture slot
-            rlActiveTextureSlot(i);
-
-            // Enable texture for active slot
-            if ((i == MATERIAL_MAP_IRRADIANCE) ||
-                (i == MATERIAL_MAP_PREFILTER) ||
-                (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id);
-            else rlEnableTexture(material.maps[i].texture.id);
-
-            rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1);
-        }
-    }
-
-    // Try binding vertex array objects (VAO) or use VBOs if not possible
-    // WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values
-    // for shader expected vertex attributes that are not provided by the mesh (i.e. colors)
-    // This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes
-    if (!rlEnableVertexArray(mesh.vaoId))
-    {
-        // Bind mesh VBO data: vertex position (shader-location = 0)
-        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
-        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
-
-        // Bind mesh VBO data: vertex texcoords (shader-location = 1)
-        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
-        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
-
-        if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
-        {
-            // Bind mesh VBO data: vertex normals (shader-location = 2)
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
-        }
-
-        // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
-        {
-            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
-            {
-                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
-                rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
-                rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
-            }
-            else
-            {
-                // Set default value for defined vertex attribute in shader but not provided by mesh
-                // WARNING: It could result in GPU undefined behaviour
-                float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-                rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
-                rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
-            }
-        }
-
-        // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
-        }
-
-        // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
-        }
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
-        }
-
-        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
-        }
-#endif
-
-        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
-    }
-
-    int eyeCount = 1;
-    if (rlIsStereoRenderEnabled()) eyeCount = 2;
-
-    for (int eye = 0; eye < eyeCount; eye++)
-    {
-        // Calculate model-view-projection matrix (MVP)
-        Matrix matModelViewProjection = MatrixIdentity();
-        if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection);
-        else
-        {
-            // Setup current eye viewport (half screen width)
-            rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight());
-            matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye));
-        }
-
-        // Send combined model-view-projection matrix to shader
-        rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection);
-
-        // Draw mesh
-        if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0);
-        else rlDrawVertexArray(0, mesh.vertexCount);
-    }
-
-    // Unbind all bound texture maps
-    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
-    {
-        if (material.maps[i].texture.id > 0)
-        {
-            // Select current shader texture slot
-            rlActiveTextureSlot(i);
-
-            // Disable texture for active slot
-            if ((i == MATERIAL_MAP_IRRADIANCE) ||
-                (i == MATERIAL_MAP_PREFILTER) ||
-                (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
-            else rlDisableTexture();
-        }
-    }
-
-    // Disable all possible vertex array objects (or VBOs)
-    rlDisableVertexArray();
-    rlDisableVertexBuffer();
-    rlDisableVertexBufferElement();
-
-    // Disable shader program
-    rlDisableShader();
-
-    // Restore rlgl internal modelview and projection matrices
-    rlSetMatrixModelview(matView);
-    rlSetMatrixProjection(matProjection);
-#endif
-}
-
-// Draw multiple mesh instances with material and different transforms
-void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances)
-{
-#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
-    // Instancing required variables
-    float16 *instanceTransforms = NULL;
-    unsigned int instancesVboId = 0;
-
-    // Bind shader program
-    rlEnableShader(material.shader.id);
-
-    // Send required data to shader (matrices, values)
-    //-----------------------------------------------------
-    // Upload to shader material.colDiffuse
-    if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1)
-    {
-        float values[4] = {
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f,
-            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f
-        };
-
-        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1);
-    }
-
-    // Upload to shader material.colSpecular (if location available)
-    if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1)
-    {
-        float values[4] = {
-            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r/255.0f,
-            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g/255.0f,
-            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b/255.0f,
-            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a/255.0f
-        };
-
-        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1);
-    }
-
-    // Get a copy of current matrices to work with,
-    // just in case stereo render is required, and we need to modify them
-    // NOTE: At this point the modelview matrix just contains the view matrix (camera)
-    // That's because BeginMode3D() sets it and there is no model-drawing function
-    // that modifies it, all use rlPushMatrix() and rlPopMatrix()
-    Matrix matModel = MatrixIdentity();
-    Matrix matView = rlGetMatrixModelview();
-    Matrix matModelView = MatrixIdentity();
-    Matrix matProjection = rlGetMatrixProjection();
-
-    // Upload view and projection matrices (if locations available)
-    if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
-    if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
-
-    // Create instances buffer
-    instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16));
-
-    // Fill buffer with instances transformations as float16 arrays
-    for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]);
-
-    // Enable mesh VAO to attach new buffer
-    rlEnableVertexArray(mesh.vaoId);
-
-    // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData()
-    // It isn't clear which would be reliably faster in all cases and on all platforms,
-    // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems
-    // no faster, since we're transferring all the transform matrices anyway
-    instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false);
-
-    // Instances transformation matrices are send to shader attribute location: SHADER_LOC_MATRIX_MODEL
-    for (unsigned int i = 0; i < 4; i++)
-    {
-        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i);
-        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4));
-        rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_MATRIX_MODEL] + i, 1);
-    }
-
-    rlDisableVertexBuffer();
-    rlDisableVertexArray();
-
-    // Accumulate internal matrix transform (push/pop) and view matrix
-    // NOTE: In this case, model instance transformation must be computed in the shader
-    matModelView = MatrixMultiply(rlGetMatrixTransform(), matView);
-
-    // Upload model normal matrix (if locations available)
-    if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-    // Upload Bone Transforms
-    if (material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1 && mesh.boneMatrices)
-    {
-        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
-    }
-#endif
-
-    //-----------------------------------------------------
-
-    // Bind active texture maps (if available)
-    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
-    {
-        if (material.maps[i].texture.id > 0)
-        {
-            // Select current shader texture slot
-            rlActiveTextureSlot(i);
-
-            // Enable texture for active slot
-            if ((i == MATERIAL_MAP_IRRADIANCE) ||
-                (i == MATERIAL_MAP_PREFILTER) ||
-                (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id);
-            else rlEnableTexture(material.maps[i].texture.id);
-
-            rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1);
-        }
-    }
-
-    // Try binding vertex array objects (VAO)
-    // or use VBOs if not possible
-    if (!rlEnableVertexArray(mesh.vaoId))
-    {
-        // Bind mesh VBO data: vertex position (shader-location = 0)
-        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
-        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
-
-        // Bind mesh VBO data: vertex texcoords (shader-location = 1)
-        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
-        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
-
-        if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
-        {
-            // Bind mesh VBO data: vertex normals (shader-location = 2)
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
-        }
-
-        // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
-        {
-            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
-            {
-                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
-                rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
-                rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
-            }
-            else
-            {
-                // Set default value for unused attribute
-                // NOTE: Required when using default shader and no VAO support
-                float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-                rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
-                rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
-            }
-        }
-
-        // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
-        }
-
-        // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
-        }
-
-#ifdef RL_SUPPORT_MESH_GPU_SKINNING
-        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
-        }
-
-        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
-        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
-        {
-            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
-            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
-            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
-        }
-#endif
-
-        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
-    }
-
-    int eyeCount = 1;
-    if (rlIsStereoRenderEnabled()) eyeCount = 2;
-
-    for (int eye = 0; eye < eyeCount; eye++)
-    {
-        // Calculate model-view-projection matrix (MVP)
-        Matrix matModelViewProjection = MatrixIdentity();
-        if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection);
-        else
-        {
-            // Setup current eye viewport (half screen width)
-            rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight());
-            matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye));
-        }
-
-        // Send combined model-view-projection matrix to shader
-        rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection);
-
-        // Draw mesh instanced
-        if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount*3, 0, instances);
-        else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances);
-    }
-
-    // Unbind all bound texture maps
-    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
-    {
-        if (material.maps[i].texture.id > 0)
-        {
-            // Select current shader texture slot
-            rlActiveTextureSlot(i);
-
-            // Disable texture for active slot
-            if ((i == MATERIAL_MAP_IRRADIANCE) ||
-                (i == MATERIAL_MAP_PREFILTER) ||
-                (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
-            else rlDisableTexture();
-        }
-    }
-
-    // Disable all possible vertex array objects (or VBOs)
-    rlDisableVertexArray();
-    rlDisableVertexBuffer();
-    rlDisableVertexBufferElement();
-
-    // Disable shader program
-    rlDisableShader();
-
-    // Remove instance transforms buffer
-    rlUnloadVertexBuffer(instancesVboId);
-    RL_FREE(instanceTransforms);
-#endif
-}
-
-// Unload mesh from memory (RAM and VRAM)
-void UnloadMesh(Mesh mesh)
-{
-    // Unload rlgl mesh vboId data
-    rlUnloadVertexArray(mesh.vaoId);
-
-    if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]);
-    RL_FREE(mesh.vboId);
-
-    RL_FREE(mesh.vertices);
-    RL_FREE(mesh.texcoords);
-    RL_FREE(mesh.normals);
-    RL_FREE(mesh.colors);
-    RL_FREE(mesh.tangents);
-    RL_FREE(mesh.texcoords2);
-    RL_FREE(mesh.indices);
-
-    RL_FREE(mesh.animVertices);
-    RL_FREE(mesh.animNormals);
-    RL_FREE(mesh.boneWeights);
-    RL_FREE(mesh.boneIds);
-    RL_FREE(mesh.boneMatrices);
-}
-
-// Export mesh data to file
-bool ExportMesh(Mesh mesh, const char *fileName)
-{
-    bool success = false;
-
-    if (IsFileExtension(fileName, ".obj"))
-    {
-        // Estimated data size, it should be enough...
-        int dataSize = mesh.vertexCount*(int)strlen("v 0000.00f 0000.00f 0000.00f") +
-                       mesh.vertexCount*(int)strlen("vt 0.000f 0.00f") +
-                       mesh.vertexCount*(int)strlen("vn 0.000f 0.00f 0.00f") +
-                       mesh.triangleCount*(int)strlen("f 00000/00000/00000 00000/00000/00000 00000/00000/00000");
-
-        // NOTE: Text data buffer size is estimated considering mesh data size
-        char *txtData = (char *)RL_CALLOC(dataSize*2 + 2000, sizeof(char));
-
-        int byteCount = 0;
-        byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n");
-        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
-        byteCount += sprintf(txtData + byteCount, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized   //\n");
-        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
-        byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report:  github.com/raysan5/raylib                        //\n");
-        byteCount += sprintf(txtData + byteCount, "# // feedback and support:       ray[at]raylib.com                                //\n");
-        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
-        byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)                          //\n");
-        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
-        byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
-        byteCount += sprintf(txtData + byteCount, "# Vertex Count:     %i\n", mesh.vertexCount);
-        byteCount += sprintf(txtData + byteCount, "# Triangle Count:   %i\n\n", mesh.triangleCount);
-
-        byteCount += sprintf(txtData + byteCount, "g mesh\n");
-
-        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
-        {
-            byteCount += sprintf(txtData + byteCount, "v %.2f %.2f %.2f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
-        }
-
-        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
-        {
-            byteCount += sprintf(txtData + byteCount, "vt %.3f %.3f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
-        }
-
-        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
-        {
-            byteCount += sprintf(txtData + byteCount, "vn %.3f %.3f %.3f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
-        }
-
-        if (mesh.indices != NULL)
-        {
-            for (int i = 0, v = 0; i < mesh.triangleCount; i++, v += 3)
-            {
-                byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n",
-                    mesh.indices[v] + 1, mesh.indices[v] + 1, mesh.indices[v] + 1,
-                    mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1,
-                    mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1);
-            }
-        }
-        else
-        {
-            for (int i = 0, v = 1; i < mesh.triangleCount; i++, v += 3)
-            {
-                byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", v, v, v, v + 1, v + 1, v + 1, v + 2, v + 2, v + 2);
-            }
-        }
-
-        byteCount += sprintf(txtData + byteCount, "\n");
-
-        // NOTE: Text data length exported is determined by '\0' (NULL) character
-        success = SaveFileText(fileName, txtData);
-
-        RL_FREE(txtData);
-    }
-    else if (IsFileExtension(fileName, ".raw"))
-    {
-        // TODO: Support additional file formats to export mesh vertex data
-    }
-
-    return success;
-}
-
-// Export mesh as code file (.h) defining multiple arrays of vertex attributes
-bool ExportMeshAsCode(Mesh mesh, const char *fileName)
-{
-    bool success = false;
-
-#ifndef TEXT_BYTES_PER_LINE
-    #define TEXT_BYTES_PER_LINE     20
-#endif
-
-    // NOTE: Text data buffer size is fixed to 64MB
-    char *txtData = (char *)RL_CALLOC(64*1024*1024, sizeof(char));  // 64 MB
-
-    int byteCount = 0;
-    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// MeshAsCode exporter v1.0 - Mesh vertex data exported as arrays                     //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
-    byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2023 Ramon Santamaria (@raysan5)                                     //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
-
-    // Get file name from path and convert variable name to uppercase
-    char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
-    for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
-
-    // Add image information
-    byteCount += sprintf(txtData + byteCount, "// Mesh basic information\n");
-    byteCount += sprintf(txtData + byteCount, "#define %s_VERTEX_COUNT    %i\n", varFileName, mesh.vertexCount);
-    byteCount += sprintf(txtData + byteCount, "#define %s_TRIANGLE_COUNT   %i\n\n", varFileName, mesh.triangleCount);
-
-    // Define vertex attributes data as separate arrays
-    //-----------------------------------------------------------------------------------------
-    if (mesh.vertices != NULL)      // Vertex position (XYZ - 3 components per vertex - float)
-    {
-        byteCount += sprintf(txtData + byteCount, "static float %s_VERTEX_DATA[%i] = { ", varFileName, mesh.vertexCount*3);
-        for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.vertices[i]);
-        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.vertices[mesh.vertexCount*3 - 1]);
-    }
-
-    if (mesh.texcoords != NULL)      // Vertex texture coordinates (UV - 2 components per vertex - float)
-    {
-        byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD_DATA[%i] = { ", varFileName, mesh.vertexCount*2);
-        for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords[i]);
-        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords[mesh.vertexCount*2 - 1]);
-    }
-
-    if (mesh.texcoords2 != NULL)      // Vertex texture coordinates (UV - 2 components per vertex - float)
-    {
-        byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD2_DATA[%i] = { ", varFileName, mesh.vertexCount*2);
-        for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords2[i]);
-        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords2[mesh.vertexCount*2 - 1]);
-    }
-
-    if (mesh.normals != NULL)      // Vertex normals (XYZ - 3 components per vertex - float)
-    {
-        byteCount += sprintf(txtData + byteCount, "static float %s_NORMAL_DATA[%i] = { ", varFileName, mesh.vertexCount*3);
-        for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.normals[i]);
-        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.normals[mesh.vertexCount*3 - 1]);
-    }
-
-    if (mesh.tangents != NULL)      // Vertex tangents (XYZW - 4 components per vertex - float)
-    {
-        byteCount += sprintf(txtData + byteCount, "static float %s_TANGENT_DATA[%i] = { ", varFileName, mesh.vertexCount*4);
-        for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.tangents[i]);
-        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.tangents[mesh.vertexCount*4 - 1]);
-    }
-
-    if (mesh.colors != NULL)        // Vertex colors (RGBA - 4 components per vertex - unsigned char)
-    {
-        byteCount += sprintf(txtData + byteCount, "static unsigned char %s_COLOR_DATA[%i] = { ", varFileName, mesh.vertexCount*4);
-        for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), mesh.colors[i]);
-        byteCount += sprintf(txtData + byteCount, "0x%x };\n\n", mesh.colors[mesh.vertexCount*4 - 1]);
-    }
-
-    if (mesh.indices != NULL)       // Vertex indices (3 index per triangle - unsigned short)
-    {
-        byteCount += sprintf(txtData + byteCount, "static unsigned short %s_INDEX_DATA[%i] = { ", varFileName, mesh.triangleCount*3);
-        for (int i = 0; i < mesh.triangleCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%i,\n" : "%i, "), mesh.indices[i]);
-        byteCount += sprintf(txtData + byteCount, "%i };\n", mesh.indices[mesh.triangleCount*3 - 1]);
-    }
-    //-----------------------------------------------------------------------------------------
-
-    // NOTE: Text data size exported is determined by '\0' (NULL) character
-    success = SaveFileText(fileName, txtData);
-
-    RL_FREE(txtData);
-
-    //if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
-    //else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
-
-    return success;
-}
-
-#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
-// Process obj materials
-static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount)
-{
-    // Init model mats
-    for (int m = 0; m < materialCount; m++)
-    {
-        // Init material to default
-        // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
-        materials[m] = LoadMaterialDefault();
-
-        if (mats == NULL) continue;
-
-        // Get default texture, in case no texture is defined
-        // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
-        materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
-
-        if (mats[m].diffuse_texname != NULL) materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(mats[m].diffuse_texname);  //char *diffuse_texname; // map_Kd
-        else materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(mats[m].diffuse[0]*255.0f), (unsigned char)(mats[m].diffuse[1]*255.0f), (unsigned char)(mats[m].diffuse[2]*255.0f), 255 }; //float diffuse[3];
-        materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
-
-        if (mats[m].specular_texname != NULL) materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(mats[m].specular_texname);  //char *specular_texname; // map_Ks
-        materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(mats[m].specular[0]*255.0f), (unsigned char)(mats[m].specular[1]*255.0f), (unsigned char)(mats[m].specular[2]*255.0f), 255 }; //float specular[3];
-        materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
-
-        if (mats[m].bump_texname != NULL) materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(mats[m].bump_texname);  //char *bump_texname; // map_bump, bump
-        materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
-        materials[m].maps[MATERIAL_MAP_NORMAL].value = mats[m].shininess;
-
-        materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(mats[m].emission[0]*255.0f), (unsigned char)(mats[m].emission[1]*255.0f), (unsigned char)(mats[m].emission[2]*255.0f), 255 }; //float emission[3];
-
-        if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname);  //char *displacement_texname; // disp
-    }
-}
-#endif
-
-// Load materials from model file
-Material *LoadMaterials(const char *fileName, int *materialCount)
-{
-    Material *materials = NULL;
-    unsigned int count = 0;
-
-    // TODO: Support IQM and GLTF for materials parsing
-
-#if defined(SUPPORT_FILEFORMAT_MTL)
-    if (IsFileExtension(fileName, ".mtl"))
-    {
-        tinyobj_material_t *mats = NULL;
-
-        int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
-        if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
-
-        materials = RL_MALLOC(count*sizeof(Material));
-        ProcessMaterialsOBJ(materials, mats, count);
-
-        tinyobj_materials_free(mats, count);
-    }
-#else
-    TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName);
-#endif
-
-    *materialCount = count;
-    return materials;
-}
-
-// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
-Material LoadMaterialDefault(void)
-{
-    Material material = { 0 };
-    material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap));
-
-    // Using rlgl default shader
-    material.shader.id = rlGetShaderIdDefault();
-    material.shader.locs = rlGetShaderLocsDefault();
-
-    // Using rlgl default texture (1x1 pixel, UNCOMPRESSED_R8G8B8A8, 1 mipmap)
-    material.maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
-    //material.maps[MATERIAL_MAP_NORMAL].texture;         // NOTE: By default, not set
-    //material.maps[MATERIAL_MAP_SPECULAR].texture;       // NOTE: By default, not set
-
-    material.maps[MATERIAL_MAP_DIFFUSE].color = WHITE;    // Diffuse color
-    material.maps[MATERIAL_MAP_SPECULAR].color = WHITE;   // Specular color
-
-    return material;
-}
-
-// Check if a material is valid (map textures loaded in GPU)
-bool IsMaterialValid(Material material)
-{
-    bool result = false;
-
-    if ((material.maps != NULL) &&      // Validate material contain some map
-        (material.shader.id > 0)) result = true; // Validate material shader is valid
-
-    // TODO: Check if available maps contain loaded textures
-
-    return result;
-}
-
-// Unload material from memory
-void UnloadMaterial(Material material)
-{
-    // Unload material shader (avoid unloading default shader, managed by raylib)
-    if (material.shader.id != rlGetShaderIdDefault()) UnloadShader(material.shader);
-
-    // Unload loaded texture maps (avoid unloading default texture, managed by raylib)
-    if (material.maps != NULL)
-    {
-        for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
-        {
-            if (material.maps[i].texture.id != rlGetTextureIdDefault()) rlUnloadTexture(material.maps[i].texture.id);
-        }
-    }
-
-    RL_FREE(material.maps);
-}
-
-// Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
-// NOTE: Previous texture should be manually unloaded
-void SetMaterialTexture(Material *material, int mapType, Texture2D texture)
-{
-    material->maps[mapType].texture = texture;
-}
-
-// Set the material for a mesh
-void SetModelMeshMaterial(Model *model, int meshId, int materialId)
-{
-    if (meshId >= model->meshCount) TRACELOG(LOG_WARNING, "MESH: Id greater than mesh count");
-    else if (materialId >= model->materialCount) TRACELOG(LOG_WARNING, "MATERIAL: Id greater than material count");
-    else  model->meshMaterial[meshId] = materialId;
-}
-
-// Load model animations from file
-ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount)
-{
-    ModelAnimation *animations = NULL;
-
-#if defined(SUPPORT_FILEFORMAT_IQM)
-    if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount);
-#endif
-#if defined(SUPPORT_FILEFORMAT_M3D)
-    if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount);
-#endif
-#if defined(SUPPORT_FILEFORMAT_GLTF)
-    if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationsGLTF(fileName, animCount);
-#endif
-
-    return animations;
-}
-
-// Update model animated vertex data (positions and normals) for a given frame
-// NOTE: Updated data is uploaded to GPU
-void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
-{
-    if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
-    {
-        if (frame >= anim.frameCount) frame = frame%anim.frameCount;
-
-        for (int m = 0; m < model.meshCount; m++)
-        {
-            Mesh mesh = model.meshes[m];
-
-            if (mesh.boneIds == NULL || mesh.boneWeights == NULL)
-            {
-                TRACELOG(LOG_WARNING, "MODEL: UpdateModelAnimation(): Mesh %i has no connection to bones", m);
-                continue;
-            }
-
-            bool updated = false;           // Flag to check when anim vertex information is updated
-            Vector3 animVertex = { 0 };
-            Vector3 animNormal = { 0 };
-
-            Vector3 inTranslation = { 0 };
-            Quaternion inRotation = { 0 };
-            // Vector3 inScale = { 0 };
-
-            Vector3 outTranslation = { 0 };
-            Quaternion outRotation = { 0 };
-            Vector3 outScale = { 0 };
-
-            int boneId = 0;
-            int boneCounter = 0;
-            float boneWeight = 0.0;
-
-            const int vValues = mesh.vertexCount*3;
-            for (int vCounter = 0; vCounter < vValues; vCounter += 3)
-            {
-                mesh.animVertices[vCounter] = 0;
-                mesh.animVertices[vCounter + 1] = 0;
-                mesh.animVertices[vCounter + 2] = 0;
-
-                if (mesh.animNormals != NULL)
-                {
-                    mesh.animNormals[vCounter] = 0;
-                    mesh.animNormals[vCounter + 1] = 0;
-                    mesh.animNormals[vCounter + 2] = 0;
-                }
-
-                // Iterates over 4 bones per vertex
-                for (int j = 0; j < 4; j++, boneCounter++)
-                {
-                    boneWeight = mesh.boneWeights[boneCounter];
-
-                    // Early stop when no transformation will be applied
-                    if (boneWeight == 0.0f) continue;
-
-                    boneId = mesh.boneIds[boneCounter];
-                    //int boneIdParent = model.bones[boneId].parent;
-                    inTranslation = model.bindPose[boneId].translation;
-                    inRotation = model.bindPose[boneId].rotation;
-                    //inScale = model.bindPose[boneId].scale;
-                    outTranslation = anim.framePoses[frame][boneId].translation;
-                    outRotation = anim.framePoses[frame][boneId].rotation;
-                    outScale = anim.framePoses[frame][boneId].scale;
-
-                    // Vertices processing
-                    // NOTE: We use meshes.vertices (default vertex position) to calculate meshes.animVertices (animated vertex position)
-                    animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
-                    animVertex = Vector3Subtract(animVertex, inTranslation);
-                    animVertex = Vector3Multiply(animVertex, outScale);
-                    animVertex = Vector3RotateByQuaternion(animVertex, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
-                    animVertex = Vector3Add(animVertex, outTranslation);
-                    //animVertex = Vector3Transform(animVertex, model.transform);
-                    mesh.animVertices[vCounter] += animVertex.x*boneWeight;
-                    mesh.animVertices[vCounter + 1] += animVertex.y*boneWeight;
-                    mesh.animVertices[vCounter + 2] += animVertex.z*boneWeight;
-                    updated = true;
-
-                    // Normals processing
-                    // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
-                    if (mesh.normals != NULL)
-                    {
-                        animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
-                        animNormal = Vector3RotateByQuaternion(animNormal, QuaternionMultiply(outRotation, QuaternionInvert(inRotation)));
-                        mesh.animNormals[vCounter] += animNormal.x*boneWeight;
-                        mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
-                        mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
-                    }
-                }
-            }
-
-            // Upload new vertex data to GPU for model drawing
-            // NOTE: Only update data when values changed
-            if (updated)
-            {
-                rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position
-                rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0);  // Update vertex normals
-            }
-        }
-    }
-}
-
-void UpdateModelAnimationBoneMatrices(Model model, ModelAnimation anim, int frame)
-{
-    if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
-    {
-        if (frame >= anim.frameCount) frame = frame%anim.frameCount;
-
-        for (int i = 0; i < model.meshCount; i++)
-        {
-            if (model.meshes[i].boneMatrices)
-            {
-                assert(model.meshes[i].boneCount == anim.boneCount);
-
-                for (int boneId = 0; boneId < model.meshes[i].boneCount; boneId++)
-                {
-                    Vector3 inTranslation = model.bindPose[boneId].translation;
-                    Quaternion inRotation = model.bindPose[boneId].rotation;
-                    Vector3 inScale = model.bindPose[boneId].scale;
-
-                    Vector3 outTranslation = anim.framePoses[frame][boneId].translation;
-                    Quaternion outRotation = anim.framePoses[frame][boneId].rotation;
-                    Vector3 outScale = anim.framePoses[frame][boneId].scale;
-
-                    Vector3 invTranslation = Vector3RotateByQuaternion(Vector3Negate(inTranslation), QuaternionInvert(inRotation));
-                    Quaternion invRotation = QuaternionInvert(inRotation);
-                    Vector3 invScale = Vector3Divide((Vector3){ 1.0f, 1.0f, 1.0f }, inScale);
-
-                    Vector3 boneTranslation = Vector3Add(
-                        Vector3RotateByQuaternion(Vector3Multiply(outScale, invTranslation),
-                        outRotation), outTranslation);
-                    Quaternion boneRotation = QuaternionMultiply(outRotation, invRotation);
-                    Vector3 boneScale = Vector3Multiply(outScale, invScale);
-
-                    Matrix boneMatrix = MatrixMultiply(MatrixMultiply(
-                        QuaternionToMatrix(boneRotation),
-                        MatrixTranslate(boneTranslation.x, boneTranslation.y, boneTranslation.z)),
-                        MatrixScale(boneScale.x, boneScale.y, boneScale.z));
-
-                    model.meshes[i].boneMatrices[boneId] = boneMatrix;
-                }
-            }
-        }
-    }
-}
-
-// Unload animation array data
-void UnloadModelAnimations(ModelAnimation *animations, int animCount)
-{
-    for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]);
-    RL_FREE(animations);
-}
-
-// Unload animation data
-void UnloadModelAnimation(ModelAnimation anim)
-{
-    for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
-
-    RL_FREE(anim.bones);
-    RL_FREE(anim.framePoses);
-}
-
-// Check model animation skeleton match
-// NOTE: Only number of bones and parent connections are checked
-bool IsModelAnimationValid(Model model, ModelAnimation anim)
-{
-    int result = true;
-
-    if (model.boneCount != anim.boneCount) result = false;
-    else
-    {
-        for (int i = 0; i < model.boneCount; i++)
-        {
-            if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; }
-        }
-    }
-
-    return result;
-}
-
-#if defined(SUPPORT_MESH_GENERATION)
-// Generate polygonal mesh
-Mesh GenMeshPoly(int sides, float radius)
-{
-    Mesh mesh = { 0 };
-
-    if (sides < 3) return mesh; // Security check
-
-    int vertexCount = sides*3;
-
-    // Vertices definition
-    Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
-
-    float d = 0.0f, dStep = 360.0f/sides;
-    for (int v = 0; v < vertexCount - 2; v += 3)
-    {
-        vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
-        vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius };
-        vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius };
-        d += dStep;
-    }
-
-    // Normals definition
-    Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
-    for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
-
-    // TexCoords definition
-    Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
-    for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f };
-
-    mesh.vertexCount = vertexCount;
-    mesh.triangleCount = sides;
-    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-
-    // Mesh vertices position array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.vertices[3*i] = vertices[i].x;
-        mesh.vertices[3*i + 1] = vertices[i].y;
-        mesh.vertices[3*i + 2] = vertices[i].z;
-    }
-
-    // Mesh texcoords array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.texcoords[2*i] = texcoords[i].x;
-        mesh.texcoords[2*i + 1] = texcoords[i].y;
-    }
-
-    // Mesh normals array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.normals[3*i] = normals[i].x;
-        mesh.normals[3*i + 1] = normals[i].y;
-        mesh.normals[3*i + 2] = normals[i].z;
-    }
-
-    RL_FREE(vertices);
-    RL_FREE(normals);
-    RL_FREE(texcoords);
-
-    // Upload vertex data to GPU (static mesh)
-    // NOTE: mesh.vboId array is allocated inside UploadMesh()
-    UploadMesh(&mesh, false);
-
-    return mesh;
-}
-
-// Generate plane mesh (with subdivisions)
-Mesh GenMeshPlane(float width, float length, int resX, int resZ)
-{
-    Mesh mesh = { 0 };
-
-#define CUSTOM_MESH_GEN_PLANE
-#if defined(CUSTOM_MESH_GEN_PLANE)
-    resX++;
-    resZ++;
-
-    // Vertices definition
-    int vertexCount = resX*resZ; // vertices get reused for the faces
-
-    Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
-    for (int z = 0; z < resZ; z++)
-    {
-        // [-length/2, length/2]
-        float zPos = ((float)z/(resZ - 1) - 0.5f)*length;
-        for (int x = 0; x < resX; x++)
-        {
-            // [-width/2, width/2]
-            float xPos = ((float)x/(resX - 1) - 0.5f)*width;
-            vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos };
-        }
-    }
-
-    // Normals definition
-    Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
-    for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
-
-    // TexCoords definition
-    Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
-    for (int v = 0; v < resZ; v++)
-    {
-        for (int u = 0; u < resX; u++)
-        {
-            texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) };
-        }
-    }
-
-    // Triangles definition (indices)
-    int numFaces = (resX - 1)*(resZ - 1);
-    int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int));
-    int t = 0;
-    for (int face = 0; face < numFaces; face++)
-    {
-        // Retrieve lower left corner from face ind
-        int i = face + face/(resX - 1);
-
-        triangles[t++] = i + resX;
-        triangles[t++] = i + 1;
-        triangles[t++] = i;
-
-        triangles[t++] = i + resX;
-        triangles[t++] = i + resX + 1;
-        triangles[t++] = i + 1;
-    }
-
-    mesh.vertexCount = vertexCount;
-    mesh.triangleCount = numFaces*2;
-    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short));
-
-    // Mesh vertices position array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.vertices[3*i] = vertices[i].x;
-        mesh.vertices[3*i + 1] = vertices[i].y;
-        mesh.vertices[3*i + 2] = vertices[i].z;
-    }
-
-    // Mesh texcoords array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.texcoords[2*i] = texcoords[i].x;
-        mesh.texcoords[2*i + 1] = texcoords[i].y;
-    }
-
-    // Mesh normals array
-    for (int i = 0; i < mesh.vertexCount; i++)
-    {
-        mesh.normals[3*i] = normals[i].x;
-        mesh.normals[3*i + 1] = normals[i].y;
-        mesh.normals[3*i + 2] = normals[i].z;
-    }
-
-    // Mesh indices array initialization
-    for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
-
-    RL_FREE(vertices);
-    RL_FREE(normals);
-    RL_FREE(texcoords);
-    RL_FREE(triangles);
-
-#else       // Use par_shapes library to generate plane mesh
-
-    par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ);   // No normals/texcoords generated!!!
-    par_shapes_scale(plane, width, length, 1.0f);
-    par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
-    par_shapes_translate(plane, -width/2, 0.0f, length/2);
-
-    mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
-
-    mesh.vertexCount = plane->ntriangles*3;
-    mesh.triangleCount = plane->ntriangles;
-
-    for (int k = 0; k < mesh.vertexCount; k++)
-    {
-        mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
-        mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
-        mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
-
-        mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
-        mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
-        mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
-
-        mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
-        mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
-    }
-
-    par_shapes_free_mesh(plane);
-#endif
-
-    // Upload vertex data to GPU (static mesh)
-    UploadMesh(&mesh, false);
-
-    return mesh;
-}
-
-// Generated cuboid mesh
-Mesh GenMeshCube(float width, float height, float length)
-{
-    Mesh mesh = { 0 };
-
-#define CUSTOM_MESH_GEN_CUBE
-#if defined(CUSTOM_MESH_GEN_CUBE)
-    float vertices[] = {
-        -width/2, -height/2, length/2,
-        width/2, -height/2, length/2,
-        width/2, height/2, length/2,
-        -width/2, height/2, length/2,
-        -width/2, -height/2, -length/2,
-        -width/2, height/2, -length/2,
-        width/2, height/2, -length/2,
-        width/2, -height/2, -length/2,
-        -width/2, height/2, -length/2,
-        -width/2, height/2, length/2,
-        width/2, height/2, length/2,
-        width/2, height/2, -length/2,
-        -width/2, -height/2, -length/2,
-        width/2, -height/2, -length/2,
-        width/2, -height/2, length/2,
-        -width/2, -height/2, length/2,
-        width/2, -height/2, -length/2,
-        width/2, height/2, -length/2,
-        width/2, height/2, length/2,
-        width/2, -height/2, length/2,
-        -width/2, -height/2, -length/2,
-        -width/2, -height/2, length/2,
-        -width/2, height/2, length/2,
-        -width/2, height/2, -length/2
-    };
-
-    float texcoords[] = {
-        0.0f, 0.0f,
-        1.0f, 0.0f,
-        1.0f, 1.0f,
-        0.0f, 1.0f,
-        1.0f, 0.0f,
-        1.0f, 1.0f,
-        0.0f, 1.0f,
-        0.0f, 0.0f,
-        0.0f, 1.0f,
-        0.0f, 0.0f,
-        1.0f, 0.0f,
-        1.0f, 1.0f,
-        1.0f, 1.0f,
-        0.0f, 1.0f,
-        0.0f, 0.0f,
-        1.0f, 0.0f,
-        1.0f, 0.0f,
-        1.0f, 1.0f,
-        0.0f, 1.0f,
-        0.0f, 0.0f,
-        0.0f, 0.0f,
-        1.0f, 0.0f,
-        1.0f, 1.0f,
-        0.0f, 1.0f
-    };
-
-    float normals[] = {
-        0.0f, 0.0f, 1.0f,
-        0.0f, 0.0f, 1.0f,
-        0.0f, 0.0f, 1.0f,
-        0.0f, 0.0f, 1.0f,
-        0.0f, 0.0f,-1.0f,
-        0.0f, 0.0f,-1.0f,
-        0.0f, 0.0f,-1.0f,
-        0.0f, 0.0f,-1.0f,
-        0.0f, 1.0f, 0.0f,
-        0.0f, 1.0f, 0.0f,
-        0.0f, 1.0f, 0.0f,
-        0.0f, 1.0f, 0.0f,
-        0.0f,-1.0f, 0.0f,
-        0.0f,-1.0f, 0.0f,
-        0.0f,-1.0f, 0.0f,
-        0.0f,-1.0f, 0.0f,
-        1.0f, 0.0f, 0.0f,
-        1.0f, 0.0f, 0.0f,
-        1.0f, 0.0f, 0.0f,
-        1.0f, 0.0f, 0.0f,
-        -1.0f, 0.0f, 0.0f,
-        -1.0f, 0.0f, 0.0f,
-        -1.0f, 0.0f, 0.0f,
-        -1.0f, 0.0f, 0.0f
-    };
-
-    mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float));
-    memcpy(mesh.vertices, vertices, 24*3*sizeof(float));
-
-    mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float));
-    memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float));
-
-    mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float));
-    memcpy(mesh.normals, normals, 24*3*sizeof(float));
-
-    mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short));
-
-    int k = 0;
-
-    // Indices can be initialized right now
-    for (int i = 0; i < 36; i += 6)
-    {
-        mesh.indices[i] = 4*k;
-        mesh.indices[i + 1] = 4*k + 1;
-        mesh.indices[i + 2] = 4*k + 2;
-        mesh.indices[i + 3] = 4*k;
-        mesh.indices[i + 4] = 4*k + 2;
-        mesh.indices[i + 5] = 4*k + 3;
-
-        k++;
-    }
-
-    mesh.vertexCount = 24;
-    mesh.triangleCount = 12;
-
-#else               // Use par_shapes library to generate cube mesh
-/*
-// Platonic solids:
-par_shapes_mesh* par_shapes_create_tetrahedron();       // 4 sides polyhedron (pyramid)
-par_shapes_mesh* par_shapes_create_cube();              // 6 sides polyhedron (cube)
-par_shapes_mesh* par_shapes_create_octahedron();        // 8 sides polyhedron (diamond)
-par_shapes_mesh* par_shapes_create_dodecahedron();      // 12 sides polyhedron
-par_shapes_mesh* par_shapes_create_icosahedron();       // 20 sides polyhedron
-*/
-    // Platonic solid generation: cube (6 sides)
-    // NOTE: No normals/texcoords generated by default
-    par_shapes_mesh *cube = par_shapes_create_cube();
-    cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
-    for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
-    par_shapes_scale(cube, width, height, length);
-    par_shapes_translate(cube, -width/2, 0.0f, -length/2);
-    par_shapes_compute_normals(cube);
-
-    mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
-
-    mesh.vertexCount = cube->ntriangles*3;
-    mesh.triangleCount = cube->ntriangles;
-
-    for (int k = 0; k < mesh.vertexCount; k++)
-    {
-        mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
-        mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
-        mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
-
-        mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
-        mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
-        mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
-
-        mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
-        mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
-    }
-
-    par_shapes_free_mesh(cube);
-#endif
-
-    // Upload vertex data to GPU (static mesh)
-    UploadMesh(&mesh, false);
-
-    return mesh;
-}
-
-// Generate sphere mesh (standard sphere)
-Mesh GenMeshSphere(float radius, int rings, int slices)
-{
-    Mesh mesh = { 0 };
-
-    if ((rings >= 3) && (slices >= 3))
-    {
-        par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
-        par_shapes_scale(sphere, radius, radius, radius);
-        // NOTE: Soft normals are computed internally
-
-        mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = sphere->ntriangles*3;
-        mesh.triangleCount = sphere->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
-            mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(sphere);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: sphere");
-
-    return mesh;
-}
-
-// Generate hemisphere mesh (half sphere, no bottom cap)
-Mesh GenMeshHemiSphere(float radius, int rings, int slices)
-{
-    Mesh mesh = { 0 };
-
-    if ((rings >= 3) && (slices >= 3))
-    {
-        if (radius < 0.0f) radius = 0.0f;
-
-        par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
-        par_shapes_scale(sphere, radius, radius, radius);
-        // NOTE: Soft normals are computed internally
-
-        mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = sphere->ntriangles*3;
-        mesh.triangleCount = sphere->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
-            mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(sphere);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: hemisphere");
-
-    return mesh;
-}
-
-// Generate cylinder mesh
-Mesh GenMeshCylinder(float radius, float height, int slices)
-{
-    Mesh mesh = { 0 };
-
-    if (slices >= 3)
-    {
-        // Instance a cylinder that sits on the Z=0 plane using the given tessellation
-        // levels across the UV domain.  Think of "slices" like a number of pizza
-        // slices, and "stacks" like a number of stacked rings
-        // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
-        par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
-        par_shapes_scale(cylinder, radius, radius, height);
-        par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
-
-        // Generate an orientable disk shape (top cap)
-        par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 });
-        capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
-        for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
-        par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
-        par_shapes_rotate(capTop, 90*DEG2RAD, (float[]){ 0, 1, 0 });
-        par_shapes_translate(capTop, 0, height, 0);
-
-        // Generate an orientable disk shape (bottom cap)
-        par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
-        capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
-        for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
-        par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
-        par_shapes_rotate(capBottom, -90*DEG2RAD, (float[]){ 0, 1, 0 });
-
-        par_shapes_merge_and_free(cylinder, capTop);
-        par_shapes_merge_and_free(cylinder, capBottom);
-
-        mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = cylinder->ntriangles*3;
-        mesh.triangleCount = cylinder->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
-            mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(cylinder);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cylinder");
-
-    return mesh;
-}
-
-// Generate cone/pyramid mesh
-Mesh GenMeshCone(float radius, float height, int slices)
-{
-    Mesh mesh = { 0 };
-
-    if (slices >= 3)
-    {
-        // Instance a cone that sits on the Z=0 plane using the given tessellation
-        // levels across the UV domain.  Think of "slices" like a number of pizza
-        // slices, and "stacks" like a number of stacked rings
-        // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
-        par_shapes_mesh *cone = par_shapes_create_cone(slices, 8);
-        par_shapes_scale(cone, radius, radius, height);
-        par_shapes_rotate(cone, -PI/2.0f, (float[]){ 1, 0, 0 });
-        par_shapes_rotate(cone, PI/2.0f, (float[]){ 0, 1, 0 });
-
-        // Generate an orientable disk shape (bottom cap)
-        par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
-        capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
-        for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
-        par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
-
-        par_shapes_merge_and_free(cone, capBottom);
-
-        mesh.vertices = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(cone->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = cone->ntriangles*3;
-        mesh.triangleCount = cone->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = cone->points[cone->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = cone->points[cone->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = cone->points[cone->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = cone->normals[cone->triangles[k]*3];
-            mesh.normals[k*3 + 1] = cone->normals[cone->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = cone->normals[cone->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = cone->tcoords[cone->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = cone->tcoords[cone->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(cone);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cone");
-
-    return mesh;
-}
-
-// Generate torus mesh
-Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
-{
-    Mesh mesh = { 0 };
-
-    if ((sides >= 3) && (radSeg >= 3))
-    {
-        if (radius > 1.0f) radius = 1.0f;
-        else if (radius < 0.1f) radius = 0.1f;
-
-        // Create a donut that sits on the Z=0 plane with the specified inner radius
-        // The outer radius can be controlled with par_shapes_scale
-        par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
-        par_shapes_scale(torus, size/2, size/2, size/2);
-
-        mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = torus->ntriangles*3;
-        mesh.triangleCount = torus->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
-            mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(torus);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: torus");
-
-    return mesh;
-}
-
-// Generate trefoil knot mesh
-Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
-{
-    Mesh mesh = { 0 };
-
-    if ((sides >= 3) && (radSeg >= 3))
-    {
-        if (radius > 3.0f) radius = 3.0f;
-        else if (radius < 0.5f) radius = 0.5f;
-
-        par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius);
-        par_shapes_scale(knot, size, size, size);
-
-        mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
-        mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float));
-        mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
-
-        mesh.vertexCount = knot->ntriangles*3;
-        mesh.triangleCount = knot->ntriangles;
-
-        for (int k = 0; k < mesh.vertexCount; k++)
-        {
-            mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
-            mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
-            mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
-
-            mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
-            mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
-            mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
-
-            mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
-            mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
-        }
-
-        par_shapes_free_mesh(knot);
-
-        // Upload vertex data to GPU (static mesh)
-        UploadMesh(&mesh, false);
-    }
-    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: knot");
-
-    return mesh;
-}
-
-// Generate a mesh from heightmap
-// NOTE: Vertex data is uploaded to GPU
-Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
-{
-    #define GRAY_VALUE(c) ((float)(c.r + c.g + c.b)/3.0f)
-
-    Mesh mesh = { 0 };
-
-    int mapX = heightmap.width;
-    int mapZ = heightmap.height;
-
-    Color *pixels = LoadImageColors(heightmap);
-
-    // NOTE: One vertex per pixel
-    mesh.triangleCount = (mapX - 1)*(mapZ - 1)*2;    // One quad every four pixels
-
-    mesh.vertexCount = mesh.triangleCount*3;
-
-    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
-    mesh.colors = NULL;
-
-    int vCounter = 0;       // Used to count vertices float by float
-    int tcCounter = 0;      // Used to count texcoords float by float
-    int nCounter = 0;       // Used to count normals float by float
-
-    Vector3 scaleFactor = { size.x/(mapX - 1), size.y/255.0f, size.z/(mapZ - 1) };
-
-    Vector3 vA = { 0 };
-    Vector3 vB = { 0 };
-    Vector3 vC = { 0 };
-    Vector3 vN = { 0 };
-
-    for (int z = 0; z < mapZ-1; z++)
-    {
-        for (int x = 0; x < mapX-1; x++)
-        {
-            // Fill vertices array with data
-            //----------------------------------------------------------
-
-            // one triangle - 3 vertex
-            mesh.vertices[vCounter] = (float)x*scaleFactor.x;
-            mesh.vertices[vCounter + 1] = GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
-            mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
-
-            mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x;
-            mesh.vertices[vCounter + 4] = GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
-            mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
-
-            mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x;
-            mesh.vertices[vCounter + 7] = GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
-            mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
-
-            // Another triangle - 3 vertex
-            mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6];
-            mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7];
-            mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8];
-
-            mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3];
-            mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4];
-            mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
-
-            mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x;
-            mesh.vertices[vCounter + 16] = GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
-            mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
-            vCounter += 18;     // 6 vertex, 18 floats
-
-            // Fill texcoords array with data
-            //--------------------------------------------------------------
-            mesh.texcoords[tcCounter] = (float)x/(mapX - 1);
-            mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1);
-
-            mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1);
-            mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1);
-
-            mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1);
-            mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1);
-
-            mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4];
-            mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5];
-
-            mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2];
-            mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3];
-
-            mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1);
-            mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1);
-            tcCounter += 12;    // 6 texcoords, 12 floats
-
-            // Fill normals array with data
-            //--------------------------------------------------------------
-            for (int i = 0; i < 18; i += 9)
-            {
-                vA.x = mesh.vertices[nCounter + i];
-                vA.y = mesh.vertices[nCounter + i + 1];
-                vA.z = mesh.vertices[nCounter + i + 2];
-
-                vB.x = mesh.vertices[nCounter + i + 3];
-                vB.y = mesh.vertices[nCounter + i + 4];
-                vB.z = mesh.vertices[nCounter + i + 5];
-
-                vC.x = mesh.vertices[nCounter + i + 6];
-                vC.y = mesh.vertices[nCounter + i + 7];
-                vC.z = mesh.vertices[nCounter + i + 8];
-
-                vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA)));
-
-                mesh.normals[nCounter + i] = vN.x;
-                mesh.normals[nCounter + i + 1] = vN.y;
-                mesh.normals[nCounter + i + 2] = vN.z;
-
-                mesh.normals[nCounter + i + 3] = vN.x;
-                mesh.normals[nCounter + i + 4] = vN.y;
-                mesh.normals[nCounter + i + 5] = vN.z;
-
-                mesh.normals[nCounter + i + 6] = vN.x;
-                mesh.normals[nCounter + i + 7] = vN.y;
-                mesh.normals[nCounter + i + 8] = vN.z;
-            }
-
-            nCounter += 18;     // 6 vertex, 18 floats
-        }
-    }
-
-    UnloadImageColors(pixels);  // Unload pixels color data
-
-    // Upload vertex data to GPU (static mesh)
-    UploadMesh(&mesh, false);
-
-    return mesh;
-}
-
-// Generate a cubes mesh from pixel data
-// NOTE: Vertex data is uploaded to GPU
-Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
-{
-    #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
-
-    Mesh mesh = { 0 };
-
-    Color *pixels = LoadImageColors(cubicmap);
-
-    // NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
-    int maxTriangles = cubicmap.width*cubicmap.height*12;
-
-    int vCounter = 0;       // Used to count vertices
-    int tcCounter = 0;      // Used to count texcoords
-    int nCounter = 0;       // Used to count normals
-
-    float w = cubeSize.x;
-    float h = cubeSize.z;
-    float h2 = cubeSize.y;
-
-    Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
-    Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2));
-    Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
-
-    // Define the 6 normals of the cube, we will combine them accordingly later...
-    Vector3 n1 = { 1.0f, 0.0f, 0.0f };
-    Vector3 n2 = { -1.0f, 0.0f, 0.0f };
-    Vector3 n3 = { 0.0f, 1.0f, 0.0f };
-    Vector3 n4 = { 0.0f, -1.0f, 0.0f };
-    Vector3 n5 = { 0.0f, 0.0f, -1.0f };
-    Vector3 n6 = { 0.0f, 0.0f, 1.0f };
-
-    // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
-    typedef struct RectangleF {
-        float x;
-        float y;
-        float width;
-        float height;
-    } RectangleF;
-
-    RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
-    RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
-    RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
-    RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
-    RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
-    RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
-
-    for (int z = 0; z < cubicmap.height; ++z)
-    {
-        for (int x = 0; x < cubicmap.width; ++x)
-        {
-            // Define the 8 vertex of the cube, we will combine them accordingly later...
-            Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
-            Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
-            Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
-            Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) };
-            Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) };
-            Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) };
-            Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
-            Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
-
-            // We check pixel color to be WHITE -> draw full cube
-            if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE))
-            {
-                // Define triangles and checking collateral cubes
-                //------------------------------------------------
-
-                // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
-                // WARNING: Not required for a WHITE cubes, created to allow seeing the map from outside
-                mapVertices[vCounter] = v1;
-                mapVertices[vCounter + 1] = v2;
-                mapVertices[vCounter + 2] = v3;
-                mapVertices[vCounter + 3] = v1;
-                mapVertices[vCounter + 4] = v3;
-                mapVertices[vCounter + 5] = v4;
-                vCounter += 6;
-
-                mapNormals[nCounter] = n3;
-                mapNormals[nCounter + 1] = n3;
-                mapNormals[nCounter + 2] = n3;
-                mapNormals[nCounter + 3] = n3;
-                mapNormals[nCounter + 4] = n3;
-                mapNormals[nCounter + 5] = n3;
-                nCounter += 6;
-
-                mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
-                mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
-                mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
-                mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
-                mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
-                mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
-                tcCounter += 6;
-
-                // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
-                mapVertices[vCounter] = v6;
-                mapVertices[vCounter + 1] = v8;
-                mapVertices[vCounter + 2] = v7;
-                mapVertices[vCounter + 3] = v6;
-                mapVertices[vCounter + 4] = v5;
-                mapVertices[vCounter + 5] = v8;
-                vCounter += 6;
-
-                mapNormals[nCounter] = n4;
-                mapNormals[nCounter + 1] = n4;
-                mapNormals[nCounter + 2] = n4;
-                mapNormals[nCounter + 3] = n4;
-                mapNormals[nCounter + 4] = n4;
-                mapNormals[nCounter + 5] = n4;
-                nCounter += 6;
-
-                mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
-                mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
-                mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
-                mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
-                mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
-                mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
-                tcCounter += 6;
-
-                // Checking cube on bottom of current cube
-                if (((z < cubicmap.height - 1) && COLOR_EQUAL(pixels[(z + 1)*cubicmap.width + x], BLACK)) || (z == cubicmap.height - 1))
-                {
-                    // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
-                    // NOTE: Collateral occluded faces are not generated
-                    mapVertices[vCounter] = v2;
-                    mapVertices[vCounter + 1] = v7;
-                    mapVertices[vCounter + 2] = v3;
-                    mapVertices[vCounter + 3] = v3;
-                    mapVertices[vCounter + 4] = v7;
-                    mapVertices[vCounter + 5] = v8;
-                    vCounter += 6;
-
-                    mapNormals[nCounter] = n6;
-                    mapNormals[nCounter + 1] = n6;
-                    mapNormals[nCounter + 2] = n6;
-                    mapNormals[nCounter + 3] = n6;
-                    mapNormals[nCounter + 4] = n6;
-                    mapNormals[nCounter + 5] = n6;
-                    nCounter += 6;
-
-                    mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y };
-                    mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
-                    mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
-                    mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
-                    mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
-                    mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height };
-                    tcCounter += 6;
-                }
-
-                // Checking cube on top of current cube
-                if (((z > 0) && COLOR_EQUAL(pixels[(z - 1)*cubicmap.width + x], BLACK)) || (z == 0))
-                {
-                    // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
-                    // NOTE: Collateral occluded faces are not generated
-                    mapVertices[vCounter] = v1;
-                    mapVertices[vCounter + 1] = v5;
-                    mapVertices[vCounter + 2] = v6;
-                    mapVertices[vCounter + 3] = v1;
-                    mapVertices[vCounter + 4] = v4;
-                    mapVertices[vCounter + 5] = v5;
-                    vCounter += 6;
-
-                    mapNormals[nCounter] = n5;
-                    mapNormals[nCounter + 1] = n5;
-                    mapNormals[nCounter + 2] = n5;
-                    mapNormals[nCounter + 3] = n5;
-                    mapNormals[nCounter + 4] = n5;
-                    mapNormals[nCounter + 5] = n5;
-                    nCounter += 6;
-
-                    mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
-                    mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
-                    mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height };
-                    mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
-                    mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y };
-                    mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
-                    tcCounter += 6;
-                }
-
-                // Checking cube on right of current cube
-                if (((x < cubicmap.width - 1) && COLOR_EQUAL(pixels[z*cubicmap.width + (x + 1)], BLACK)) || (x == cubicmap.width - 1))
-                {
-                    // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
-                    // NOTE: Collateral occluded faces are not generated
-                    mapVertices[vCounter] = v3;
-                    mapVertices[vCounter + 1] = v8;
-                    mapVertices[vCounter + 2] = v4;
-                    mapVertices[vCounter + 3] = v4;
-                    mapVertices[vCounter + 4] = v8;
-                    mapVertices[vCounter + 5] = v5;
-                    vCounter += 6;
-
-                    mapNormals[nCounter] = n1;
-                    mapNormals[nCounter + 1] = n1;
-                    mapNormals[nCounter + 2] = n1;
-                    mapNormals[nCounter + 3] = n1;
-                    mapNormals[nCounter + 4] = n1;
-                    mapNormals[nCounter + 5] = n1;
-                    nCounter += 6;
-
-                    mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y };
-                    mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
-                    mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
-                    mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
-                    mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
-                    mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height };
-                    tcCounter += 6;
-                }
-
-                // Checking cube on left of current cube
-                if (((x > 0) && COLOR_EQUAL(pixels[z*cubicmap.width + (x - 1)], BLACK)) || (x == 0))
-                {
-                    // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
-                    // NOTE: Collateral occluded faces are not generated
-                    mapVertices[vCounter] = v1;
-                    mapVertices[vCounter + 1] = v7;
-                    mapVertices[vCounter + 2] = v2;
-                    mapVertices[vCounter + 3] = v1;
-                    mapVertices[vCounter + 4] = v6;
-                    mapVertices[vCounter + 5] = v7;
-                    vCounter += 6;
-
-                    mapNormals[nCounter] = n2;
-                    mapNormals[nCounter + 1] = n2;
-                    mapNormals[nCounter + 2] = n2;
-                    mapNormals[nCounter + 3] = n2;
-                    mapNormals[nCounter + 4] = n2;
-                    mapNormals[nCounter + 5] = n2;
-                    nCounter += 6;
-
-                    mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y };
-                    mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
-                    mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y };
-                    mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y };
-                    mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height };
-                    mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
-                    tcCounter += 6;
-                }
-            }
-            // We check pixel color to be BLACK, we will only draw floor and roof
-            else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK))
-            {
-                // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
-                mapVertices[vCounter] = v1;
-                mapVertices[vCounter + 1] = v3;
-                mapVertices[vCounter + 2] = v2;
-                mapVertices[vCounter + 3] = v1;
-                mapVertices[vCounter + 4] = v4;
-                mapVertices[vCounter + 5] = v3;
-                vCounter += 6;
-
-                mapNormals[nCounter] = n4;
-                mapNormals[nCounter + 1] = n4;
-                mapNormals[nCounter + 2] = n4;
-                mapNormals[nCounter + 3] = n4;
-                mapNormals[nCounter + 4] = n4;
-                mapNormals[nCounter + 5] = n4;
-                nCounter += 6;
-
-                mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
-                mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
-                mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
-                mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
-                mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
-                mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
-                tcCounter += 6;
-
-                // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
-                mapVertices[vCounter] = v6;
-                mapVertices[vCounter + 1] = v7;
-                mapVertices[vCounter + 2] = v8;
-                mapVertices[vCounter + 3] = v6;
-                mapVertices[vCounter + 4] = v8;
-                mapVertices[vCounter + 5] = v5;
-                vCounter += 6;
-
-                mapNormals[nCounter] = n3;
-                mapNormals[nCounter + 1] = n3;
-                mapNormals[nCounter + 2] = n3;
-                mapNormals[nCounter + 3] = n3;
-                mapNormals[nCounter + 4] = n3;
-                mapNormals[nCounter + 5] = n3;
-                nCounter += 6;
-
-                mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
-                mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
-                mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
-                mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
-                mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
-                mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
-                tcCounter += 6;
-            }
-        }
-    }
-
-    // Move data from mapVertices temp arrays to vertices float array
-    mesh.vertexCount = vCounter;
-    mesh.triangleCount = vCounter/3;
-
-    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
-    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
-    mesh.colors = NULL;
-
-    int fCounter = 0;
-
-    // Move vertices data
-    for (int i = 0; i < vCounter; i++)
-    {
-        mesh.vertices[fCounter] = mapVertices[i].x;
-        mesh.vertices[fCounter + 1] = mapVertices[i].y;
-        mesh.vertices[fCounter + 2] = mapVertices[i].z;
-        fCounter += 3;
-    }
-
-    fCounter = 0;
-
-    // Move normals data
-    for (int i = 0; i < nCounter; i++)
-    {
-        mesh.normals[fCounter] = mapNormals[i].x;
-        mesh.normals[fCounter + 1] = mapNormals[i].y;
-        mesh.normals[fCounter + 2] = mapNormals[i].z;
-        fCounter += 3;
-    }
-
-    fCounter = 0;
-
-    // Move texcoords data
-    for (int i = 0; i < tcCounter; i++)
-    {
-        mesh.texcoords[fCounter] = mapTexcoords[i].x;
-        mesh.texcoords[fCounter + 1] = mapTexcoords[i].y;
-        fCounter += 2;
-    }
-
-    RL_FREE(mapVertices);
-    RL_FREE(mapNormals);
-    RL_FREE(mapTexcoords);
-
-    UnloadImageColors(pixels);   // Unload pixels color data
-
-    // Upload vertex data to GPU (static mesh)
-    UploadMesh(&mesh, false);
-
-    return mesh;
-}
-#endif      // SUPPORT_MESH_GENERATION
-
-// Compute mesh bounding box limits
-// NOTE: minVertex and maxVertex should be transformed by model transform matrix
-BoundingBox GetMeshBoundingBox(Mesh mesh)
-{
-    // Get min and max vertex to construct bounds (AABB)
-    Vector3 minVertex = { 0 };
-    Vector3 maxVertex = { 0 };
-
-    if (mesh.vertices != NULL)
-    {
-        minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
-        maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
-
-        for (int i = 1; i < mesh.vertexCount; i++)
-        {
-            minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
-            maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
-        }
-    }
-
-    // Create the bounding box
-    BoundingBox box = { 0 };
-    box.min = minVertex;
-    box.max = maxVertex;
-
-    return box;
-}
-
-// Compute mesh tangents
-// NOTE: To calculate mesh tangents and binormals we need mesh vertex positions and texture coordinates
-// Implementation based on: https://answers.unity.com/questions/7789/calculating-tangents-vector4.html
-void GenMeshTangents(Mesh *mesh)
-{
-    if ((mesh->vertices == NULL) || (mesh->texcoords == NULL))
-    {
-        TRACELOG(LOG_WARNING, "MESH: Tangents generation requires texcoord vertex attribute data");
-        return;
-    }
-
-    if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
-    else
-    {
-        RL_FREE(mesh->tangents);
-        mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
-    }
-
-    Vector3 *tan1 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
-    Vector3 *tan2 = (Vector3 *)RL_MALLOC(mesh->vertexCount*sizeof(Vector3));
-
-    if (mesh->vertexCount % 3 != 0)
-    {
-        TRACELOG(LOG_WARNING, "MESH: vertexCount expected to be a multiple of 3. Expect uninitialized values.");
-    }
-
-    for (int i = 0; i <= mesh->vertexCount - 3; i += 3)
-    {
-        // Get triangle vertices
-        Vector3 v1 = { mesh->vertices[(i + 0)*3 + 0], mesh->vertices[(i + 0)*3 + 1], mesh->vertices[(i + 0)*3 + 2] };
-        Vector3 v2 = { mesh->vertices[(i + 1)*3 + 0], mesh->vertices[(i + 1)*3 + 1], mesh->vertices[(i + 1)*3 + 2] };
-        Vector3 v3 = { mesh->vertices[(i + 2)*3 + 0], mesh->vertices[(i + 2)*3 + 1], mesh->vertices[(i + 2)*3 + 2] };
-
-        // Get triangle texcoords
-        Vector2 uv1 = { mesh->texcoords[(i + 0)*2 + 0], mesh->texcoords[(i + 0)*2 + 1] };
-        Vector2 uv2 = { mesh->texcoords[(i + 1)*2 + 0], mesh->texcoords[(i + 1)*2 + 1] };
-        Vector2 uv3 = { mesh->texcoords[(i + 2)*2 + 0], mesh->texcoords[(i + 2)*2 + 1] };
-
-        float x1 = v2.x - v1.x;
-        float y1 = v2.y - v1.y;
-        float z1 = v2.z - v1.z;
-        float x2 = v3.x - v1.x;
-        float y2 = v3.y - v1.y;
-        float z2 = v3.z - v1.z;
-
-        float s1 = uv2.x - uv1.x;
-        float t1 = uv2.y - uv1.y;
-        float s2 = uv3.x - uv1.x;
-        float t2 = uv3.y - uv1.y;
-
-        float div = s1*t2 - s2*t1;
-        float r = (div == 0.0f)? 0.0f : 1.0f/div;
-
-        Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
-        Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
-
-        tan1[i + 0] = sdir;
-        tan1[i + 1] = sdir;
-        tan1[i + 2] = sdir;
-
-        tan2[i + 0] = tdir;
-        tan2[i + 1] = tdir;
-        tan2[i + 2] = tdir;
-    }
-
-    // Compute tangents considering normals
-    for (int i = 0; i < mesh->vertexCount; i++)
-    {
-        Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
-        Vector3 tangent = tan1[i];
-
-        // TODO: Review, not sure if tangent computation is right, just used reference proposed maths...
-#if defined(COMPUTE_TANGENTS_METHOD_01)
-        Vector3 tmp = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
-        tmp = Vector3Normalize(tmp);
-        mesh->tangents[i*4 + 0] = tmp.x;
-        mesh->tangents[i*4 + 1] = tmp.y;
-        mesh->tangents[i*4 + 2] = tmp.z;
-        mesh->tangents[i*4 + 3] = 1.0f;
-#else
-        Vector3OrthoNormalize(&normal, &tangent);
-        mesh->tangents[i*4 + 0] = tangent.x;
-        mesh->tangents[i*4 + 1] = tangent.y;
-        mesh->tangents[i*4 + 2] = tangent.z;
-        mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, tangent), tan2[i]) < 0.0f)? -1.0f : 1.0f;
-#endif
-    }
-
-    RL_FREE(tan1);
-    RL_FREE(tan2);
-
-    if (mesh->vboId != NULL)
-    {
-        if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0)
-        {
-            // Update existing vertex buffer
-            rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0);
-        }
-        else
-        {
-            // Load a new tangent attributes buffer
-            mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
-        }
-
-        rlEnableVertexArray(mesh->vaoId);
-        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
-        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
-        rlDisableVertexArray();
-    }
-
-    TRACELOG(LOG_INFO, "MESH: Tangents data computed and uploaded for provided mesh");
-}
-
-// Draw a model (with texture if set)
-void DrawModel(Model model, Vector3 position, float scale, Color tint)
-{
-    Vector3 vScale = { scale, scale, scale };
-    Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f };
-
-    DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
-}
-
-// Draw a model with extended parameters
-void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
-{
-    // Calculate transformation matrix from function parameters
-    // Get transform matrix (rotation -> scale -> translation)
-    Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
-    Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
-    Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
-
-    Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
-
-    // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
-    model.transform = MatrixMultiply(model.transform, matTransform);
-
-    for (int i = 0; i < model.meshCount; i++)
-    {
-        Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color;
-
-        Color colorTint = WHITE;
-        colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255);
-        colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255);
-        colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255);
-        colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255);
-
-        model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint;
-        DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
-        model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color;
-    }
-}
-
-// Draw a model wires (with texture if set)
-void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
-{
-    rlEnableWireMode();
-
-    DrawModel(model, position, scale, tint);
-
-    rlDisableWireMode();
-}
-
-// Draw a model wires (with texture if set) with extended parameters
-void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
-{
-    rlEnableWireMode();
-
-    DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
-
-    rlDisableWireMode();
-}
-
-// Draw a model points
-void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
-{
-    rlEnablePointMode();
-    rlDisableBackfaceCulling();
-
-    DrawModel(model, position, scale, tint);
-
-    rlEnableBackfaceCulling();
-    rlDisableWireMode();
-}
-
-// Draw a model points
-void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
-{
-    rlEnablePointMode();
-    rlDisableBackfaceCulling();
-
-    DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
-
-    rlEnableBackfaceCulling();
-    rlDisableWireMode();
-}
-
-// Draw a billboard
-void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
-{
-    Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
-
-    DrawBillboardRec(camera, texture, source, position, (Vector2) { scale*fabsf((float)source.width/source.height), scale }, tint);
-}
-
-// Draw a billboard (part of a texture defined by a rectangle)
-void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint)
-{
-    // NOTE: Billboard locked on axis-Y
-    Vector3 up = { 0.0f, 1.0f, 0.0f };
-
-    DrawBillboardPro(camera, texture, source, position, up, size, Vector2Scale(size, 0.5), 0.0f, tint);
-}
-
-// Draw a billboard with additional parameters
-void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint)
-{
-    // Compute the up vector and the right vector
-    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
-    Vector3 right = { matView.m0, matView.m4, matView.m8 };
-    right = Vector3Scale(right, size.x);
-    up = Vector3Scale(up, size.y);
-
-    // Flip the content of the billboard while maintaining the counterclockwise edge rendering order
-    if (size.x < 0.0f)
-    {
-        source.x += size.x;
-        source.width *= -1.0;
-        right = Vector3Negate(right);
-        origin.x *= -1.0f;
-    }
-    if (size.y < 0.0f)
-    {
-        source.y += size.y;
-        source.height *= -1.0;
-        up = Vector3Negate(up);
-        origin.y *= -1.0f;
-    }
-
-    // Draw the texture region described by source on the following rectangle in 3D space:
-    //
-    //                size.x          <--.
-    //  3 ^---------------------------+ 2 \ rotation
-    //    |                           |   /
-    //    |                           |
-    //    |   origin.x   position     |
-    // up |..............             | size.y
-    //    |             .             |
-    //    |             . origin.y    |
-    //    |             .             |
-    //  0 +---------------------------> 1
-    //                right
-    Vector3 forward;
-    if (rotation != 0.0) forward = Vector3CrossProduct(right, up);
-
-    Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(right), origin.x), Vector3Scale(Vector3Normalize(up), origin.y));
-
-    Vector3 points[4];
-    points[0] = Vector3Zero();
-    points[1] = right;
-    points[2] = Vector3Add(up, right);
-    points[3] = up;
-
-    for (int i = 0; i < 4; i++)
-    {
-        points[i] = Vector3Subtract(points[i], origin3D);
-        if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation * DEG2RAD);
-        points[i] = Vector3Add(points[i], position);
-    }
-
-    Vector2 texcoords[4];
-    texcoords[0] = (Vector2) { (float)source.x/texture.width, (float)(source.y + source.height)/texture.height };
-    texcoords[1] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height };
-    texcoords[2] = (Vector2) { (float)(source.x + source.width)/texture.width, (float)source.y/texture.height };
-    texcoords[3] = (Vector2) { (float)source.x/texture.width, (float)source.y/texture.height };
-
-    rlSetTexture(texture.id);
-    rlBegin(RL_QUADS);
-
-        rlColor4ub(tint.r, tint.g, tint.b, tint.a);
-        for (int i = 0; i < 4; i++)
-        {
-            rlTexCoord2f(texcoords[i].x, texcoords[i].y);
-            rlVertex3f(points[i].x, points[i].y, points[i].z);
-        }
-
-    rlEnd();
-    rlSetTexture(0);
-}
-
-// Draw a bounding box with wires
-void DrawBoundingBox(BoundingBox box, Color color)
-{
-    Vector3 size = { 0 };
-
-    size.x = fabsf(box.max.x - box.min.x);
-    size.y = fabsf(box.max.y - box.min.y);
-    size.z = fabsf(box.max.z - box.min.z);
-
-    Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
-
-    DrawCubeWires(center, size.x, size.y, size.z, color);
-}
-
-// Check collision between two spheres
-bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2)
-{
-    bool collision = false;
-
-    // Simple way to check for collision, just checking distance between two points
-    // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
-    /*
-    float dx = center1.x - center2.x;      // X distance between centers
-    float dy = center1.y - center2.y;      // Y distance between centers
-    float dz = center1.z - center2.z;      // Z distance between centers
-
-    float distance = sqrtf(dx*dx + dy*dy + dz*dz);  // Distance between centers
-
-    if (distance <= (radius1 + radius2)) collision = true;
-    */
-
-    // Check for distances squared to avoid sqrtf()
-    if (Vector3DotProduct(Vector3Subtract(center2, center1), Vector3Subtract(center2, center1)) <= (radius1 + radius2)*(radius1 + radius2)) collision = true;
-
-    return collision;
-}
-
-// Check collision between two boxes
-// NOTE: Boxes are defined by two points minimum and maximum
-bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
-{
-    bool collision = true;
-
-    if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
-    {
-        if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
-        if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
-    }
-    else collision = false;
-
-    return collision;
-}
-
-// Check collision between box and sphere
-bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius)
-{
-    bool collision = false;
-
-    float dmin = 0;
-
-    if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2);
-    else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2);
-
-    if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2);
-    else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2);
-
-    if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2);
-    else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2);
-
-    if (dmin <= (radius*radius)) collision = true;
-
-    return collision;
-}
-
-// Get collision info between ray and sphere
-RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius)
-{
-    RayCollision collision = { 0 };
-
-    Vector3 raySpherePos = Vector3Subtract(center, ray.position);
-    float vector = Vector3DotProduct(raySpherePos, ray.direction);
-    float distance = Vector3Length(raySpherePos);
-    float d = radius*radius - (distance*distance - vector*vector);
-
-    collision.hit = d >= 0.0f;
-
-    // Check if ray origin is inside the sphere to calculate the correct collision point
-    if (distance < radius)
-    {
-        collision.distance = vector + sqrtf(d);
-
-        // Calculate collision point
-        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
-
-        // Calculate collision normal (pointing outwards)
-        collision.normal = Vector3Negate(Vector3Normalize(Vector3Subtract(collision.point, center)));
-    }
-    else
-    {
-        collision.distance = vector - sqrtf(d);
-
-        // Calculate collision point
-        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
-
-        // Calculate collision normal (pointing inwards)
-        collision.normal = Vector3Normalize(Vector3Subtract(collision.point, center));
-    }
-
-    return collision;
-}
-
-// Get collision info between ray and box
-RayCollision GetRayCollisionBox(Ray ray, BoundingBox box)
-{
-    RayCollision collision = { 0 };
-
-    // Note: If ray.position is inside the box, the distance is negative (as if the ray was reversed)
-    // Reversing ray.direction will give use the correct result
-    bool insideBox = (ray.position.x > box.min.x) && (ray.position.x < box.max.x) &&
-                     (ray.position.y > box.min.y) && (ray.position.y < box.max.y) &&
-                     (ray.position.z > box.min.z) && (ray.position.z < box.max.z);
-
-    if (insideBox) ray.direction = Vector3Negate(ray.direction);
-
-    float t[11] = { 0 };
-
-    t[8] = 1.0f/ray.direction.x;
-    t[9] = 1.0f/ray.direction.y;
-    t[10] = 1.0f/ray.direction.z;
-
-    t[0] = (box.min.x - ray.position.x)*t[8];
-    t[1] = (box.max.x - ray.position.x)*t[8];
-    t[2] = (box.min.y - ray.position.y)*t[9];
-    t[3] = (box.max.y - ray.position.y)*t[9];
-    t[4] = (box.min.z - ray.position.z)*t[10];
-    t[5] = (box.max.z - ray.position.z)*t[10];
-    t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
-    t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
-
-    collision.hit = !((t[7] < 0) || (t[6] > t[7]));
-    collision.distance = t[6];
-    collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
-
-    // Get box center point
-    collision.normal = Vector3Lerp(box.min, box.max, 0.5f);
-    // Get vector center point->hit point
-    collision.normal = Vector3Subtract(collision.point, collision.normal);
-    // Scale vector to unit cube
-    // NOTE: We use an additional .01 to fix numerical errors
-    collision.normal = Vector3Scale(collision.normal, 2.01f);
-    collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min));
-    // The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
-    // and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal!
-    collision.normal.x = (float)((int)collision.normal.x);
-    collision.normal.y = (float)((int)collision.normal.y);
-    collision.normal.z = (float)((int)collision.normal.z);
-
-    collision.normal = Vector3Normalize(collision.normal);
-
-    if (insideBox)
-    {
-        // Reset ray.direction
-        ray.direction = Vector3Negate(ray.direction);
-        // Fix result
-        collision.distance *= -1.0f;
-        collision.normal = Vector3Negate(collision.normal);
-    }
-
-    return collision;
-}
-
-// Get collision info between ray and mesh
-RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform)
-{
-    RayCollision collision = { 0 };
-
-    // Check if mesh vertex data on CPU for testing
-    if (mesh.vertices != NULL)
-    {
-        int triangleCount = mesh.triangleCount;
-
-        // Test against all triangles in mesh
-        for (int i = 0; i < triangleCount; i++)
-        {
-            Vector3 a, b, c;
-            Vector3* vertdata = (Vector3*)mesh.vertices;
-
-            if (mesh.indices)
-            {
-                a = vertdata[mesh.indices[i*3 + 0]];
-                b = vertdata[mesh.indices[i*3 + 1]];
-                c = vertdata[mesh.indices[i*3 + 2]];
-            }
-            else
-            {
-                a = vertdata[i*3 + 0];
-                b = vertdata[i*3 + 1];
-                c = vertdata[i*3 + 2];
-            }
-
-            a = Vector3Transform(a, transform);
-            b = Vector3Transform(b, transform);
-            c = Vector3Transform(c, transform);
-
-            RayCollision triHitInfo = GetRayCollisionTriangle(ray, a, b, c);
-
-            if (triHitInfo.hit)
-            {
-                // Save the closest hit triangle
-                if ((!collision.hit) || (collision.distance > triHitInfo.distance)) collision = triHitInfo;
-            }
-        }
-    }
-
-    return collision;
-}
-
-// Get collision info between ray and triangle
-// NOTE: The points are expected to be in counter-clockwise winding
-// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
-RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
-{
-    #define EPSILON 0.000001f        // A small number
-
-    RayCollision collision = { 0 };
-    Vector3 edge1 = { 0 };
-    Vector3 edge2 = { 0 };
-    Vector3 p, q, tv;
-    float det, invDet, u, v, t;
-
-    // Find vectors for two edges sharing V1
-    edge1 = Vector3Subtract(p2, p1);
-    edge2 = Vector3Subtract(p3, p1);
-
-    // Begin calculating determinant - also used to calculate u parameter
-    p = Vector3CrossProduct(ray.direction, edge2);
-
-    // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
-    det = Vector3DotProduct(edge1, p);
-
-    // Avoid culling!
-    if ((det > -EPSILON) && (det < EPSILON)) return collision;
-
-    invDet = 1.0f/det;
-
-    // Calculate distance from V1 to ray origin
-    tv = Vector3Subtract(ray.position, p1);
-
-    // Calculate u parameter and test bound
-    u = Vector3DotProduct(tv, p)*invDet;
-
-    // The intersection lies outside the triangle
-    if ((u < 0.0f) || (u > 1.0f)) return collision;
-
-    // Prepare to test v parameter
-    q = Vector3CrossProduct(tv, edge1);
-
-    // Calculate V parameter and test bound
-    v = Vector3DotProduct(ray.direction, q)*invDet;
-
-    // The intersection lies outside the triangle
-    if ((v < 0.0f) || ((u + v) > 1.0f)) return collision;
-
-    t = Vector3DotProduct(edge2, q)*invDet;
-
-    if (t > EPSILON)
-    {
-        // Ray hit, get hit point and normal
-        collision.hit = true;
-        collision.distance = t;
-        collision.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2));
-        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, t));
-    }
-
-    return collision;
-}
-
-// Get collision info between ray and quad
-// NOTE: The points are expected to be in counter-clockwise winding
-RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4)
-{
-    RayCollision collision = { 0 };
-
-    collision = GetRayCollisionTriangle(ray, p1, p2, p4);
-
-    if (!collision.hit) collision = GetRayCollisionTriangle(ray, p2, p3, p4);
-
-    return collision;
-}
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF)
-// Build pose from parent joints
-// NOTE: Required for animations loading (required by IQM and GLTF)
-static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform *transforms)
-{
-    for (int i = 0; i < boneCount; i++)
-    {
-        if (bones[i].parent >= 0)
-        {
-            if (bones[i].parent > i)
-            {
-                TRACELOG(LOG_WARNING, "Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping.", i, bones[i].parent);
-                continue;
-            }
-            transforms[i].rotation = QuaternionMultiply(transforms[bones[i].parent].rotation, transforms[i].rotation);
-            transforms[i].translation = Vector3RotateByQuaternion(transforms[i].translation, transforms[bones[i].parent].rotation);
-            transforms[i].translation = Vector3Add(transforms[i].translation, transforms[bones[i].parent].translation);
-            transforms[i].scale = Vector3Multiply(transforms[i].scale, transforms[bones[i].parent].scale);
-        }
-    }
-}
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_OBJ)
-// Load OBJ mesh data
-//
-// Keep the following information in mind when reading this
-//  - A mesh is created for every material present in the obj file
-//  - the model.meshCount is therefore the materialCount returned from tinyobj
-//  - the mesh is automatically triangulated by tinyobj
-static Model LoadOBJ(const char *fileName)
-{
-    tinyobj_attrib_t objAttributes = { 0 };
-    tinyobj_shape_t* objShapes = NULL;
-    unsigned int objShapeCount = 0;
-
-    tinyobj_material_t* objMaterials = NULL;
-    unsigned int objMaterialCount = 0;
-
-    Model model = { 0 };
-    model.transform = MatrixIdentity();
-
-    char* fileText = LoadFileText(fileName);
-
-    if (fileText == NULL)
-    {
-        TRACELOG(LOG_ERROR, "MODEL Unable to read obj file %s", fileName);
-        return model;
-    }
-
-    char currentDir[1024] = { 0 };
-    strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
-    const char* workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
-    if (CHDIR(workingDir) != 0)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
-    }
-
-    unsigned int dataSize = (unsigned int)strlen(fileText);
-
-    unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
-    int ret = tinyobj_parse_obj(&objAttributes, &objShapes, &objShapeCount, &objMaterials, &objMaterialCount, fileText, dataSize, flags);
-
-    if (ret != TINYOBJ_SUCCESS)
-    {
-        TRACELOG(LOG_ERROR, "MODEL Unable to read obj data %s", fileName);
-        return model;
-    }
-
-    UnloadFileText(fileText);
-
-    unsigned int faceVertIndex = 0;
-    unsigned int nextShape = 1;
-    int lastMaterial = -1;
-    unsigned int meshIndex = 0;
-
-    // count meshes
-    unsigned int nextShapeEnd = objAttributes.num_face_num_verts;
-
-    // see how many verts till the next shape
-
-    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
-
-    // walk all the faces
-    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
-    {
-        if (faceVertIndex >= nextShapeEnd)
-        {
-            // try to find the last vert in the next shape
-            nextShape++;
-            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
-            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
-            meshIndex++;
-        }
-        else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial)
-        {
-            meshIndex++;// if this is a new material, we need to allocate a new mesh
-        }
-
-        lastMaterial = objAttributes.material_ids[faceId];
-        faceVertIndex += objAttributes.face_num_verts[faceId];
-    }
-
-    // allocate the base meshes and materials
-    model.meshCount = meshIndex + 1;
-    model.meshes = (Mesh*)MemAlloc(sizeof(Mesh) * model.meshCount);
-
-    if (objMaterialCount > 0)
-    {
-        model.materialCount = objMaterialCount;
-        model.materials = (Material*)MemAlloc(sizeof(Material) * objMaterialCount);
-    }
-    else // we must allocate at least one material
-    {
-        model.materialCount = 1;
-        model.materials = (Material*)MemAlloc(sizeof(Material) * 1);
-    }
-
-    model.meshMaterial = (int*)MemAlloc(sizeof(int) * model.meshCount);
-
-    // see how many verts are in each mesh
-    unsigned int* localMeshVertexCounts = (unsigned int*)MemAlloc(sizeof(unsigned int) * model.meshCount);
-
-    faceVertIndex = 0;
-    nextShapeEnd = objAttributes.num_face_num_verts;
-    lastMaterial = -1;
-    meshIndex = 0;
-    unsigned int localMeshVertexCount = 0;
-
-    nextShape = 1;
-    if (objShapeCount > 1)
-        nextShapeEnd = objShapes[nextShape].face_offset;
-
-    // walk all the faces
-    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
-    {
-        bool newMesh = false; // do we need a new mesh?
-        if (faceVertIndex >= nextShapeEnd)
-        {
-            // try to find the last vert in the next shape
-            nextShape++;
-            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
-            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
-
-            newMesh = true;
-        }
-        else if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial)
-        {
-            newMesh = true;
-        }
-
-        lastMaterial = objAttributes.material_ids[faceId];
-
-        if (newMesh)
-        {
-            localMeshVertexCounts[meshIndex] = localMeshVertexCount;
-
-            localMeshVertexCount = 0;
-            meshIndex++;
-        }
-
-        faceVertIndex += objAttributes.face_num_verts[faceId];
-        localMeshVertexCount += objAttributes.face_num_verts[faceId];
-    }
-    localMeshVertexCounts[meshIndex] = localMeshVertexCount;
-
-    for (int i = 0; i < model.meshCount; i++)
-    {
-        // allocate the buffers for each mesh
-        unsigned int vertexCount = localMeshVertexCounts[i];
-
-        model.meshes[i].vertexCount = vertexCount;
-        model.meshes[i].triangleCount = vertexCount / 3;
-
-        model.meshes[i].vertices = (float*)MemAlloc(sizeof(float) * vertexCount * 3);
-        model.meshes[i].normals = (float*)MemAlloc(sizeof(float) * vertexCount * 3);
-        model.meshes[i].texcoords = (float*)MemAlloc(sizeof(float) * vertexCount * 2);
-        model.meshes[i].colors = (unsigned char*)MemAlloc(sizeof(unsigned char) * vertexCount * 4);
-    }
-
-    MemFree(localMeshVertexCounts);
-    localMeshVertexCounts = NULL;
-
-    // fill meshes
-    faceVertIndex = 0;
-
-    nextShapeEnd = objAttributes.num_face_num_verts;
-
-    // see how many verts till the next shape
-    nextShape = 1;
-    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
-    lastMaterial = -1;
-    meshIndex = 0;
-    localMeshVertexCount = 0;
-
-    // walk all the faces
-    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
-    {
-        bool newMesh = false; // do we need a new mesh?
-        if (faceVertIndex >= nextShapeEnd)
-        {
-            // try to find the last vert in the next shape
-            nextShape++;
-            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
-            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
-            newMesh = true;
-        }
-        // if this is a new material, we need to allocate a new mesh
-        if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true;
-        lastMaterial = objAttributes.material_ids[faceId];
-
-        if (newMesh)
-        {
-            localMeshVertexCount = 0;
-            meshIndex++;
-        }
-
-        int matId = 0;
-        if (lastMaterial >= 0 && lastMaterial < (int)objMaterialCount)
-            matId = lastMaterial;
-
-        model.meshMaterial[meshIndex] = matId;
-
-        for (int f = 0; f < objAttributes.face_num_verts[faceId]; f++)
-        {
-            int vertIndex = objAttributes.faces[faceVertIndex].v_idx;
-            int normalIndex = objAttributes.faces[faceVertIndex].vn_idx;
-            int texcordIndex = objAttributes.faces[faceVertIndex].vt_idx;
-
-            for (int i = 0; i < 3; i++)
-                model.meshes[meshIndex].vertices[localMeshVertexCount * 3 + i] = objAttributes.vertices[vertIndex * 3 + i];
-
-            for (int i = 0; i < 3; i++)
-                model.meshes[meshIndex].normals[localMeshVertexCount * 3 + i] = objAttributes.normals[normalIndex * 3 + i];
-
-            for (int i = 0; i < 2; i++)
-                model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + i] = objAttributes.texcoords[texcordIndex * 2 + i];
-
-            model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount * 2 + 1];
-
-            for (int i = 0; i < 4; i++)
-                model.meshes[meshIndex].colors[localMeshVertexCount * 4 + i] = 255;
-
-            faceVertIndex++;
-            localMeshVertexCount++;
-        }
-    }
-
-    if (objMaterialCount > 0) ProcessMaterialsOBJ(model.materials, objMaterials, objMaterialCount);
-    else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh
-
-    tinyobj_attrib_free(&objAttributes);
-    tinyobj_shapes_free(objShapes, objShapeCount);
-    tinyobj_materials_free(objMaterials, objMaterialCount);
-
-    for (int i = 0; i < model.meshCount; i++)
-        UploadMesh(model.meshes + i, true);
-
-    // Restore current working directory
-    if (CHDIR(currentDir) != 0)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
-    }
-
-    return model;
-}
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_IQM)
-// Load IQM mesh data
-static Model LoadIQM(const char *fileName)
-{
-    #define IQM_MAGIC     "INTERQUAKEMODEL" // IQM file magic number
-    #define IQM_VERSION          2          // only IQM version 2 supported
-
-    #define BONE_NAME_LENGTH    32          // BoneInfo name string length
-    #define MESH_NAME_LENGTH    32          // Mesh name string length
-    #define MATERIAL_NAME_LENGTH 32         // Material name string length
-
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-    unsigned char *fileDataPtr = fileData;
-
-    // IQM file structs
-    //-----------------------------------------------------------------------------------
-    typedef struct IQMHeader {
-        char magic[16];
-        unsigned int version;
-        unsigned int dataSize;
-        unsigned int flags;
-        unsigned int num_text, ofs_text;
-        unsigned int num_meshes, ofs_meshes;
-        unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
-        unsigned int num_triangles, ofs_triangles, ofs_adjacency;
-        unsigned int num_joints, ofs_joints;
-        unsigned int num_poses, ofs_poses;
-        unsigned int num_anims, ofs_anims;
-        unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
-        unsigned int num_comment, ofs_comment;
-        unsigned int num_extensions, ofs_extensions;
-    } IQMHeader;
-
-    typedef struct IQMMesh {
-        unsigned int name;
-        unsigned int material;
-        unsigned int first_vertex, num_vertexes;
-        unsigned int first_triangle, num_triangles;
-    } IQMMesh;
-
-    typedef struct IQMTriangle {
-        unsigned int vertex[3];
-    } IQMTriangle;
-
-    typedef struct IQMJoint {
-        unsigned int name;
-        int parent;
-        float translate[3], rotate[4], scale[3];
-    } IQMJoint;
-
-    typedef struct IQMVertexArray {
-        unsigned int type;
-        unsigned int flags;
-        unsigned int format;
-        unsigned int size;
-        unsigned int offset;
-    } IQMVertexArray;
-
-    // NOTE: Below IQM structures are not used but listed for reference
-    /*
-    typedef struct IQMAdjacency {
-        unsigned int triangle[3];
-    } IQMAdjacency;
-
-    typedef struct IQMPose {
-        int parent;
-        unsigned int mask;
-        float channeloffset[10];
-        float channelscale[10];
-    } IQMPose;
-
-    typedef struct IQMAnim {
-        unsigned int name;
-        unsigned int first_frame, num_frames;
-        float framerate;
-        unsigned int flags;
-    } IQMAnim;
-
-    typedef struct IQMBounds {
-        float bbmin[3], bbmax[3];
-        float xyradius, radius;
-    } IQMBounds;
-    */
-    //-----------------------------------------------------------------------------------
-
-    // IQM vertex data types
-    enum {
-        IQM_POSITION     = 0,
-        IQM_TEXCOORD     = 1,
-        IQM_NORMAL       = 2,
-        IQM_TANGENT      = 3,       // NOTE: Tangents unused by default
-        IQM_BLENDINDEXES = 4,
-        IQM_BLENDWEIGHTS = 5,
-        IQM_COLOR        = 6,
-        IQM_CUSTOM       = 0x10     // NOTE: Custom vertex values unused by default
-    };
-
-    Model model = { 0 };
-
-    IQMMesh *imesh = NULL;
-    IQMTriangle *tri = NULL;
-    IQMVertexArray *va = NULL;
-    IQMJoint *ijoint = NULL;
-
-    float *vertex = NULL;
-    float *normal = NULL;
-    float *text = NULL;
-    char *blendi = NULL;
-    unsigned char *blendw = NULL;
-    unsigned char *color = NULL;
-
-    // In case file can not be read, return an empty model
-    if (fileDataPtr == NULL) return model;
-
-    const char *basePath = GetDirectoryPath(fileName);
-
-    // Read IQM header
-    IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
-
-    if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
-        return model;
-    }
-
-    if (iqmHeader->version != IQM_VERSION)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
-        return model;
-    }
-
-    //fileDataPtr += sizeof(IQMHeader);       // Move file data pointer
-
-    // Meshes data processing
-    imesh = RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh));
-    //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET);
-    //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile);
-    memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh));
-
-    model.meshCount = iqmHeader->num_meshes;
-    model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
-
-    model.materialCount = model.meshCount;
-    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
-    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
-
-    char name[MESH_NAME_LENGTH] = { 0 };
-    char material[MATERIAL_NAME_LENGTH] = { 0 };
-
-    for (int i = 0; i < model.meshCount; i++)
-    {
-        //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET);
-        //fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile);
-        memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char));
-
-        //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET);
-        //fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile);
-        memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char));
-
-        model.materials[i] = LoadMaterialDefault();
-        model.materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture(TextFormat("%s/%s", basePath, material));
-
-        model.meshMaterial[i] = i;
-
-        TRACELOG(LOG_DEBUG, "MODEL: [%s] mesh name (%s), material (%s)", fileName, name, material);
-
-        model.meshes[i].vertexCount = imesh[i].num_vertexes;
-
-        model.meshes[i].vertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));       // Default vertex positions
-        model.meshes[i].normals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));        // Default vertex normals
-        model.meshes[i].texcoords = RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float));      // Default vertex texcoords
-
-        model.meshes[i].boneIds = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char));  // Up-to 4 bones supported!
-        model.meshes[i].boneWeights = RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float));      // Up-to 4 bones supported!
-
-        model.meshes[i].triangleCount = imesh[i].num_triangles;
-        model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
-
-        // Animated vertex data, what we actually process for rendering
-        // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
-        model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
-        model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
-    }
-
-    // Triangles data processing
-    tri = RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle));
-    //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET);
-    //fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile);
-    memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle));
-
-    for (int m = 0; m < model.meshCount; m++)
-    {
-        int tcounter = 0;
-
-        for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++)
-        {
-            // IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order,
-            // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes
-            // NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default
-            model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex;
-            model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex;
-            model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
-            tcounter += 3;
-        }
-    }
-
-    // Vertex arrays data processing
-    va = RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
-    //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET);
-    //fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile);
-    memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
-
-    for (unsigned int i = 0; i < iqmHeader->num_vertexarrays; i++)
-    {
-        switch (va[i].type)
-        {
-            case IQM_POSITION:
-            {
-                vertex = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
-                memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    int vCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
-                    {
-                        model.meshes[m].vertices[vCounter] = vertex[i];
-                        model.meshes[m].animVertices[vCounter] = vertex[i];
-                        vCounter++;
-                    }
-                }
-            } break;
-            case IQM_NORMAL:
-            {
-                normal = RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
-                memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    int vCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
-                    {
-                        model.meshes[m].normals[vCounter] = normal[i];
-                        model.meshes[m].animNormals[vCounter] = normal[i];
-                        vCounter++;
-                    }
-                }
-            } break;
-            case IQM_TEXCOORD:
-            {
-                text = RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
-                memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    int vCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
-                    {
-                        model.meshes[m].texcoords[vCounter] = text[i];
-                        vCounter++;
-                    }
-                }
-            } break;
-            case IQM_BLENDINDEXES:
-            {
-                blendi = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
-                memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    int boneCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
-                    {
-                        model.meshes[m].boneIds[boneCounter] = blendi[i];
-                        boneCounter++;
-                    }
-                }
-            } break;
-            case IQM_BLENDWEIGHTS:
-            {
-                blendw = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
-                memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    int boneCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
-                    {
-                        model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f;
-                        boneCounter++;
-                    }
-                }
-            } break;
-            case IQM_COLOR:
-            {
-                color = RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
-                //fseek(iqmFile, va[i].offset, SEEK_SET);
-                //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
-                memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
-
-                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
-                {
-                    model.meshes[m].colors = RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char));
-
-                    int vCounter = 0;
-                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
-                    {
-                        model.meshes[m].colors[vCounter] = color[i];
-                        vCounter++;
-                    }
-                }
-            } break;
-        }
-    }
-
-    // Bones (joints) data processing
-    ijoint = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
-    //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET);
-    //fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile);
-    memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
-
-    model.boneCount = iqmHeader->num_joints;
-    model.bones = RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
-    model.bindPose = RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
-
-    for (unsigned int i = 0; i < iqmHeader->num_joints; i++)
-    {
-        // Bones
-        model.bones[i].parent = ijoint[i].parent;
-        //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET);
-        //fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile);
-        memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char));
-
-        // Bind pose (base pose)
-        model.bindPose[i].translation.x = ijoint[i].translate[0];
-        model.bindPose[i].translation.y = ijoint[i].translate[1];
-        model.bindPose[i].translation.z = ijoint[i].translate[2];
-
-        model.bindPose[i].rotation.x = ijoint[i].rotate[0];
-        model.bindPose[i].rotation.y = ijoint[i].rotate[1];
-        model.bindPose[i].rotation.z = ijoint[i].rotate[2];
-        model.bindPose[i].rotation.w = ijoint[i].rotate[3];
-
-        model.bindPose[i].scale.x = ijoint[i].scale[0];
-        model.bindPose[i].scale.y = ijoint[i].scale[1];
-        model.bindPose[i].scale.z = ijoint[i].scale[2];
-    }
-
-    BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose);
-
-    for (int i = 0; i < model.meshCount; i++)
-    {
-        model.meshes[i].boneCount = model.boneCount;
-        model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
-
-        for (int j = 0; j < model.meshes[i].boneCount; j++)
-        {
-            model.meshes[i].boneMatrices[j] = MatrixIdentity();
-        }
-    }
-
-    UnloadFileData(fileData);
-
-    RL_FREE(imesh);
-    RL_FREE(tri);
-    RL_FREE(va);
-    RL_FREE(vertex);
-    RL_FREE(normal);
-    RL_FREE(text);
-    RL_FREE(blendi);
-    RL_FREE(blendw);
-    RL_FREE(ijoint);
-    RL_FREE(color);
-
-    return model;
-}
-
-// Load IQM animation data
-static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount)
-{
-    #define IQM_MAGIC       "INTERQUAKEMODEL"   // IQM file magic number
-    #define IQM_VERSION     2                   // only IQM version 2 supported
-
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-    unsigned char *fileDataPtr = fileData;
-
-    typedef struct IQMHeader {
-        char magic[16];
-        unsigned int version;
-        unsigned int dataSize;
-        unsigned int flags;
-        unsigned int num_text, ofs_text;
-        unsigned int num_meshes, ofs_meshes;
-        unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
-        unsigned int num_triangles, ofs_triangles, ofs_adjacency;
-        unsigned int num_joints, ofs_joints;
-        unsigned int num_poses, ofs_poses;
-        unsigned int num_anims, ofs_anims;
-        unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
-        unsigned int num_comment, ofs_comment;
-        unsigned int num_extensions, ofs_extensions;
-    } IQMHeader;
-
-    typedef struct IQMJoint {
-        unsigned int name;
-        int parent;
-        float translate[3], rotate[4], scale[3];
-    } IQMJoint;
-
-    typedef struct IQMPose {
-        int parent;
-        unsigned int mask;
-        float channeloffset[10];
-        float channelscale[10];
-    } IQMPose;
-
-    typedef struct IQMAnim {
-        unsigned int name;
-        unsigned int first_frame, num_frames;
-        float framerate;
-        unsigned int flags;
-    } IQMAnim;
-
-    // In case file can not be read, return an empty model
-    if (fileDataPtr == NULL) return NULL;
-
-    // Read IQM header
-    IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
-
-    if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
-        return NULL;
-    }
-
-    if (iqmHeader->version != IQM_VERSION)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
-        return NULL;
-    }
-
-    // Get bones data
-    IQMPose *poses = RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose));
-    //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET);
-    //fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile);
-    memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose));
-
-    // Get animations data
-    *animCount = iqmHeader->num_anims;
-    IQMAnim *anim = RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim));
-    //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET);
-    //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile);
-    memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim));
-
-    ModelAnimation *animations = RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
-
-    // frameposes
-    unsigned short *framedata = RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
-    //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
-    //fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile);
-    memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
-
-    // joints
-    IQMJoint *joints = RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
-    memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
-
-    for (unsigned int a = 0; a < iqmHeader->num_anims; a++)
-    {
-        animations[a].frameCount = anim[a].num_frames;
-        animations[a].boneCount = iqmHeader->num_poses;
-        animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
-        animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
-        memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32);   //  I don't like this 32 here
-        TraceLog(LOG_INFO, "IQM Anim %s", animations[a].name);
-        // animations[a].framerate = anim.framerate;     // TODO: Use animation framerate data?
-
-        for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
-        {
-            // If animations and skeleton are in the same file, copy bone names to anim
-            if (iqmHeader->num_joints > 0)
-                memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
-            else
-                strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); // default bone name otherwise
-            animations[a].bones[j].parent = poses[j].parent;
-        }
-
-        for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
-
-        int dcounter = anim[a].first_frame*iqmHeader->num_framechannels;
-
-        for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
-        {
-            for (unsigned int i = 0; i < iqmHeader->num_poses; i++)
-            {
-                animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0];
-
-                if (poses[i].mask & 0x01)
-                {
-                    animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1];
-
-                if (poses[i].mask & 0x02)
-                {
-                    animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2];
-
-                if (poses[i].mask & 0x04)
-                {
-                    animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3];
-
-                if (poses[i].mask & 0x08)
-                {
-                    animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4];
-
-                if (poses[i].mask & 0x10)
-                {
-                    animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5];
-
-                if (poses[i].mask & 0x20)
-                {
-                    animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6];
-
-                if (poses[i].mask & 0x40)
-                {
-                    animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7];
-
-                if (poses[i].mask & 0x80)
-                {
-                    animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8];
-
-                if (poses[i].mask & 0x100)
-                {
-                    animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9];
-
-                if (poses[i].mask & 0x200)
-                {
-                    animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
-                    dcounter++;
-                }
-
-                animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation);
-            }
-        }
-
-        // Build frameposes
-        for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
-        {
-            for (int i = 0; i < animations[a].boneCount; i++)
-            {
-                if (animations[a].bones[i].parent >= 0)
-                {
-                    animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation);
-                    animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation);
-                    animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation);
-                    animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale);
-                }
-            }
-        }
-    }
-
-    UnloadFileData(fileData);
-
-    RL_FREE(joints);
-    RL_FREE(framedata);
-    RL_FREE(poses);
-    RL_FREE(anim);
-
-    return animations;
-}
-
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_GLTF)
-// Load file data callback for cgltf
-static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, const char *path, cgltf_size *size, void **data)
-{
-    int filesize;
-    unsigned char *filedata = LoadFileData(path, &filesize);
-
-    if (filedata == NULL) return cgltf_result_io_error;
-
-    *size = filesize;
-    *data = filedata;
-
-    return cgltf_result_success;
-}
-
-// Release file data callback for cgltf
-static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data)
-{
-    UnloadFileData(data);
-}
-
-// Load image from different glTF provided methods (uri, path, buffer_view)
-static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPath)
-{
-    Image image = { 0 };
-
-    if (cgltfImage->uri != NULL)     // Check if image data is provided as an uri (base64 or path)
-    {
-        if ((strlen(cgltfImage->uri) > 5) &&
-            (cgltfImage->uri[0] == 'd') &&
-            (cgltfImage->uri[1] == 'a') &&
-            (cgltfImage->uri[2] == 't') &&
-            (cgltfImage->uri[3] == 'a') &&
-            (cgltfImage->uri[4] == ':'))     // Check if image is provided as base64 text data
-        {
-            // Data URI Format: data:<mediatype>;base64,<data>
-
-            // Find the comma
-            int i = 0;
-            while ((cgltfImage->uri[i] != ',') && (cgltfImage->uri[i] != 0)) i++;
-
-            if (cgltfImage->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image");
-            else
-            {
-                int base64Size = (int)strlen(cgltfImage->uri + i + 1);
-                while (cgltfImage->uri[i + base64Size] == '=') base64Size--;    // Ignore optional paddings
-                int numberOfEncodedBits = base64Size*6 - (base64Size*6) % 8 ;   // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
-                int outSize = numberOfEncodedBits/8 ;                           // Actual encoded bytes
-                void *data = NULL;
-
-                cgltf_options options = { 0 };
-                options.file.read = LoadFileGLTFCallback;
-                options.file.release = ReleaseFileGLTFCallback;
-                cgltf_result result = cgltf_load_buffer_base64(&options, outSize, cgltfImage->uri + i + 1, &data);
-
-                if (result == cgltf_result_success)
-                {
-                    image = LoadImageFromMemory(".png", (unsigned char *)data, outSize);
-                    RL_FREE(data);
-                }
-            }
-        }
-        else     // Check if image is provided as image path
-        {
-            image = LoadImage(TextFormat("%s/%s", texPath, cgltfImage->uri));
-        }
-    }
-    else if (cgltfImage->buffer_view->buffer->data != NULL)    // Check if image is provided as data buffer
-    {
-        unsigned char *data = RL_MALLOC(cgltfImage->buffer_view->size);
-        int offset = (int)cgltfImage->buffer_view->offset;
-        int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1;
-
-        // Copy buffer data to memory for loading
-        for (unsigned int i = 0; i < cgltfImage->buffer_view->size; i++)
-        {
-            data[i] = ((unsigned char *)cgltfImage->buffer_view->buffer->data)[offset];
-            offset += stride;
-        }
-
-        // Check mime_type for image: (cgltfImage->mime_type == "image/png")
-        // NOTE: Detected that some models define mime_type as "image\\/png"
-        if ((strcmp(cgltfImage->mime_type, "image\\/png") == 0) ||
-            (strcmp(cgltfImage->mime_type, "image/png") == 0)) image = LoadImageFromMemory(".png", data, (int)cgltfImage->buffer_view->size);
-        else if ((strcmp(cgltfImage->mime_type, "image\\/jpeg") == 0) ||
-                 (strcmp(cgltfImage->mime_type, "image/jpeg") == 0)) image = LoadImageFromMemory(".jpg", data, (int)cgltfImage->buffer_view->size);
-        else TRACELOG(LOG_WARNING, "MODEL: glTF image data MIME type not recognized", TextFormat("%s/%s", texPath, cgltfImage->uri));
-
-        RL_FREE(data);
-    }
-
-    return image;
-}
-
-// Load bone info from GLTF skin data
-static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount)
-{
-    *boneCount = (int)skin.joints_count;
-    BoneInfo *bones = RL_MALLOC(skin.joints_count*sizeof(BoneInfo));
-
-    for (unsigned int i = 0; i < skin.joints_count; i++)
-    {
-        cgltf_node node = *skin.joints[i];
-        if (node.name != NULL)
-        {
-            strncpy(bones[i].name, node.name, sizeof(bones[i].name));
-            bones[i].name[sizeof(bones[i].name) - 1] = '\0';
-        }
-
-        // Find parent bone index
-        int parentIndex = -1;
-
-        for (unsigned int j = 0; j < skin.joints_count; j++)
-        {
-            if (skin.joints[j] == node.parent)
-            {
-                parentIndex = (int)j;
-                break;
-            }
-        }
-
-        bones[i].parent = parentIndex;
-    }
-
-    return bones;
-}
-
-// Load glTF file into model struct, .gltf and .glb supported
-static Model LoadGLTF(const char *fileName)
-{
-    /*********************************************************************************************
-
-        Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend)
-        Transform handling implemented by Paul Melis (@paulmelis).
-        Reviewed by Ramon Santamaria (@raysan5)
-
-        FEATURES:
-          - Supports .gltf and .glb files
-          - Supports embedded (base64) or external textures
-          - Supports PBR metallic/roughness flow, loads material textures, values and colors
-                     PBR specular/glossiness flow and extended texture flows not supported
-          - Supports multiple meshes per model (every primitives is loaded as a separate mesh)
-          - Supports basic animations
-          - Transforms, including parent-child relations, are applied on the mesh data, but the
-            hierarchy is not kept (as it can't be represented).
-          - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes)
-            are turned into separate raylib Meshes.
-
-        RESTRICTIONS:
-          - Only triangle meshes supported
-          - Vertex attribute types and formats supported:
-              > Vertices (position): vec3: float
-              > Normals: vec3: float
-              > Texcoords: vec2: float
-              > Colors: vec4: u8, u16, f32 (normalized)
-              > Indices: u16, u32 (truncated to u16)
-          - Scenes defined in the glTF file are ignored. All nodes in the file
-            are used.
-
-    ***********************************************************************************************/
-
-    // Macro to simplify attributes loading code
-    #define LOAD_ATTRIBUTE(accesor, numComp, srcType, dstPtr) LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, srcType)
-
-    #define LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, dstType) \
-    { \
-        int n = 0; \
-        srcType *buffer = (srcType *)accesor->buffer_view->buffer->data + accesor->buffer_view->offset/sizeof(srcType) + accesor->offset/sizeof(srcType); \
-        for (unsigned int k = 0; k < accesor->count; k++) \
-        {\
-            for (int l = 0; l < numComp; l++) \
-            {\
-                dstPtr[numComp*k + l] = (dstType)buffer[n + l];\
-            }\
-            n += (int)(accesor->stride/sizeof(srcType));\
-        }\
-    }
-
-    Model model = { 0 };
-
-    // glTF file loading
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
-    if (fileData == NULL) return model;
-
-    // glTF data loading
-    cgltf_options options = { 0 };
-    options.file.read = LoadFileGLTFCallback;
-    options.file.release = ReleaseFileGLTFCallback;
-    cgltf_data *data = NULL;
-    cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
-
-    if (result == cgltf_result_success)
-    {
-        if (data->file_type == cgltf_file_type_glb) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glb) loaded successfully", fileName);
-        else if (data->file_type == cgltf_file_type_gltf) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glTF) loaded successfully", fileName);
-        else TRACELOG(LOG_WARNING, "MODEL: [%s] Model format not recognized", fileName);
-
-        TRACELOG(LOG_INFO, "    > Meshes count: %i", data->meshes_count);
-        TRACELOG(LOG_INFO, "    > Materials count: %i (+1 default)", data->materials_count);
-        TRACELOG(LOG_DEBUG, "    > Buffers count: %i", data->buffers_count);
-        TRACELOG(LOG_DEBUG, "    > Images count: %i", data->images_count);
-        TRACELOG(LOG_DEBUG, "    > Textures count: %i", data->textures_count);
-
-        // Force reading data buffers (fills buffer_view->buffer->data)
-        // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded
-        result = cgltf_load_buffers(&options, data, fileName);
-        if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
-
-        int primitivesCount = 0;
-        // NOTE: We will load every primitive in the glTF as a separate raylib Mesh.
-        // Determine total number of meshes needed from the node hierarchy.
-        for (unsigned int i = 0; i < data->nodes_count; i++)
-        {
-            cgltf_node *node = &(data->nodes[i]);
-            cgltf_mesh *mesh = node->mesh;
-            if (!mesh)
-                continue;
-
-            for (unsigned int p = 0; p < mesh->primitives_count; p++)
-            {
-                if (mesh->primitives[p].type == cgltf_primitive_type_triangles)
-                    primitivesCount++;
-            }
-        }
-        TRACELOG(LOG_DEBUG, "    > Primitives (triangles only) count based on hierarchy : %i", primitivesCount);
-
-        // Load our model data: meshes and materials
-        model.meshCount = primitivesCount;
-        model.meshes = RL_CALLOC(model.meshCount, sizeof(Mesh));
-
-        // NOTE: We keep an extra slot for default material, in case some mesh requires it
-        model.materialCount = (int)data->materials_count + 1;
-        model.materials = RL_CALLOC(model.materialCount, sizeof(Material));
-        model.materials[0] = LoadMaterialDefault();     // Load default material (index: 0)
-
-        // Load mesh-material indices, by default all meshes are mapped to material index: 0
-        model.meshMaterial = RL_CALLOC(model.meshCount, sizeof(int));
-
-        // Load materials data
-        //----------------------------------------------------------------------------------------------------
-        for (unsigned int i = 0, j = 1; i < data->materials_count; i++, j++)
-        {
-            model.materials[j] = LoadMaterialDefault();
-            const char *texPath = GetDirectoryPath(fileName);
-
-            // Check glTF material flow: PBR metallic/roughness flow
-            // NOTE: Alternatively, materials can follow PBR specular/glossiness flow
-            if (data->materials[i].has_pbr_metallic_roughness)
-            {
-                // Load base color texture (albedo)
-                if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture)
-                {
-                    Image imAlbedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath);
-                    if (imAlbedo.data != NULL)
-                    {
-                        model.materials[j].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(imAlbedo);
-                        UnloadImage(imAlbedo);
-                    }
-                }
-                // Load base color factor (tint)
-                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
-                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
-                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
-                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
-
-                // Load metallic/roughness texture
-                if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
-                {
-                    Image imMetallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath);
-                    if (imMetallicRoughness.data != NULL)
-                    {
-                        model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(imMetallicRoughness);
-                        UnloadImage(imMetallicRoughness);
-                    }
-
-                    // Load metallic/roughness material properties
-                    float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
-                    model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].value = roughness;
-
-                    float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
-                    model.materials[j].maps[MATERIAL_MAP_METALNESS].value = metallic;
-                }
-
-                // Load normal texture
-                if (data->materials[i].normal_texture.texture)
-                {
-                    Image imNormal = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath);
-                    if (imNormal.data != NULL)
-                    {
-                        model.materials[j].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(imNormal);
-                        UnloadImage(imNormal);
-                    }
-                }
-
-                // Load ambient occlusion texture
-                if (data->materials[i].occlusion_texture.texture)
-                {
-                    Image imOcclusion = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath);
-                    if (imOcclusion.data != NULL)
-                    {
-                        model.materials[j].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(imOcclusion);
-                        UnloadImage(imOcclusion);
-                    }
-                }
-
-                // Load emissive texture
-                if (data->materials[i].emissive_texture.texture)
-                {
-                    Image imEmissive = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath);
-                    if (imEmissive.data != NULL)
-                    {
-                        model.materials[j].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(imEmissive);
-                        UnloadImage(imEmissive);
-                    }
-
-                    // Load emissive color factor
-                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.r = (unsigned char)(data->materials[i].emissive_factor[0]*255);
-                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.g = (unsigned char)(data->materials[i].emissive_factor[1]*255);
-                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.b = (unsigned char)(data->materials[i].emissive_factor[2]*255);
-                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.a = 255;
-                }
-            }
-
-            // Other possible materials not supported by raylib pipeline:
-            // has_clearcoat, has_transmission, has_volume, has_ior, has specular, has_sheen
-        }
-
-        // Visit each node in the hierarchy and process any mesh linked from it.
-        // Each primitive within a glTF node becomes a Raylib Mesh.
-        // The local-to-world transform of each node is used to transform the
-        // points/normals/tangents of the created Mesh(es).
-        // Any glTF mesh linked from more than one Node (i.e. instancing)
-        // is turned into multiple Mesh's, as each Node will have its own
-        // transform applied.
-        // Note: the code below disregards the scenes defined in the file, all nodes are used.
-        //----------------------------------------------------------------------------------------------------
-        int meshIndex = 0;
-        for (unsigned int i = 0; i < data->nodes_count; i++)
-        {
-            cgltf_node *node = &(data->nodes[i]);
-
-            cgltf_mesh *mesh = node->mesh;
-            if (!mesh)
-                continue;
-
-            cgltf_float worldTransform[16];
-            cgltf_node_transform_world(node, worldTransform);
-
-            Matrix worldMatrix = {
-                worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12],
-                worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13],
-                worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14],
-                worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15]
-            };
-
-            Matrix worldMatrixNormals = MatrixTranspose(MatrixInvert(worldMatrix));
-
-            for (unsigned int p = 0; p < mesh->primitives_count; p++)
-            {
-                // NOTE: We only support primitives defined by triangles
-                // Other alternatives: points, lines, line_strip, triangle_strip
-                if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
-
-                // NOTE: Attributes data could be provided in several data formats (8, 8u, 16u, 32...),
-                // Only some formats for each attribute type are supported, read info at the top of this function!
-
-                for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
-                {
-                    // Check the different attributes for every primitive
-                    if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_position)      // POSITION, vec3, float
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        // WARNING: SPECS: POSITION accessor MUST have its min and max properties defined
-
-                        if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f))
-                        {
-                            // Init raylib mesh vertices to copy glTF attribute data
-                            model.meshes[meshIndex].vertexCount = (int)attribute->count;
-                            model.meshes[meshIndex].vertices = RL_MALLOC(attribute->count*3*sizeof(float));
-
-                            // Load 3 components of float data type into mesh.vertices
-                            LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices)
-
-                            // Transform the vertices
-                            float *vertices = model.meshes[meshIndex].vertices;
-                            for (unsigned int k = 0; k < attribute->count; k++)
-                            {
-                                Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k+1], vertices[3*k+2] }, worldMatrix);
-                                vertices[3*k] = vt.x;
-                                vertices[3*k+1] = vt.y;
-                                vertices[3*k+2] = vt.z;
-                            }
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Vertices attribute data format not supported, use vec3 float", fileName);
-                    }
-                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_normal)   // NORMAL, vec3, float
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f))
-                        {
-                            // Init raylib mesh normals to copy glTF attribute data
-                            model.meshes[meshIndex].normals = RL_MALLOC(attribute->count*3*sizeof(float));
-
-                            // Load 3 components of float data type into mesh.normals
-                            LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals)
-
-                            // Transform the normals
-                            float *normals = model.meshes[meshIndex].normals;
-                            for (unsigned int k = 0; k < attribute->count; k++)
-                            {
-                                Vector3 nt = Vector3Transform((Vector3){ normals[3*k], normals[3*k+1], normals[3*k+2] }, worldMatrixNormals);
-                                normals[3*k] = nt.x;
-                                normals[3*k+1] = nt.y;
-                                normals[3*k+2] = nt.z;
-                            }
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Normal attribute data format not supported, use vec3 float", fileName);
-                    }
-                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent)   // TANGENT, vec3, float
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f))
-                        {
-                            // Init raylib mesh tangent to copy glTF attribute data
-                            model.meshes[meshIndex].tangents = RL_MALLOC(attribute->count*4*sizeof(float));
-
-                            // Load 4 components of float data type into mesh.tangents
-                            LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents)
-
-                            // Transform the tangents
-                            float *tangents = model.meshes[meshIndex].tangents;
-                            for (unsigned int k = 0; k < attribute->count; k++)
-                            {
-                                Vector3 tt = Vector3Transform((Vector3){ tangents[3*k], tangents[3*k+1], tangents[3*k+2] }, worldMatrix);
-                                tangents[3*k] = tt.x;
-                                tangents[3*k+1] = tt.y;
-                                tangents[3*k+2] = tt.z;
-                            }
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Tangent attribute data format not supported, use vec4 float", fileName);
-                    }
-                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_texcoord) // TEXCOORD_n, vec2, float/u8n/u16n
-                    {
-                        // Support up to 2 texture coordinates attributes
-                        float *texcoordPtr = NULL;
-
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        if (attribute->type == cgltf_type_vec2)
-                        {
-                            if (attribute->component_type == cgltf_component_type_r_32f)  // vec2, float
-                            {
-                                // Init raylib mesh texcoords to copy glTF attribute data
-                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
-
-                                // Load 3 components of float data type into mesh.texcoords
-                                LOAD_ATTRIBUTE(attribute, 2, float, texcoordPtr)
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_8u) // vec2, u8n
-                            {
-                                // Init raylib mesh texcoords to copy glTF attribute data
-                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*2*sizeof(unsigned char));
-                                LOAD_ATTRIBUTE(attribute, 2, unsigned char, temp);
-
-                                // Convert data to raylib texcoord data type (float)
-                                for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/255.0f;
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_16u) // vec2, u16n
-                            {
-                                // Init raylib mesh texcoords to copy glTF attribute data
-                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*2*sizeof(unsigned short));
-                                LOAD_ATTRIBUTE(attribute, 2, unsigned short, temp);
-
-                                // Convert data to raylib texcoord data type (float)
-                                for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/65535.0f;
-
-                                RL_FREE(temp);
-                            }
-                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported", fileName);
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported, use vec2 float", fileName);
-
-                        int index = mesh->primitives[p].attributes[j].index;
-                        if (index == 0) model.meshes[meshIndex].texcoords = texcoordPtr;
-                        else if (index == 1) model.meshes[meshIndex].texcoords2 = texcoordPtr;
-                        else
-                        {
-                            TRACELOG(LOG_WARNING, "MODEL: [%s] No more than 2 texture coordinates attributes supported", fileName);
-                            if (texcoordPtr != NULL) RL_FREE(texcoordPtr);
-                        }
-                    }
-                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_color)    // COLOR_n, vec3/vec4, float/u8n/u16n
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        // WARNING: SPECS: All components of each COLOR_n accessor element MUST be clamped to [0.0, 1.0] range
-
-                        if (attribute->type == cgltf_type_vec3)  // RGB
-                        {
-                            if (attribute->component_type == cgltf_component_type_r_8u)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned char *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned char));
-                                LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp);
-
-                                // Convert data to raylib color data type (4 bytes)
-                                for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
-                                {
-                                    model.meshes[meshIndex].colors[c] = temp[k];
-                                    model.meshes[meshIndex].colors[c + 1] = temp[k + 1];
-                                    model.meshes[meshIndex].colors[c + 2] = temp[k + 2];
-                                    model.meshes[meshIndex].colors[c + 3] = 255;
-                                }
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_16u)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned short *temp = RL_MALLOC(attribute->count*3*sizeof(unsigned short));
-                                LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp);
-
-                                // Convert data to raylib color data type (4 bytes)
-                                for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
-                                {
-                                    model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[k]/65535.0f)*255.0f);
-                                    model.meshes[meshIndex].colors[c + 1] = (unsigned char)(((float)temp[k + 1]/65535.0f)*255.0f);
-                                    model.meshes[meshIndex].colors[c + 2] = (unsigned char)(((float)temp[k + 2]/65535.0f)*255.0f);
-                                    model.meshes[meshIndex].colors[c + 3] = 255;
-                                }
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_32f)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                float *temp = RL_MALLOC(attribute->count*3*sizeof(float));
-                                LOAD_ATTRIBUTE(attribute, 3, float, temp);
-
-                                // Convert data to raylib color data type (4 bytes)
-                                for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
-                                {
-                                    model.meshes[meshIndex].colors[c] = (unsigned char)(temp[k]*255.0f);
-                                    model.meshes[meshIndex].colors[c + 1] = (unsigned char)(temp[k + 1]*255.0f);
-                                    model.meshes[meshIndex].colors[c + 2] = (unsigned char)(temp[k + 2]*255.0f);
-                                    model.meshes[meshIndex].colors[c + 3] = 255;
-                                }
-
-                                RL_FREE(temp);
-                            }
-                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
-                        }
-                        else if (attribute->type == cgltf_type_vec4) // RGBA
-                        {
-                            if (attribute->component_type == cgltf_component_type_r_8u)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load 4 components of unsigned char data type into mesh.colors
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors)
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_16u)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short));
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
-
-                                // Convert data to raylib color data type (4 bytes)
-                                for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[c]/65535.0f)*255.0f);
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_32f)
-                            {
-                                // Init raylib mesh color to copy glTF attribute data
-                                model.meshes[meshIndex].colors = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                float *temp = RL_MALLOC(attribute->count*4*sizeof(float));
-                                LOAD_ATTRIBUTE(attribute, 4, float, temp);
-
-                                // Convert data to raylib color data type (4 bytes), we expect the color data normalized
-                                for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f);
-
-                                RL_FREE(temp);
-                            }
-                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
-                    }
-
-                    // NOTE: Attributes related to animations are processed separately
-                }
-
-                // Load primitive indices data (if provided)
-                if (mesh->primitives[p].indices != NULL)
-                {
-                    cgltf_accessor *attribute = mesh->primitives[p].indices;
-
-                    model.meshes[meshIndex].triangleCount = (int)attribute->count/3;
-
-                    if (attribute->component_type == cgltf_component_type_r_16u)
-                    {
-                        // Init raylib mesh indices to copy glTF attribute data
-                        model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
-
-                        // Load unsigned short data type into mesh.indices
-                        LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices)
-                    }
-                    else if (attribute->component_type == cgltf_component_type_r_8u)
-                    {
-                        // Init raylib mesh indices to copy glTF attribute data
-                        model.meshes[meshIndex].indices = RL_MALLOC(attribute->count * sizeof(unsigned short));
-                        LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short)
-
-                    }
-                    else if (attribute->component_type == cgltf_component_type_r_32u)
-                    {
-                        // Init raylib mesh indices to copy glTF attribute data
-                        model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short));
-                        LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short);
-
-                        TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName);
-                    }
-                    else
-                    {
-                        TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data format not supported, use u16", fileName);
-                    }
-                }
-                else model.meshes[meshIndex].triangleCount = model.meshes[meshIndex].vertexCount/3;    // Unindexed mesh
-
-                // Assign to the primitive mesh the corresponding material index
-                // NOTE: If no material defined, mesh uses the already assigned default material (index: 0)
-                for (unsigned int m = 0; m < data->materials_count; m++)
-                {
-                    // The primitive actually keeps the pointer to the corresponding material,
-                    // raylib instead assigns to the mesh the by its index, as loaded in model.materials array
-                    // To get the index, we check if material pointers match, and we assign the corresponding index,
-                    // skipping index 0, the default material
-                    if (&data->materials[m] == mesh->primitives[p].material)
-                    {
-                        model.meshMaterial[meshIndex] = m + 1;
-                        break;
-                    }
-                }
-
-                meshIndex++;       // Move to next mesh
-            }
-        }
-
-        // Load glTF meshes animation data
-        // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins
-        // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
-        //
-        // LIMITATIONS:
-        //  - Only supports 1 armature per file, and skips loading it if there are multiple armatures
-        //  - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file)
-        //  - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets)
-        //----------------------------------------------------------------------------------------------------
-        if (data->skins_count > 0)
-        {
-            cgltf_skin skin = data->skins[0];
-            model.bones = LoadBoneInfoGLTF(skin, &model.boneCount);
-            model.bindPose = RL_MALLOC(model.boneCount*sizeof(Transform));
-
-            for (int i = 0; i < model.boneCount; i++)
-            {
-                cgltf_node* node = skin.joints[i];
-                cgltf_float worldTransform[16];
-                cgltf_node_transform_world(node, worldTransform);
-                Matrix worldMatrix = {
-                    worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12],
-                    worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13],
-                    worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14],
-                    worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15]
-                };
-                MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale));
-            }
-        }
-        if (data->skins_count > 1)
-        {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count);
-        }
-
-        meshIndex = 0;
-        for (unsigned int i = 0; i < data->nodes_count; i++)
-        {
-            cgltf_node *node = &(data->nodes[i]);
-
-            cgltf_mesh *mesh = node->mesh;
-            if (!mesh)
-                continue;
-
-            for (unsigned int p = 0; p < mesh->primitives_count; p++)
-            {
-                // NOTE: We only support primitives defined by triangles
-                if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
-
-                for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
-                {
-                    // NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib
-
-                    if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16)
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        // NOTE: JOINTS_n can only be vec4 and u8/u16
-                        // SPECS: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview
-
-                        // WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char),
-                        // if data is provided in any other format, it is converted to supported format but
-                        // it could imply data loss (a warning message is issued in that case)
-
-                        if (attribute->type == cgltf_type_vec4)
-                        {
-                            if (attribute->component_type == cgltf_component_type_r_8u)
-                            {
-                                // Init raylib mesh boneIds to copy glTF attribute data
-                                model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
-
-                                // Load attribute: vec4, u8 (unsigned char)
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds)
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_16u)
-                            {
-                                // Init raylib mesh boneIds to copy glTF attribute data
-                                model.meshes[meshIndex].boneIds = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned short *temp = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short));
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
-
-                                // Convert data to raylib color data type (4 bytes)
-                                bool boneIdOverflowWarning = false;
-                                for (int b = 0; b < model.meshes[meshIndex].vertexCount*4; b++)
-                                {
-                                    if ((temp[b] > 255) && !boneIdOverflowWarning)
-                                    {
-                                        TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format (u16) overflow", fileName);
-                                        boneIdOverflowWarning = true;
-                                    }
-
-                                    // Despite the possible overflow, we convert data to unsigned char
-                                    model.meshes[meshIndex].boneIds[b] = (unsigned char)temp[b];
-                                }
-
-                                RL_FREE(temp);
-                            }
-                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName);
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName);
-                    }
-                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_weights)  // WEIGHTS_n (vec4, u8n/u16n/f32)
-                    {
-                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
-
-                        if (attribute->type == cgltf_type_vec4)
-                        {
-                            // TODO: Support component types: u8, u16?
-                            if (attribute->component_type == cgltf_component_type_r_8u)
-                            {
-                                // Init raylib mesh bone weight to copy glTF attribute data
-                                model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned char *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned char));
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp);
-
-                                // Convert data to raylib bone weight data type (4 bytes)
-                                for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/255.0f;
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_16u)
-                            {
-                                // Init raylib mesh bone weight to copy glTF attribute data
-                                model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
-
-                                // Load data into a temp buffer to be converted to raylib data type
-                                unsigned short *temp = RL_MALLOC(attribute->count*4*sizeof(unsigned short));
-                                LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
-
-                                // Convert data to raylib bone weight data type
-                                for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/65535.0f;
-
-                                RL_FREE(temp);
-                            }
-                            else if (attribute->component_type == cgltf_component_type_r_32f)
-                            {
-                                // Init raylib mesh bone weight to copy glTF attribute data
-                                model.meshes[meshIndex].boneWeights = RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
-
-                                // Load 4 components of float data type into mesh.boneWeights
-                                // for cgltf_attribute_type_weights we have:
-                                //   - data.meshes[0] (256 vertices)
-                                //   - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
-                                LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights)
-                            }
-                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName);
-                        }
-                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName);
-                    }
-                }
-
-                // Animated vertex data
-                model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
-                memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float));
-                model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
-                if (model.meshes[meshIndex].normals != NULL)
-                {
-                    memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
-                }
-
-                // Bone Transform Matrices
-                model.meshes[meshIndex].boneCount = model.boneCount;
-                model.meshes[meshIndex].boneMatrices = RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
-
-                for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
-                {
-                    model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
-                }
-
-                meshIndex++;       // Move to next mesh
-            }
-
-        }
-
-        // Free all cgltf loaded data
-        cgltf_free(data);
-    }
-    else TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
-
-    // WARNING: cgltf requires the file pointer available while reading data
-    UnloadFileData(fileData);
-
-    return model;
-}
-
-// Get interpolated pose for bone sampler at a specific time. Returns true on success
-static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_accessor *input, cgltf_accessor *output, float time, void *data)
-{
-    if (interpolationType >= cgltf_interpolation_type_max_enum) return false;
-
-    // Input and output should have the same count
-    float tstart = 0.0f;
-    float tend = 0.0f;
-    int keyframe = 0;       // Defaults to first pose
-
-    for (int i = 0; i < (int)input->count - 1; i++)
-    {
-        cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1);
-        if (!r1) return false;
-
-        cgltf_bool r2 = cgltf_accessor_read_float(input, i + 1, &tend, 1);
-        if (!r2) return false;
-
-        if ((tstart <= time) && (time < tend))
-        {
-            keyframe = i;
-            break;
-        }
-    }
-
-    // Constant animation, no need to interpolate
-    if (FloatEquals(tend, tstart)) return true;
-
-    float duration = fmaxf((tend - tstart), EPSILON);
-    float t = (time - tstart)/duration;
-    t = (t < 0.0f)? 0.0f : t;
-    t = (t > 1.0f)? 1.0f : t;
-
-    if (output->component_type != cgltf_component_type_r_32f) return false;
-
-    if (output->type == cgltf_type_vec3)
-    {
-        switch (interpolationType)
-        {
-            case cgltf_interpolation_type_step:
-            {
-                float tmp[3] = { 0.0f };
-                cgltf_accessor_read_float(output, keyframe, tmp, 3);
-                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
-                Vector3 *r = data;
-
-                *r = v1;
-            } break;
-            case cgltf_interpolation_type_linear:
-            {
-                float tmp[3] = { 0.0f };
-                cgltf_accessor_read_float(output, keyframe, tmp, 3);
-                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
-                cgltf_accessor_read_float(output, keyframe+1, tmp, 3);
-                Vector3 v2 = {tmp[0], tmp[1], tmp[2]};
-                Vector3 *r = data;
-
-                *r = Vector3Lerp(v1, v2, t);
-            } break;
-            case cgltf_interpolation_type_cubic_spline:
-            {
-                float tmp[3] = { 0.0f };
-                cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 3);
-                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
-                cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 3);
-                Vector3 tangent1 = {tmp[0], tmp[1], tmp[2]};
-                cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 3);
-                Vector3 v2 = {tmp[0], tmp[1], tmp[2]};
-                cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 3);
-                Vector3 tangent2 = {tmp[0], tmp[1], tmp[2]};
-                Vector3 *r = data;
-
-                *r = Vector3CubicHermite(v1, tangent1, v2, tangent2, t);
-            } break;
-            default: break;
-        }
-    }
-    else if (output->type == cgltf_type_vec4)
-    {
-        // Only v4 is for rotations, so we know it's a quaternion
-        switch (interpolationType)
-        {
-            case cgltf_interpolation_type_step:
-            {
-                float tmp[4] = { 0.0f };
-                cgltf_accessor_read_float(output, keyframe, tmp, 4);
-                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
-                Vector4 *r = data;
-
-                *r = v1;
-            } break;
-            case cgltf_interpolation_type_linear:
-            {
-                float tmp[4] = { 0.0f };
-                cgltf_accessor_read_float(output, keyframe, tmp, 4);
-                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
-                cgltf_accessor_read_float(output, keyframe+1, tmp, 4);
-                Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
-                Vector4 *r = data;
-
-                *r = QuaternionSlerp(v1, v2, t);
-            } break;
-            case cgltf_interpolation_type_cubic_spline:
-            {
-                float tmp[4] = { 0.0f };
-                cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4);
-                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
-                cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4);
-                Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f};
-                cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4);
-                Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
-                cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4);
-                Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f};
-                Vector4 *r = data;
-
-                v1 = QuaternionNormalize(v1);
-                v2 = QuaternionNormalize(v2);
-
-                if (Vector4DotProduct(v1, v2) < 0.0f)
-                {
-                    v2 = Vector4Negate(v2);
-                }
-
-                outTangent1 = Vector4Scale(outTangent1, duration);
-                inTangent2 = Vector4Scale(inTangent2, duration);
-
-                *r = QuaternionCubicHermiteSpline(v1, outTangent1, v2, inTangent2, t);
-            } break;
-            default: break;
-        }
-    }
-
-    return true;
-}
-
-#define GLTF_ANIMDELAY 17    // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
-
-static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount)
-{
-    // glTF file loading
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
-    ModelAnimation *animations = NULL;
-
-    // glTF data loading
-    cgltf_options options = { 0 };
-    options.file.read = LoadFileGLTFCallback;
-    options.file.release = ReleaseFileGLTFCallback;
-    cgltf_data *data = NULL;
-    cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
-
-    if (result != cgltf_result_success)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
-        *animCount = 0;
-        return NULL;
-    }
-
-    result = cgltf_load_buffers(&options, data, fileName);
-    if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load animation buffers", fileName);
-
-    if (result == cgltf_result_success)
-    {
-        if (data->skins_count > 0)
-        {
-            cgltf_skin skin = data->skins[0];
-            *animCount = (int)data->animations_count;
-            animations = RL_MALLOC(data->animations_count*sizeof(ModelAnimation));
-
-            for (unsigned int i = 0; i < data->animations_count; i++)
-            {
-                animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount);
-
-                cgltf_animation animData = data->animations[i];
-
-                struct Channels {
-                    cgltf_animation_channel *translate;
-                    cgltf_animation_channel *rotate;
-                    cgltf_animation_channel *scale;
-                    cgltf_interpolation_type interpolationType;
-                };
-
-                struct Channels *boneChannels = RL_CALLOC(animations[i].boneCount, sizeof(struct Channels));
-                float animDuration = 0.0f;
-
-                for (unsigned int j = 0; j < animData.channels_count; j++)
-                {
-                    cgltf_animation_channel channel = animData.channels[j];
-                    int boneIndex = -1;
-
-                    for (unsigned int k = 0; k < skin.joints_count; k++)
-                    {
-                        if (animData.channels[j].target_node == skin.joints[k])
-                        {
-                            boneIndex = k;
-                            break;
-                        }
-                    }
-
-                    if (boneIndex == -1)
-                    {
-                        // Animation channel for a node not in the armature
-                        continue;
-                    }
-
-                    boneChannels[boneIndex].interpolationType = animData.channels[j].sampler->interpolation;
-
-                    if (animData.channels[j].sampler->interpolation != cgltf_interpolation_type_max_enum)
-                    {
-                        if (channel.target_path == cgltf_animation_path_type_translation)
-                        {
-                            boneChannels[boneIndex].translate = &animData.channels[j];
-                        }
-                        else if (channel.target_path == cgltf_animation_path_type_rotation)
-                        {
-                            boneChannels[boneIndex].rotate = &animData.channels[j];
-                        }
-                        else if (channel.target_path == cgltf_animation_path_type_scale)
-                        {
-                            boneChannels[boneIndex].scale = &animData.channels[j];
-                        }
-                        else
-                        {
-                            TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, i);
-                        }
-                    }
-                    else TRACELOG(LOG_WARNING, "MODEL: [%s] Invalid interpolation curve encountered for GLTF animation.", fileName);
-
-                    float t = 0.0f;
-                    cgltf_bool r = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &t, 1);
-
-                    if (!r)
-                    {
-                        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load input time", fileName);
-                        continue;
-                    }
-
-                    animDuration = (t > animDuration)? t : animDuration;
-                }
-
-                if (animData.name != NULL)
-                {
-                    strncpy(animations[i].name, animData.name, sizeof(animations[i].name));
-                    animations[i].name[sizeof(animations[i].name) - 1] = '\0';
-                }
-
-                animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY) + 1;
-                animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
-
-                for (int j = 0; j < animations[i].frameCount; j++)
-                {
-                    animations[i].framePoses[j] = RL_MALLOC(animations[i].boneCount*sizeof(Transform));
-                    float time = ((float) j*GLTF_ANIMDELAY)/1000.0f;
-
-                    for (int k = 0; k < animations[i].boneCount; k++)
-                    {
-                        Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]};
-                        Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]};
-                        Vector3 scale = {skin.joints[k]->scale[0], skin.joints[k]->scale[1], skin.joints[k]->scale[2]};
-
-                        if (boneChannels[k].translate)
-                        {
-                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation))
-                            {
-                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, animations[i].bones[k].name);
-                            }
-                        }
-
-                        if (boneChannels[k].rotate)
-                        {
-                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].rotate->sampler->input, boneChannels[k].rotate->sampler->output, time, &rotation))
-                            {
-                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, animations[i].bones[k].name);
-                            }
-                        }
-
-                        if (boneChannels[k].scale)
-                        {
-                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].scale->sampler->input, boneChannels[k].scale->sampler->output, time, &scale))
-                            {
-                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, animations[i].bones[k].name);
-                            }
-                        }
-
-                        animations[i].framePoses[j][k] = (Transform){
-                            .translation = translation,
-                            .rotation = rotation,
-                            .scale = scale
-                        };
-                    }
-
-                    BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]);
-                }
-
-                TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, (animData.name != NULL)? animData.name : "NULL", animations[i].frameCount, animDuration);
-                RL_FREE(boneChannels);
-            }
-        }
-
-        if (data->skins_count > 1)
-        {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count);
-        }
-
-        cgltf_free(data);
-    }
-    UnloadFileData(fileData);
-    return animations;
-}
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_VOX)
-// Load VOX (MagicaVoxel) mesh data
-static Model LoadVOX(const char *fileName)
-{
-    Model model = { 0 };
-
-    int nbvertices = 0;
-    int meshescount = 0;
-
-    // Read vox file into buffer
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
-    if (fileData == 0)
-    {
-        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName);
-        return model;
-    }
-
-    // Read and build voxarray description
-    VoxArray3D voxarray = { 0 };
-    int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray);
-
-    if (ret != VOX_SUCCESS)
-    {
-        // Error
-        UnloadFileData(fileData);
-
-        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX data", fileName);
-        return model;
-    }
-    else
-    {
-        // Success: Compute meshes count
-        nbvertices = voxarray.vertices.used;
-        meshescount = 1 + (nbvertices/65536);
-
-        TRACELOG(LOG_INFO, "MODEL: [%s] VOX data loaded successfully : %i vertices/%i meshes", fileName, nbvertices, meshescount);
-    }
-
-    // Build models from meshes
-    model.transform = MatrixIdentity();
-
-    model.meshCount = meshescount;
-    model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
-
-    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
-
-    model.materialCount = 1;
-    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
-    model.materials[0] = LoadMaterialDefault();
-
-    // Init model meshes
-    int verticesRemain = voxarray.vertices.used;
-    int verticesMax = 65532; // 5461 voxels x 12 vertices per voxel -> 65532 (must be inf 65536)
-
-    // 6*4 = 12 vertices per voxel
-    Vector3 *pvertices = (Vector3 *)voxarray.vertices.array;
-    Vector3 *pnormals = (Vector3 *)voxarray.normals.array;
-    Color *pcolors = (Color *)voxarray.colors.array;
-
-    unsigned short *pindices = voxarray.indices.array;    // 5461*6*6 = 196596 indices max per mesh
-
-    int size = 0;
-
-    for (int i = 0; i < meshescount; i++)
-    {
-        Mesh *pmesh = &model.meshes[i];
-        memset(pmesh, 0, sizeof(Mesh));
-
-        // Copy vertices
-        pmesh->vertexCount = (int)fmin(verticesMax, verticesRemain);
-
-        size = pmesh->vertexCount*sizeof(float)*3;
-        pmesh->vertices = (float *)RL_MALLOC(size);
-        memcpy(pmesh->vertices, pvertices, size);
-
-        // Copy normals
-        pmesh->normals = (float *)RL_MALLOC(size);
-        memcpy(pmesh->normals, pnormals, size);
-
-        // Copy indices
-        size = voxarray.indices.used*sizeof(unsigned short);
-        pmesh->indices = (unsigned short *)RL_MALLOC(size);
-        memcpy(pmesh->indices, pindices, size);
-
-        pmesh->triangleCount = (pmesh->vertexCount/4)*2;
-
-        // Copy colors
-        size = pmesh->vertexCount*sizeof(Color);
-        pmesh->colors = RL_MALLOC(size);
-        memcpy(pmesh->colors, pcolors, size);
-
-        // First material index
-        model.meshMaterial[i] = 0;
-
-        verticesRemain -= verticesMax;
-        pvertices += verticesMax;
-        pnormals += verticesMax;
-        pcolors += verticesMax;
-    }
-
-    // Free buffers
-    Vox_FreeArrays(&voxarray);
-    UnloadFileData(fileData);
-
-    return model;
-}
-#endif
-
-#if defined(SUPPORT_FILEFORMAT_M3D)
-// Hook LoadFileData()/UnloadFileData() calls to M3D loaders
-unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); }
-void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); }
-
-// Load M3D mesh data
-static Model LoadM3D(const char *fileName)
-{
-    Model model = { 0 };
-
-    m3d_t *m3d = NULL;
-    m3dp_t *prop = NULL;
-    int i, j, k, l, n, mi = -2, vcolor = 0;
-
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
-    if (fileData != NULL)
-    {
-        m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
-
-        if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
-        {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2);
-            if (m3d) m3d_free(m3d);
-            UnloadFileData(fileData);
-            return model;
-        }
-        else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial);
-
-        // no face? this is probably just a material library
-        if (!m3d->numface)
-        {
-            m3d_free(m3d);
-            UnloadFileData(fileData);
-            return model;
-        }
-
-        if (m3d->nummaterial > 0)
-        {
-            model.meshCount = model.materialCount = m3d->nummaterial;
-            TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount);
-        }
-        else
-        {
-            model.meshCount = 1; model.materialCount = 0;
-            TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
-        }
-
-        // We always need a default material, so we add +1
-        model.materialCount++;
-
-        // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise
-        // WARNING: Sorting is not needed, valid M3D model files should already be sorted
-        // Just keeping the sorting function for reference (Check PR #3363 #3385)
-        /*
-        for (i = 1; i < m3d->numface; i++)
-        {
-            if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue;
-
-            // face[i-1] > face[i].  slide face[i] lower
-            m3df_t slider = m3d->face[i];
-            j = i-1;
-
-            do
-            {   // face[j] > slider, face[j+1] is svailable vacant gap
-                m3d->face[j+1] = m3d->face[j];
-                j = j-1;
-            }
-            while (j >= 0 && m3d->face[j].materialid > slider.materialid);
-
-            m3d->face[j+1] = slider;
-        }
-        */
-
-        model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
-        model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
-        model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material));
-
-        // Map no material to index 0 with default shader, everything else materialid + 1
-        model.materials[0] = LoadMaterialDefault();
-
-        for (i = l = 0, k = -1; i < (int)m3d->numface; i++, l++)
-        {
-            // Materials are grouped together
-            if (mi != m3d->face[i].materialid)
-            {
-                // there should be only one material switch per material kind, but be bulletproof for non-optimal model files
-                if (k + 1 >= model.meshCount)
-                {
-                    model.meshCount++;
-                    model.meshes = (Mesh *)RL_REALLOC(model.meshes, model.meshCount*sizeof(Mesh));
-                    memset(&model.meshes[model.meshCount - 1], 0, sizeof(Mesh));
-                    model.meshMaterial = (int *)RL_REALLOC(model.meshMaterial, model.meshCount*sizeof(int));
-                }
-
-                k++;
-                mi = m3d->face[i].materialid;
-
-                // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch
-                // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors
-                for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++)
-                {
-                    if (!m3d->vertex[m3d->face[j].vertex[0]].color ||
-                        !m3d->vertex[m3d->face[j].vertex[1]].color ||
-                        !m3d->vertex[m3d->face[j].vertex[2]].color) vcolor = 1;
-                }
-
-                model.meshes[k].vertexCount = l*3;
-                model.meshes[k].triangleCount = l;
-                model.meshes[k].vertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
-                model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
-                model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
-
-                // If no map is provided, or we have colors defined, we allocate storage for vertex colors
-                // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
-                if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
-
-                // If no map is provided and we allocated vertex colors, set them to white
-                if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL))
-                {
-                    for (int c = 0; c < model.meshes[k].vertexCount*4; c++) model.meshes[k].colors[c] = 255;
-                }
-
-                if (m3d->numbone && m3d->numskin)
-                {
-                    model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
-                    model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float));
-                    model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
-                    model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
-                }
-
-                model.meshMaterial[k] = mi + 1;
-                l = 0;
-            }
-
-            // Process meshes per material, add triangles
-            model.meshes[k].vertices[l*9 + 0] = m3d->vertex[m3d->face[i].vertex[0]].x*m3d->scale;
-            model.meshes[k].vertices[l*9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y*m3d->scale;
-            model.meshes[k].vertices[l*9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z*m3d->scale;
-            model.meshes[k].vertices[l*9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x*m3d->scale;
-            model.meshes[k].vertices[l*9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y*m3d->scale;
-            model.meshes[k].vertices[l*9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z*m3d->scale;
-            model.meshes[k].vertices[l*9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x*m3d->scale;
-            model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale;
-            model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale;
-
-            // Without vertex color (full transparency), we use the default color
-            if (model.meshes[k].colors != NULL)
-            {
-                if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000)
-                    memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4);
-                if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xFF000000)
-                    memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4);
-                if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xFF000000)
-                    memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4);
-            }
-
-            if (m3d->face[i].texcoord[0] != M3D_UNDEF)
-            {
-                model.meshes[k].texcoords[l*6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u;
-                model.meshes[k].texcoords[l*6 + 1] = 1.0f - m3d->tmap[m3d->face[i].texcoord[0]].v;
-                model.meshes[k].texcoords[l*6 + 2] = m3d->tmap[m3d->face[i].texcoord[1]].u;
-                model.meshes[k].texcoords[l*6 + 3] = 1.0f - m3d->tmap[m3d->face[i].texcoord[1]].v;
-                model.meshes[k].texcoords[l*6 + 4] = m3d->tmap[m3d->face[i].texcoord[2]].u;
-                model.meshes[k].texcoords[l*6 + 5] = 1.0f - m3d->tmap[m3d->face[i].texcoord[2]].v;
-            }
-
-            if (m3d->face[i].normal[0] != M3D_UNDEF)
-            {
-                model.meshes[k].normals[l*9 + 0] = m3d->vertex[m3d->face[i].normal[0]].x;
-                model.meshes[k].normals[l*9 + 1] = m3d->vertex[m3d->face[i].normal[0]].y;
-                model.meshes[k].normals[l*9 + 2] = m3d->vertex[m3d->face[i].normal[0]].z;
-                model.meshes[k].normals[l*9 + 3] = m3d->vertex[m3d->face[i].normal[1]].x;
-                model.meshes[k].normals[l*9 + 4] = m3d->vertex[m3d->face[i].normal[1]].y;
-                model.meshes[k].normals[l*9 + 5] = m3d->vertex[m3d->face[i].normal[1]].z;
-                model.meshes[k].normals[l*9 + 6] = m3d->vertex[m3d->face[i].normal[2]].x;
-                model.meshes[k].normals[l*9 + 7] = m3d->vertex[m3d->face[i].normal[2]].y;
-                model.meshes[k].normals[l*9 + 8] = m3d->vertex[m3d->face[i].normal[2]].z;
-            }
-
-            // Add skin (vertex / bone weight pairs)
-            if (m3d->numbone && m3d->numskin)
-            {
-                for (n = 0; n < 3; n++)
-                {
-                    int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
-
-                    // Check if there is a skin for this mesh, should be, just failsafe
-                    if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin))
-                    {
-                        for (j = 0; j < 4; j++)
-                        {
-                            model.meshes[k].boneIds[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j];
-                            model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j];
-                        }
-                    }
-                    else
-                    {
-                        // raylib does not handle boneless meshes with skeletal animations, so
-                        // we put all vertices without a bone into a special "no bone" bone
-                        model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone;
-                        model.meshes[k].boneWeights[l*12 + n*4] = 1.0f;
-                    }
-                }
-            }
-        }
-
-        // Load materials
-        for (i = 0; i < (int)m3d->nummaterial; i++)
-        {
-            model.materials[i + 1] = LoadMaterialDefault();
-
-            for (j = 0; j < m3d->material[i].numprop; j++)
-            {
-                prop = &m3d->material[i].prop[j];
-
-                switch (prop->type)
-                {
-                    case m3dp_Kd:
-                    {
-                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].color, &prop->value.color, 4);
-                        model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
-                    } break;
-                    case m3dp_Ks:
-                    {
-                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4);
-                    } break;
-                    case m3dp_Ns:
-                    {
-                        model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = prop->value.fnum;
-                    } break;
-                    case m3dp_Ke:
-                    {
-                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].color, &prop->value.color, 4);
-                        model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].value = 0.0f;
-                    } break;
-                    case m3dp_Pm:
-                    {
-                        model.materials[i + 1].maps[MATERIAL_MAP_METALNESS].value = prop->value.fnum;
-                    } break;
-                    case m3dp_Pr:
-                    {
-                        model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].value = prop->value.fnum;
-                    } break;
-                    case m3dp_Ps:
-                    {
-                        model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].color = WHITE;
-                        model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].value = prop->value.fnum;
-                    } break;
-                    default:
-                    {
-                        if (prop->type >= 128)
-                        {
-                            Image image = { 0 };
-                            image.data = m3d->texture[prop->value.textureid].d;
-                            image.width = m3d->texture[prop->value.textureid].w;
-                            image.height = m3d->texture[prop->value.textureid].h;
-                            image.mipmaps = 1;
-                            image.format = (m3d->texture[prop->value.textureid].f == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 :
-                                           ((m3d->texture[prop->value.textureid].f == 3)? PIXELFORMAT_UNCOMPRESSED_R8G8B8 :
-                                           ((m3d->texture[prop->value.textureid].f == 2)? PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA : PIXELFORMAT_UNCOMPRESSED_GRAYSCALE));
-
-                            switch (prop->type)
-                            {
-                                case m3dp_map_Kd: model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTextureFromImage(image); break;
-                                case m3dp_map_Ks: model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].texture = LoadTextureFromImage(image); break;
-                                case m3dp_map_Ke: model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(image); break;
-                                case m3dp_map_Km: model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(image); break;
-                                case m3dp_map_Ka: model.materials[i + 1].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(image); break;
-                                case m3dp_map_Pm: model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(image); break;
-                                default: break;
-                            }
-                        }
-                    } break;
-                }
-            }
-        }
-
-        // Load bones
-        if (m3d->numbone)
-        {
-            model.boneCount = m3d->numbone + 1;
-            model.bones = RL_CALLOC(model.boneCount, sizeof(BoneInfo));
-            model.bindPose = RL_CALLOC(model.boneCount, sizeof(Transform));
-
-            for (i = 0; i < (int)m3d->numbone; i++)
-            {
-                model.bones[i].parent = m3d->bone[i].parent;
-                strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name));
-                model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
-                model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
-                model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
-                model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x;
-                model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y;
-                model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
-                model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
-
-                // TODO: If the orientation quaternion is not normalized, then that's encoding scaling
-                model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation);
-                model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
-
-                // Child bones are stored in parent bone relative space, convert that into model space
-                if (model.bones[i].parent >= 0)
-                {
-                    model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation);
-                    model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation);
-                    model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation);
-                    model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
-                }
-            }
-
-            // Add a special "no bone" bone
-            model.bones[i].parent = -1;
-            strcpy(model.bones[i].name, "NO BONE");
-            model.bindPose[i].translation.x = 0.0f;
-            model.bindPose[i].translation.y = 0.0f;
-            model.bindPose[i].translation.z = 0.0f;
-            model.bindPose[i].rotation.x = 0.0f;
-            model.bindPose[i].rotation.y = 0.0f;
-            model.bindPose[i].rotation.z = 0.0f;
-            model.bindPose[i].rotation.w = 1.0f;
-            model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
-        }
-
-        // Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
-        // called, but not before, however DrawMesh uses these if they exist (so not good if they are left empty)
-        if (m3d->numbone && m3d->numskin)
-        {
-            for (i = 0; i < model.meshCount; i++)
-            {
-                memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
-                memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
-
-                model.meshes[i].boneCount = model.boneCount;
-                model.meshes[i].boneMatrices = RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
-                for (j = 0; j < model.meshes[i].boneCount; j++)
-                {
-                    model.meshes[i].boneMatrices[j] = MatrixIdentity();
-                }
-            }
-        }
-
-        m3d_free(m3d);
-        UnloadFileData(fileData);
-    }
-
-    return model;
-}
-
-#define M3D_ANIMDELAY 17    // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
-
-// Load M3D animation data
-static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount)
-{
-    ModelAnimation *animations = NULL;
-
-    m3d_t *m3d = NULL;
-    int i = 0, j = 0;
-    *animCount = 0;
-
-    int dataSize = 0;
-    unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
-    if (fileData != NULL)
-    {
-        m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
-
-        if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
-        {
-            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2);
-            UnloadFileData(fileData);
-            return NULL;
-        }
-        else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName,
-            m3d->numaction, m3d->numbone, m3d->numskin);
-
-        // No animation or bone+skin?
-        if (!m3d->numaction || !m3d->numbone || !m3d->numskin)
-        {
-            m3d_free(m3d);
-            UnloadFileData(fileData);
-            return NULL;
-        }
-
-        animations = RL_MALLOC(m3d->numaction*sizeof(ModelAnimation));
-        *animCount = m3d->numaction;
-
-        for (unsigned int a = 0; a < m3d->numaction; a++)
-        {
-            animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
-            animations[a].boneCount = m3d->numbone + 1;
-            animations[a].bones = RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
-            animations[a].framePoses = RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
-            strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name));
-            animations[a].name[sizeof(animations[a].name) - 1] = '\0';
-
-            TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
-
-            for (i = 0; i < (int)m3d->numbone; i++)
-            {
-                animations[a].bones[i].parent = m3d->bone[i].parent;
-                strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name));
-            }
-
-            // A special, never transformed "no bone" bone, used for boneless vertices
-            animations[a].bones[i].parent = -1;
-            strcpy(animations[a].bones[i].name, "NO BONE");
-
-            // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
-            // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
-            for (i = 0; i < animations[a].frameCount; i++)
-            {
-                animations[a].framePoses[i] = RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
+*           Selected desired fileformats to be supported for model data loading
+*
+*       #define SUPPORT_MESH_GENERATION
+*           Support procedural mesh generation functions, uses external par_shapes.h library
+*           NOTE: Some generated meshes DO NOT include generated texture coordinates
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"         // Declares module functions
+
+#include "config.h"         // Defines module configuration flags
+
+#if defined(SUPPORT_MODULE_RMODELS)
+
+#include "rlgl.h"           // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
+#include "raymath.h"        // Required for: Vector3, Quaternion and Matrix functionality
+
+#include <stdio.h>          // Required for: sprintf()
+#include <stdlib.h>         // Required for: malloc(), calloc(), free()
+#include <string.h>         // Required for: memcmp(), strlen(), strncpy()
+#include <math.h>           // Required for: sinf(), cosf(), sqrtf(), fabsf()
+
+#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+    #define TINYOBJ_MALLOC RL_MALLOC
+    #define TINYOBJ_CALLOC RL_CALLOC
+    #define TINYOBJ_REALLOC RL_REALLOC
+    #define TINYOBJ_FREE RL_FREE
+
+    #define TINYOBJ_LOADER_C_IMPLEMENTATION
+    #include "external/tinyobj_loader_c.h"      // OBJ/MTL file formats loading
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+    #define CGLTF_MALLOC RL_MALLOC
+    #define CGLTF_FREE RL_FREE
+
+    #define CGLTF_IMPLEMENTATION
+    #include "external/cgltf.h"         // glTF file format loading
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_VOX)
+    #define VOX_MALLOC RL_MALLOC
+    #define VOX_CALLOC RL_CALLOC
+    #define VOX_REALLOC RL_REALLOC
+    #define VOX_FREE RL_FREE
+
+    #define VOX_LOADER_IMPLEMENTATION
+    #include "external/vox_loader.h"    // VOX file format loading (MagikaVoxel)
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_M3D)
+    #define M3D_MALLOC RL_MALLOC
+    #define M3D_REALLOC RL_REALLOC
+    #define M3D_FREE RL_FREE
+
+    #define M3D_IMPLEMENTATION
+    #include "external/m3d.h"           // Model3D file format loading
+#endif
+
+#if defined(SUPPORT_MESH_GENERATION)
+    #define PAR_MALLOC(T, N) ((T *)RL_MALLOC(N*sizeof(T)))
+    #define PAR_CALLOC(T, N) ((T *)RL_CALLOC(N*sizeof(T), 1))
+    #define PAR_REALLOC(T, BUF, N) ((T *)RL_REALLOC(BUF, sizeof(T)*(N)))
+    #define PAR_FREE RL_FREE
+
+    #if defined(_MSC_VER)           // Disable some MSVC warning
+        #pragma warning(push)
+        #pragma warning(disable : 4244)
+        #pragma warning(disable : 4305)
+    #endif
+
+    #define PAR_SHAPES_IMPLEMENTATION
+    #include "external/par_shapes.h"    // Shapes 3d parametric generation
+
+    #if defined(_MSC_VER)
+        #pragma warning(pop)        // Disable MSVC warning suppression
+    #endif
+#endif
+
+#if defined(_WIN32)
+    #include <direct.h>     // Required for: _chdir() [Used in LoadOBJ()]
+    #define CHDIR _chdir
+#else
+    #include <unistd.h>     // Required for: chdir() (POSIX) [Used in LoadOBJ()]
+    #define CHDIR chdir
+#endif
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#ifndef MAX_MATERIAL_MAPS
+    #define MAX_MATERIAL_MAPS       12      // Maximum number of maps supported
+#endif
+#ifndef MAX_MESH_VERTEX_BUFFERS
+    #define MAX_MESH_VERTEX_BUFFERS  9      // Maximum vertex buffers (VBO) per mesh
+#endif
+#ifndef MAX_FILEPATH_LENGTH
+    #define MAX_FILEPATH_LENGTH   4096      // Maximum length for filepaths (Linux PATH_MAX default value)
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+// ...
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+#if defined(SUPPORT_FILEFORMAT_OBJ)
+static Model LoadOBJ(const char *fileName);     // Load OBJ mesh data
+#endif
+#if defined(SUPPORT_FILEFORMAT_IQM)
+static Model LoadIQM(const char *fileName);     // Load IQM mesh data
+static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount);   // Load IQM animation data
+#endif
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+static Model LoadGLTF(const char *fileName);    // Load GLTF mesh data
+static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount);  // Load GLTF animation data
+#endif
+#if defined(SUPPORT_FILEFORMAT_VOX)
+static Model LoadVOX(const char *filename);     // Load VOX mesh data
+#endif
+#if defined(SUPPORT_FILEFORMAT_M3D)
+static Model LoadM3D(const char *filename);     // Load M3D mesh data
+static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount);   // Load M3D animation data
+#endif
+#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount);  // Process obj materials
+#endif
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Draw a line in 3D world space
+void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color)
+{
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+        rlVertex3f(startPos.x, startPos.y, startPos.z);
+        rlVertex3f(endPos.x, endPos.y, endPos.z);
+    rlEnd();
+}
+
+// Draw a point in 3D space, actually a small line
+// WARNING: OpenGL ES 2.0 does not support point mode drawing
+void DrawPoint3D(Vector3 position, Color color)
+{
+    rlPushMatrix();
+        rlTranslatef(position.x, position.y, position.z);
+        rlBegin(RL_LINES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+            rlVertex3f(0.0f, 0.0f, 0.0f);
+            rlVertex3f(0.0f, 0.0f, 0.1f);
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a circle in 3D world space
+void DrawCircle3D(Vector3 center, float radius, Vector3 rotationAxis, float rotationAngle, Color color)
+{
+    rlPushMatrix();
+        rlTranslatef(center.x, center.y, center.z);
+        rlRotatef(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
+
+        rlBegin(RL_LINES);
+            for (int i = 0; i < 360; i += 10)
+            {
+                rlColor4ub(color.r, color.g, color.b, color.a);
+
+                rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f);
+                rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f);
+            }
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a color-filled triangle (vertex in counter-clockwise order!)
+void DrawTriangle3D(Vector3 v1, Vector3 v2, Vector3 v3, Color color)
+{
+    rlBegin(RL_TRIANGLES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+        rlVertex3f(v1.x, v1.y, v1.z);
+        rlVertex3f(v2.x, v2.y, v2.z);
+        rlVertex3f(v3.x, v3.y, v3.z);
+    rlEnd();
+}
+
+// Draw a triangle strip defined by points
+void DrawTriangleStrip3D(const Vector3 *points, int pointCount, Color color)
+{
+    if (pointCount < 3) return; // Security check
+
+    rlBegin(RL_TRIANGLES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        for (int i = 2; i < pointCount; i++)
+        {
+            if ((i%2) == 0)
+            {
+                rlVertex3f(points[i].x, points[i].y, points[i].z);
+                rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
+                rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
+            }
+            else
+            {
+                rlVertex3f(points[i].x, points[i].y, points[i].z);
+                rlVertex3f(points[i - 1].x, points[i - 1].y, points[i - 1].z);
+                rlVertex3f(points[i - 2].x, points[i - 2].y, points[i - 2].z);
+            }
+        }
+    rlEnd();
+}
+
+// Draw cube
+// NOTE: Cube position is the center position
+void DrawCube(Vector3 position, float width, float height, float length, Color color)
+{
+    float x = 0.0f;
+    float y = 0.0f;
+    float z = 0.0f;
+
+    rlPushMatrix();
+        // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate)
+        rlTranslatef(position.x, position.y, position.z);
+        //rlRotatef(45, 0, 1, 0);
+        //rlScalef(1.0f, 1.0f, 1.0f);   // NOTE: Vertices are directly scaled on definition
+
+        rlBegin(RL_TRIANGLES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            // Front face
+            rlNormal3f(0.0f, 0.0f, 1.0f);
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
+
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Right
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
+
+            // Back face
+            rlNormal3f(0.0f, 0.0f, -1.0f);
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Left
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
+
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
+
+            // Top face
+            rlNormal3f(0.0f, 1.0f, 0.0f);
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Bottom Left
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right
+
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Left
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Bottom Right
+
+            // Bottom face
+            rlNormal3f(0.0f, -1.0f, 0.0f);
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Left
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
+
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top Right
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Right
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top Left
+
+            // Right face
+            rlNormal3f(1.0f, 0.0f, 0.0f);
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top Right
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left
+
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom Left
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom Right
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top Left
+
+            // Left face
+            rlNormal3f(-1.0f, 0.0f, 0.0f);
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top Right
+
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom Left
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top Left
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom Right
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw cube (Vector version)
+void DrawCubeV(Vector3 position, Vector3 size, Color color)
+{
+    DrawCube(position, size.x, size.y, size.z, color);
+}
+
+// Draw cube wires
+void DrawCubeWires(Vector3 position, float width, float height, float length, Color color)
+{
+    float x = 0.0f;
+    float y = 0.0f;
+    float z = 0.0f;
+
+    rlPushMatrix();
+        rlTranslatef(position.x, position.y, position.z);
+
+        rlBegin(RL_LINES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            // Front face
+            //------------------------------------------------------------------
+            // Bottom line
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom left
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom right
+
+            // Left line
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Bottom right
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right
+
+            // Top line
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left
+
+            // Right line
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Bottom left
+
+            // Back face
+            //------------------------------------------------------------------
+            // Bottom line
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom left
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom right
+
+            // Left line
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Bottom right
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right
+
+            // Top line
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left
+
+            // Right line
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Bottom left
+
+            // Top face
+            //------------------------------------------------------------------
+            // Left line
+            rlVertex3f(x - width/2, y + height/2, z + length/2);  // Top left front
+            rlVertex3f(x - width/2, y + height/2, z - length/2);  // Top left back
+
+            // Right line
+            rlVertex3f(x + width/2, y + height/2, z + length/2);  // Top right front
+            rlVertex3f(x + width/2, y + height/2, z - length/2);  // Top right back
+
+            // Bottom face
+            //------------------------------------------------------------------
+            // Left line
+            rlVertex3f(x - width/2, y - height/2, z + length/2);  // Top left front
+            rlVertex3f(x - width/2, y - height/2, z - length/2);  // Top left back
+
+            // Right line
+            rlVertex3f(x + width/2, y - height/2, z + length/2);  // Top right front
+            rlVertex3f(x + width/2, y - height/2, z - length/2);  // Top right back
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw cube wires (vector version)
+void DrawCubeWiresV(Vector3 position, Vector3 size, Color color)
+{
+    DrawCubeWires(position, size.x, size.y, size.z, color);
+}
+
+// Draw sphere
+void DrawSphere(Vector3 centerPos, float radius, Color color)
+{
+    DrawSphereEx(centerPos, radius, 16, 16, color);
+}
+
+// Draw sphere with extended parameters
+void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color color)
+{
+#if 0
+    // Basic implementation, do not use it!
+    // For a sphere with 16 rings and 16 slices it requires 8640 cos()/sin() function calls!
+    // New optimized version below only requires 4 cos()/sin() calls
+
+    rlPushMatrix();
+        // NOTE: Transformation is applied in inverse order (scale -> translate)
+        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
+        rlScalef(radius, radius, radius);
+
+        rlBegin(RL_TRIANGLES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            for (int i = 0; i < (rings + 2); i++)
+            {
+                for (int j = 0; j < slices; j++)
+                {
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
+
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
+                }
+            }
+        rlEnd();
+    rlPopMatrix();
+#endif
+
+    rlPushMatrix();
+        // NOTE: Transformation is applied in inverse order (scale -> translate)
+        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
+        rlScalef(radius, radius, radius);
+
+        rlBegin(RL_TRIANGLES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            float ringangle = DEG2RAD*(180.0f/(rings + 1)); // Angle between latitudinal parallels
+            float sliceangle = DEG2RAD*(360.0f/slices); // Angle between longitudinal meridians
+
+            float cosring = cosf(ringangle);
+            float sinring = sinf(ringangle);
+            float cosslice = cosf(sliceangle);
+            float sinslice = sinf(sliceangle);
+
+            Vector3 vertices[4] = { 0 }; // Required to store face vertices
+            vertices[2] = (Vector3){ 0, 1, 0 };
+            vertices[3] = (Vector3){ sinring, cosring, 0 };
+
+            for (int i = 0; i < rings + 1; i++)
+            {
+                for (int j = 0; j < slices; j++)
+                {
+                    vertices[0] = vertices[2]; // Rotate around y axis to set up vertices for next face
+                    vertices[1] = vertices[3];
+                    vertices[2] = (Vector3){ cosslice*vertices[2].x - sinslice*vertices[2].z, vertices[2].y, sinslice*vertices[2].x + cosslice*vertices[2].z }; // Rotation matrix around y axis
+                    vertices[3] = (Vector3){ cosslice*vertices[3].x - sinslice*vertices[3].z, vertices[3].y, sinslice*vertices[3].x + cosslice*vertices[3].z };
+
+                    rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z);
+                    rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
+                    rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z);
+                    rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
+                    rlNormal3f(vertices[1].x, vertices[1].y, vertices[1].z);
+                    rlVertex3f(vertices[1].x, vertices[1].y, vertices[1].z);
+
+                    rlNormal3f(vertices[0].x, vertices[0].y, vertices[0].z);
+                    rlVertex3f(vertices[0].x, vertices[0].y, vertices[0].z);
+                    rlNormal3f(vertices[2].x, vertices[2].y, vertices[2].z);
+                    rlVertex3f(vertices[2].x, vertices[2].y, vertices[2].z);
+                    rlNormal3f(vertices[3].x, vertices[3].y, vertices[3].z);
+                    rlVertex3f(vertices[3].x, vertices[3].y, vertices[3].z);
+                }
+
+                vertices[2] = vertices[3]; // Rotate around z axis to set up  starting vertices for next ring
+                vertices[3] = (Vector3){ cosring*vertices[3].x + sinring*vertices[3].y, -sinring*vertices[3].x + cosring*vertices[3].y, vertices[3].z }; // Rotation matrix around z axis
+            }
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw sphere wires
+void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Color color)
+{
+    rlPushMatrix();
+        // NOTE: Transformation is applied in inverse order (scale -> translate)
+        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
+        rlScalef(radius, radius, radius);
+
+        rlBegin(RL_LINES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            for (int i = 0; i < (rings + 2); i++)
+            {
+                for (int j = 0; j < slices; j++)
+                {
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
+
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*(j + 1)/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*(j + 1)/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
+
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1))),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*(i + 1)))*cosf(DEG2RAD*(360.0f*j/slices)));
+                    rlVertex3f(cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*sinf(DEG2RAD*(360.0f*j/slices)),
+                               sinf(DEG2RAD*(270 + (180.0f/(rings + 1))*i)),
+                               cosf(DEG2RAD*(270 + (180.0f/(rings + 1))*i))*cosf(DEG2RAD*(360.0f*j/slices)));
+                }
+            }
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a cylinder
+// NOTE: It could be also used for pyramid and cone
+void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
+{
+    if (sides < 3) sides = 3;
+
+    const float angleStep = 360.0f/sides;
+
+    rlPushMatrix();
+        rlTranslatef(position.x, position.y, position.z);
+
+        rlBegin(RL_TRIANGLES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            if (radiusTop > 0)
+            {
+                // Draw Body -------------------------------------------------------------------------------------
+                for (int i = 0; i < sides; i++)
+                {
+                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left
+                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom); //Bottom Right
+                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right
+
+                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop); //Top Left
+                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom); //Bottom Left
+                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop); //Top Right
+                }
+
+                // Draw Cap --------------------------------------------------------------------------------------
+                for (int i = 0; i < sides; i++)
+                {
+                    rlVertex3f(0, height, 0);
+                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
+                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
+                }
+            }
+            else
+            {
+                // Draw Cone -------------------------------------------------------------------------------------
+                for (int i = 0; i < sides; i++)
+                {
+                    rlVertex3f(0, height, 0);
+                    rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
+                    rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
+                }
+            }
+
+            // Draw Base -----------------------------------------------------------------------------------------
+            for (int i = 0; i < sides; i++)
+            {
+                rlVertex3f(0, 0, 0);
+                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
+                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
+            }
+
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a cylinder with base at startPos and top at endPos
+// NOTE: It could be also used for pyramid and cone
+void DrawCylinderEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color)
+{
+    if (sides < 3) sides = 3;
+
+    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
+    if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check
+
+    // Construct a basis of the base and the top face:
+    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
+    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
+
+    float baseAngle = (2.0f*PI)/sides;
+
+    rlBegin(RL_TRIANGLES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        for (int i = 0; i < sides; i++)
+        {
+            // Compute the four vertices
+            float s1 = sinf(baseAngle*(i + 0))*startRadius;
+            float c1 = cosf(baseAngle*(i + 0))*startRadius;
+            Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z };
+            float s2 = sinf(baseAngle*(i + 1))*startRadius;
+            float c2 = cosf(baseAngle*(i + 1))*startRadius;
+            Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z };
+            float s3 = sinf(baseAngle*(i + 0))*endRadius;
+            float c3 = cosf(baseAngle*(i + 0))*endRadius;
+            Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z };
+            float s4 = sinf(baseAngle*(i + 1))*endRadius;
+            float c4 = cosf(baseAngle*(i + 1))*endRadius;
+            Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z };
+
+            if (startRadius > 0)
+            {
+                rlVertex3f(startPos.x, startPos.y, startPos.z); // |
+                rlVertex3f(w2.x, w2.y, w2.z);                   // T0
+                rlVertex3f(w1.x, w1.y, w1.z);                   // |
+            }
+                                                                //          w2 x.-----------x startPos
+            rlVertex3f(w1.x, w1.y, w1.z);                       // |           |\'.  T0    /
+            rlVertex3f(w2.x, w2.y, w2.z);                       // T1          | \ '.     /
+            rlVertex3f(w3.x, w3.y, w3.z);                       // |           |T \  '.  /
+                                                                //             | 2 \ T 'x w1
+            rlVertex3f(w2.x, w2.y, w2.z);                       // |        w4 x.---\-1-|---x endPos
+            rlVertex3f(w4.x, w4.y, w4.z);                       // T2            '.  \  |T3/
+            rlVertex3f(w3.x, w3.y, w3.z);                       // |               '. \ | /
+                                                                //                   '.\|/
+            if (endRadius > 0)                                  //                     'x w3
+            {
+                rlVertex3f(endPos.x, endPos.y, endPos.z);       // |
+                rlVertex3f(w3.x, w3.y, w3.z);                   // T3
+                rlVertex3f(w4.x, w4.y, w4.z);                   // |
+            }                                                   //
+        }
+    rlEnd();
+}
+
+// Draw a wired cylinder
+// NOTE: It could be also used for pyramid and cone
+void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, float height, int sides, Color color)
+{
+    if (sides < 3) sides = 3;
+
+    const float angleStep = 360.0f/sides;
+
+    rlPushMatrix();
+        rlTranslatef(position.x, position.y, position.z);
+
+        rlBegin(RL_LINES);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+
+            for (int i = 0; i < sides; i++)
+            {
+                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
+                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
+
+                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusBottom, 0, cosf(DEG2RAD*(i+1)*angleStep)*radiusBottom);
+                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
+
+                rlVertex3f(sinf(DEG2RAD*(i+1)*angleStep)*radiusTop, height, cosf(DEG2RAD*(i+1)*angleStep)*radiusTop);
+                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
+
+                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusTop, height, cosf(DEG2RAD*i*angleStep)*radiusTop);
+                rlVertex3f(sinf(DEG2RAD*i*angleStep)*radiusBottom, 0, cosf(DEG2RAD*i*angleStep)*radiusBottom);
+            }
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a wired cylinder with base at startPos and top at endPos
+// NOTE: It could be also used for pyramid and cone
+void DrawCylinderWiresEx(Vector3 startPos, Vector3 endPos, float startRadius, float endRadius, int sides, Color color)
+{
+    if (sides < 3) sides = 3;
+
+    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
+    if ((direction.x == 0) && (direction.y == 0) && (direction.z == 0)) return; // Security check
+
+    // Construct a basis of the base and the top face:
+    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
+    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
+
+    float baseAngle = (2.0f*PI)/sides;
+
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        for (int i = 0; i < sides; i++)
+        {
+            // Compute the four vertices
+            float s1 = sinf(baseAngle*(i + 0))*startRadius;
+            float c1 = cosf(baseAngle*(i + 0))*startRadius;
+            Vector3 w1 = { startPos.x + s1*b1.x + c1*b2.x, startPos.y + s1*b1.y + c1*b2.y, startPos.z + s1*b1.z + c1*b2.z };
+            float s2 = sinf(baseAngle*(i + 1))*startRadius;
+            float c2 = cosf(baseAngle*(i + 1))*startRadius;
+            Vector3 w2 = { startPos.x + s2*b1.x + c2*b2.x, startPos.y + s2*b1.y + c2*b2.y, startPos.z + s2*b1.z + c2*b2.z };
+            float s3 = sinf(baseAngle*(i + 0))*endRadius;
+            float c3 = cosf(baseAngle*(i + 0))*endRadius;
+            Vector3 w3 = { endPos.x + s3*b1.x + c3*b2.x, endPos.y + s3*b1.y + c3*b2.y, endPos.z + s3*b1.z + c3*b2.z };
+            float s4 = sinf(baseAngle*(i + 1))*endRadius;
+            float c4 = cosf(baseAngle*(i + 1))*endRadius;
+            Vector3 w4 = { endPos.x + s4*b1.x + c4*b2.x, endPos.y + s4*b1.y + c4*b2.y, endPos.z + s4*b1.z + c4*b2.z };
+
+            rlVertex3f(w1.x, w1.y, w1.z);
+            rlVertex3f(w2.x, w2.y, w2.z);
+
+            rlVertex3f(w1.x, w1.y, w1.z);
+            rlVertex3f(w3.x, w3.y, w3.z);
+
+            rlVertex3f(w3.x, w3.y, w3.z);
+            rlVertex3f(w4.x, w4.y, w4.z);
+        }
+    rlEnd();
+}
+
+// Draw a capsule with the center of its sphere caps at startPos and endPos
+void DrawCapsule(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color)
+{
+    if (slices < 3) slices = 3;
+
+    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
+
+    // draw a sphere if start and end points are the same
+    bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0);
+    if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f};
+
+    // Construct a basis of the base and the caps:
+    Vector3 b0 = Vector3Normalize(direction);
+    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
+    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
+    Vector3 capCenter = endPos;
+
+    float baseSliceAngle = (2.0f*PI)/slices;
+    float baseRingAngle  = PI*0.5f/rings;
+
+    rlBegin(RL_TRIANGLES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        // render both caps
+        for (int c = 0; c < 2; c++)
+        {
+            for (int i = 0; i < rings; i++)
+            {
+                for (int j = 0; j < slices; j++)
+                {
+
+                    // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
+
+                    // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
+                    // as we iterate through the rings they must get smaller by the cos(angle(i))
+
+                    // compute the four vertices
+                    float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
+                    float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
+                    Vector3 w1 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius
+                    };
+                    float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
+                    float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
+                    Vector3 w2 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius
+                    };
+
+                    float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
+                    float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
+                    Vector3 w3 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius
+                    };
+                    float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
+                    float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
+                    Vector3 w4 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius
+                    };
+
+                    // Make sure cap triangle normals are facing outwards
+                    if (c == 0)
+                    {
+                        rlVertex3f(w1.x, w1.y, w1.z);
+                        rlVertex3f(w2.x, w2.y, w2.z);
+                        rlVertex3f(w3.x, w3.y, w3.z);
+
+                        rlVertex3f(w2.x, w2.y, w2.z);
+                        rlVertex3f(w4.x, w4.y, w4.z);
+                        rlVertex3f(w3.x, w3.y, w3.z);
+                    }
+                    else
+                    {
+                        rlVertex3f(w1.x, w1.y, w1.z);
+                        rlVertex3f(w3.x, w3.y, w3.z);
+                        rlVertex3f(w2.x, w2.y, w2.z);
+
+                        rlVertex3f(w2.x, w2.y, w2.z);
+                        rlVertex3f(w3.x, w3.y, w3.z);
+                        rlVertex3f(w4.x, w4.y, w4.z);
+                    }
+                }
+            }
+            capCenter = startPos;
+            b0 = Vector3Scale(b0, -1.0f);
+        }
+        // render middle
+        if (!sphereCase)
+        {
+            for (int j = 0; j < slices; j++)
+            {
+                // compute the four vertices
+                float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius;
+                float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius;
+                Vector3 w1 = {
+                    startPos.x + ringSin1*b1.x + ringCos1*b2.x,
+                    startPos.y + ringSin1*b1.y + ringCos1*b2.y,
+                    startPos.z + ringSin1*b1.z + ringCos1*b2.z
+                };
+                float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius;
+                float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius;
+                Vector3 w2 = {
+                    startPos.x + ringSin2*b1.x + ringCos2*b2.x,
+                    startPos.y + ringSin2*b1.y + ringCos2*b2.y,
+                    startPos.z + ringSin2*b1.z + ringCos2*b2.z
+                };
+
+                float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius;
+                float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius;
+                Vector3 w3 = {
+                    endPos.x + ringSin3*b1.x + ringCos3*b2.x,
+                    endPos.y + ringSin3*b1.y + ringCos3*b2.y,
+                    endPos.z + ringSin3*b1.z + ringCos3*b2.z
+                };
+                float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius;
+                float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius;
+                Vector3 w4 = {
+                    endPos.x + ringSin4*b1.x + ringCos4*b2.x,
+                    endPos.y + ringSin4*b1.y + ringCos4*b2.y,
+                    endPos.z + ringSin4*b1.z + ringCos4*b2.z
+                };
+                                                                        //          w2 x.-----------x startPos
+                rlVertex3f(w1.x, w1.y, w1.z);                         // |           |\'.  T0    /
+                rlVertex3f(w2.x, w2.y, w2.z);                         // T1          | \ '.     /
+                rlVertex3f(w3.x, w3.y, w3.z);                         // |           |T \  '.  /
+                                                                        //             | 2 \ T 'x w1
+                rlVertex3f(w2.x, w2.y, w2.z);                         // |        w4 x.---\-1-|---x endPos
+                rlVertex3f(w4.x, w4.y, w4.z);                         // T2            '.  \  |T3/
+                rlVertex3f(w3.x, w3.y, w3.z);                         // |               '. \ | /
+                                                                        //                   '.\|/
+                                                                        //                   'x w3
+            }
+        }
+    rlEnd();
+}
+
+// Draw capsule wires with the center of its sphere caps at startPos and endPos
+void DrawCapsuleWires(Vector3 startPos, Vector3 endPos, float radius, int slices, int rings, Color color)
+{
+    if (slices < 3) slices = 3;
+
+    Vector3 direction = { endPos.x - startPos.x, endPos.y - startPos.y, endPos.z - startPos.z };
+
+    // draw a sphere if start and end points are the same
+    bool sphereCase = (direction.x == 0) && (direction.y == 0) && (direction.z == 0);
+    if (sphereCase) direction = (Vector3){0.0f, 1.0f, 0.0f};
+
+    // Construct a basis of the base and the caps:
+    Vector3 b0 = Vector3Normalize(direction);
+    Vector3 b1 = Vector3Normalize(Vector3Perpendicular(direction));
+    Vector3 b2 = Vector3Normalize(Vector3CrossProduct(b1, direction));
+    Vector3 capCenter = endPos;
+
+    float baseSliceAngle = (2.0f*PI)/slices;
+    float baseRingAngle  = PI*0.5f/rings;
+
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        // render both caps
+        for (int c = 0; c < 2; c++)
+        {
+            for (int i = 0; i < rings; i++)
+            {
+                for (int j = 0; j < slices; j++)
+                {
+
+                    // we build up the rings from capCenter in the direction of the 'direction' vector we computed earlier
+
+                    // as we iterate through the rings they must be placed higher above the center, the height we need is sin(angle(i))
+                    // as we iterate through the rings they must get smaller by the cos(angle(i))
+
+                    // compute the four vertices
+                    float ringSin1 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
+                    float ringCos1 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 0 ));
+                    Vector3 w1 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin1*b1.x + ringCos1*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin1*b1.y + ringCos1*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin1*b1.z + ringCos1*b2.z)*radius
+                    };
+                    float ringSin2 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
+                    float ringCos2 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 0 ));
+                    Vector3 w2 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 0 ))*b0.x + ringSin2*b1.x + ringCos2*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 0 ))*b0.y + ringSin2*b1.y + ringCos2*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 0 ))*b0.z + ringSin2*b1.z + ringCos2*b2.z)*radius
+                    };
+
+                    float ringSin3 = sinf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
+                    float ringCos3 = cosf(baseSliceAngle*(j + 0))*cosf(baseRingAngle*( i + 1 ));
+                    Vector3 w3 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin3*b1.x + ringCos3*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin3*b1.y + ringCos3*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin3*b1.z + ringCos3*b2.z)*radius
+                    };
+                    float ringSin4 = sinf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
+                    float ringCos4 = cosf(baseSliceAngle*(j + 1))*cosf(baseRingAngle*( i + 1 ));
+                    Vector3 w4 = (Vector3){
+                        capCenter.x + (sinf(baseRingAngle*( i + 1 ))*b0.x + ringSin4*b1.x + ringCos4*b2.x)*radius,
+                        capCenter.y + (sinf(baseRingAngle*( i + 1 ))*b0.y + ringSin4*b1.y + ringCos4*b2.y)*radius,
+                        capCenter.z + (sinf(baseRingAngle*( i + 1 ))*b0.z + ringSin4*b1.z + ringCos4*b2.z)*radius
+                    };
+
+                    rlVertex3f(w1.x, w1.y, w1.z);
+                    rlVertex3f(w2.x, w2.y, w2.z);
+
+                    rlVertex3f(w2.x, w2.y, w2.z);
+                    rlVertex3f(w3.x, w3.y, w3.z);
+
+                    rlVertex3f(w1.x, w1.y, w1.z);
+                    rlVertex3f(w3.x, w3.y, w3.z);
+
+                    rlVertex3f(w2.x, w2.y, w2.z);
+                    rlVertex3f(w4.x, w4.y, w4.z);
+
+                    rlVertex3f(w3.x, w3.y, w3.z);
+                    rlVertex3f(w4.x, w4.y, w4.z);
+                }
+            }
+            capCenter = startPos;
+            b0 = Vector3Scale(b0, -1.0f);
+        }
+        // render middle
+        if (!sphereCase)
+        {
+            for (int j = 0; j < slices; j++)
+            {
+                // compute the four vertices
+                float ringSin1 = sinf(baseSliceAngle*(j + 0))*radius;
+                float ringCos1 = cosf(baseSliceAngle*(j + 0))*radius;
+                Vector3 w1 = {
+                    startPos.x + ringSin1*b1.x + ringCos1*b2.x,
+                    startPos.y + ringSin1*b1.y + ringCos1*b2.y,
+                    startPos.z + ringSin1*b1.z + ringCos1*b2.z
+                };
+                float ringSin2 = sinf(baseSliceAngle*(j + 1))*radius;
+                float ringCos2 = cosf(baseSliceAngle*(j + 1))*radius;
+                Vector3 w2 = {
+                    startPos.x + ringSin2*b1.x + ringCos2*b2.x,
+                    startPos.y + ringSin2*b1.y + ringCos2*b2.y,
+                    startPos.z + ringSin2*b1.z + ringCos2*b2.z
+                };
+
+                float ringSin3 = sinf(baseSliceAngle*(j + 0))*radius;
+                float ringCos3 = cosf(baseSliceAngle*(j + 0))*radius;
+                Vector3 w3 = {
+                    endPos.x + ringSin3*b1.x + ringCos3*b2.x,
+                    endPos.y + ringSin3*b1.y + ringCos3*b2.y,
+                    endPos.z + ringSin3*b1.z + ringCos3*b2.z
+                };
+                float ringSin4 = sinf(baseSliceAngle*(j + 1))*radius;
+                float ringCos4 = cosf(baseSliceAngle*(j + 1))*radius;
+                Vector3 w4 = {
+                    endPos.x + ringSin4*b1.x + ringCos4*b2.x,
+                    endPos.y + ringSin4*b1.y + ringCos4*b2.y,
+                    endPos.z + ringSin4*b1.z + ringCos4*b2.z
+                };
+
+                rlVertex3f(w1.x, w1.y, w1.z);
+                rlVertex3f(w3.x, w3.y, w3.z);
+
+                rlVertex3f(w2.x, w2.y, w2.z);
+                rlVertex3f(w4.x, w4.y, w4.z);
+
+                rlVertex3f(w2.x, w2.y, w2.z);
+                rlVertex3f(w3.x, w3.y, w3.z);
+            }
+        }
+    rlEnd();
+}
+
+// Draw a plane
+void DrawPlane(Vector3 centerPos, Vector2 size, Color color)
+{
+    // NOTE: Plane is always created on XZ ground
+    rlPushMatrix();
+        rlTranslatef(centerPos.x, centerPos.y, centerPos.z);
+        rlScalef(size.x, 1.0f, size.y);
+
+        rlBegin(RL_QUADS);
+            rlColor4ub(color.r, color.g, color.b, color.a);
+            rlNormal3f(0.0f, 1.0f, 0.0f);
+
+            rlVertex3f(-0.5f, 0.0f, -0.5f);
+            rlVertex3f(-0.5f, 0.0f, 0.5f);
+            rlVertex3f(0.5f, 0.0f, 0.5f);
+            rlVertex3f(0.5f, 0.0f, -0.5f);
+        rlEnd();
+    rlPopMatrix();
+}
+
+// Draw a ray line
+void DrawRay(Ray ray, Color color)
+{
+    float scale = 10000;
+
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        rlVertex3f(ray.position.x, ray.position.y, ray.position.z);
+        rlVertex3f(ray.position.x + ray.direction.x*scale, ray.position.y + ray.direction.y*scale, ray.position.z + ray.direction.z*scale);
+    rlEnd();
+}
+
+// Draw a grid centered at (0, 0, 0)
+void DrawGrid(int slices, float spacing)
+{
+    int halfSlices = slices/2;
+
+    rlBegin(RL_LINES);
+        for (int i = -halfSlices; i <= halfSlices; i++)
+        {
+            if (i == 0)
+            {
+                rlColor3f(0.5f, 0.5f, 0.5f);
+            }
+            else
+            {
+                rlColor3f(0.75f, 0.75f, 0.75f);
+            }
+
+            rlVertex3f((float)i*spacing, 0.0f, (float)-halfSlices*spacing);
+            rlVertex3f((float)i*spacing, 0.0f, (float)halfSlices*spacing);
+
+            rlVertex3f((float)-halfSlices*spacing, 0.0f, (float)i*spacing);
+            rlVertex3f((float)halfSlices*spacing, 0.0f, (float)i*spacing);
+        }
+    rlEnd();
+}
+
+// Load model from files (mesh and material)
+Model LoadModel(const char *fileName)
+{
+    Model model = { 0 };
+
+#if defined(SUPPORT_FILEFORMAT_OBJ)
+    if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_IQM)
+    if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+    if (IsFileExtension(fileName, ".gltf") || IsFileExtension(fileName, ".glb")) model = LoadGLTF(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_VOX)
+    if (IsFileExtension(fileName, ".vox")) model = LoadVOX(fileName);
+#endif
+#if defined(SUPPORT_FILEFORMAT_M3D)
+    if (IsFileExtension(fileName, ".m3d")) model = LoadM3D(fileName);
+#endif
+
+    // Make sure model transform is set to identity matrix!
+    model.transform = MatrixIdentity();
+
+    if ((model.meshCount != 0) && (model.meshes != NULL))
+    {
+        // Upload vertex data to GPU (static meshes)
+        for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName);
+
+    if (model.materialCount == 0)
+    {
+        TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName);
+
+        model.materialCount = 1;
+        model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+        model.materials[0] = LoadMaterialDefault();
+
+        if (model.meshMaterial == NULL) model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+    }
+
+    return model;
+}
+
+// Load model from generated mesh
+// WARNING: A shallow copy of mesh is generated, passed by value,
+// as long as struct contains pointers to data and some values, we get a copy
+// of mesh pointing to same data as original version... be careful!
+Model LoadModelFromMesh(Mesh mesh)
+{
+    Model model = { 0 };
+
+    model.transform = MatrixIdentity();
+
+    model.meshCount = 1;
+    model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+    model.meshes[0] = mesh;
+
+    model.materialCount = 1;
+    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+    model.materials[0] = LoadMaterialDefault();
+
+    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+    model.meshMaterial[0] = 0;  // First material index
+
+    return model;
+}
+
+// Check if a model is valid (loaded in GPU, VAO/VBOs)
+bool IsModelValid(Model model)
+{
+    bool result = false;
+
+    if ((model.meshes != NULL) &&           // Validate model contains some mesh
+        (model.materials != NULL) &&        // Validate model contains some material (at least default one)
+        (model.meshMaterial != NULL) &&     // Validate mesh-material linkage
+        (model.meshCount > 0) &&            // Validate mesh count
+        (model.materialCount > 0)) result = true; // Validate material count
+
+    // NOTE: Many elements could be validated from a model, including every model mesh VAO/VBOs
+    // but some VBOs could not be used, it depends on Mesh vertex data
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        if ((model.meshes[i].vertices != NULL) && (model.meshes[i].vboId[0] == 0)) { result = false; break; }  // Vertex position buffer not uploaded to GPU
+        if ((model.meshes[i].texcoords != NULL) && (model.meshes[i].vboId[1] == 0)) { result = false; break; }  // Vertex textcoords buffer not uploaded to GPU
+        if ((model.meshes[i].normals != NULL) && (model.meshes[i].vboId[2] == 0)) { result = false; break; }  // Vertex normals buffer not uploaded to GPU
+        if ((model.meshes[i].colors != NULL) && (model.meshes[i].vboId[3] == 0)) { result = false; break; }  // Vertex colors buffer not uploaded to GPU
+        if ((model.meshes[i].tangents != NULL) && (model.meshes[i].vboId[4] == 0)) { result = false; break; }  // Vertex tangents buffer not uploaded to GPU
+        if ((model.meshes[i].texcoords2 != NULL) && (model.meshes[i].vboId[5] == 0)) { result = false; break; }  // Vertex texcoords2 buffer not uploaded to GPU
+        if ((model.meshes[i].indices != NULL) && (model.meshes[i].vboId[6] == 0)) { result = false; break; }  // Vertex indices buffer not uploaded to GPU
+        if ((model.meshes[i].boneIds != NULL) && (model.meshes[i].vboId[7] == 0)) { result = false; break; }  // Vertex boneIds buffer not uploaded to GPU
+        if ((model.meshes[i].boneWeights != NULL) && (model.meshes[i].vboId[8] == 0)) { result = false; break; }  // Vertex boneWeights buffer not uploaded to GPU
+
+        // NOTE: Some OpenGL versions do not support VAO, so we don't check it
+        //if (model.meshes[i].vaoId == 0) { result = false; break }
+    }
+
+    return result;
+}
+
+// Unload model (meshes/materials) from memory (RAM and/or VRAM)
+// NOTE: This function takes care of all model elements, for a detailed control
+// over them, use UnloadMesh() and UnloadMaterial()
+void UnloadModel(Model model)
+{
+    // Unload meshes
+    for (int i = 0; i < model.meshCount; i++) UnloadMesh(model.meshes[i]);
+
+    // Unload materials maps
+    // NOTE: As the user could be sharing shaders and textures between models,
+    // we don't unload the material but just free its maps,
+    // the user is responsible for freeing models shaders and textures
+    for (int i = 0; i < model.materialCount; i++) RL_FREE(model.materials[i].maps);
+
+    // Unload arrays
+    RL_FREE(model.meshes);
+    RL_FREE(model.materials);
+    RL_FREE(model.meshMaterial);
+
+    // Unload animation data
+    RL_FREE(model.bones);
+    RL_FREE(model.bindPose);
+
+    TRACELOG(LOG_INFO, "MODEL: Unloaded model (and meshes) from RAM and VRAM");
+}
+
+// Compute model bounding box limits (considers all meshes)
+BoundingBox GetModelBoundingBox(Model model)
+{
+    BoundingBox bounds = { 0 };
+
+    if (model.meshCount > 0)
+    {
+        Vector3 temp = { 0 };
+        bounds = GetMeshBoundingBox(model.meshes[0]);
+
+        for (int i = 1; i < model.meshCount; i++)
+        {
+            BoundingBox tempBounds = GetMeshBoundingBox(model.meshes[i]);
+
+            temp.x = (bounds.min.x < tempBounds.min.x)? bounds.min.x : tempBounds.min.x;
+            temp.y = (bounds.min.y < tempBounds.min.y)? bounds.min.y : tempBounds.min.y;
+            temp.z = (bounds.min.z < tempBounds.min.z)? bounds.min.z : tempBounds.min.z;
+            bounds.min = temp;
+
+            temp.x = (bounds.max.x > tempBounds.max.x)? bounds.max.x : tempBounds.max.x;
+            temp.y = (bounds.max.y > tempBounds.max.y)? bounds.max.y : tempBounds.max.y;
+            temp.z = (bounds.max.z > tempBounds.max.z)? bounds.max.z : tempBounds.max.z;
+            bounds.max = temp;
+        }
+    }
+
+    // Apply model.transform to bounding box
+    // WARNING: Current BoundingBox structure design does not support rotation transformations,
+    // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed)
+    bounds.min = Vector3Transform(bounds.min, model.transform);
+    bounds.max = Vector3Transform(bounds.max, model.transform);
+
+    return bounds;
+}
+
+// Upload vertex data into a VAO (if supported) and VBO
+void UploadMesh(Mesh *mesh, bool dynamic)
+{
+    if (mesh->vaoId > 0)
+    {
+        // Check if mesh has already been loaded in GPU
+        TRACELOG(LOG_WARNING, "VAO: [ID %i] Trying to re-load an already loaded mesh", mesh->vaoId);
+        return;
+    }
+
+    mesh->vboId = (unsigned int *)RL_CALLOC(MAX_MESH_VERTEX_BUFFERS, sizeof(unsigned int));
+
+    mesh->vaoId = 0;        // Vertex Array Object
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = 0;     // Vertex buffer: positions
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = 0;     // Vertex buffer: texcoords
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = 0;       // Vertex buffer: normals
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = 0;        // Vertex buffer: colors
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = 0;      // Vertex buffer: tangents
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = 0;    // Vertex buffer: texcoords2
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = 0;      // Vertex buffer: indices
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = 0;      // Vertex buffer: boneIds
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = 0;  // Vertex buffer: boneWeights
+#endif
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    mesh->vaoId = rlLoadVertexArray();
+    if (mesh->vaoId == 0) return;
+
+    rlEnableVertexArray(mesh->vaoId);
+
+    // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data
+
+    // Enable vertex attributes: position (shader-location = 0)
+    void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices;
+    mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic);
+    rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION, 3, RL_FLOAT, 0, 0, 0);
+    rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION);
+
+    // Enable vertex attributes: texcoords (shader-location = 1)
+
+    if (mesh->texcoords != NULL)
+    {
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD] = rlLoadVertexBuffer(mesh->texcoords, mesh->vertexCount*2*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, 2, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
+    }
+    else
+    {
+        float value[2] = { 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD, value, SHADER_ATTRIB_VEC2, 2);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD);
+    }
+    // WARNING: When setting default vertex attribute values, the values for each generic vertex attribute
+    // is part of current state, and it is maintained even if a different program object is used
+
+    if (mesh->normals != NULL)
+    {
+        // Enable vertex attributes: normals (shader-location = 2)
+        void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals;
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, 3, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
+    }
+    else
+    {
+        // Default vertex attribute: normal
+        // WARNING: Default value provided to shader if location available
+        float value[3] = { 0.0f, 0.0f, 1.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL, value, SHADER_ATTRIB_VEC3, 3);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL);
+    }
+
+    if (mesh->colors != NULL)
+    {
+        // Enable vertex attribute: color (shader-location = 3)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] = rlLoadVertexBuffer(mesh->colors, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, 4, RL_UNSIGNED_BYTE, 1, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
+    }
+    else
+    {
+        // Default vertex attribute: color
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };    // WHITE
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR, value, SHADER_ATTRIB_VEC4, 4);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR);
+    }
+
+    if (mesh->tangents != NULL)
+    {
+        // Enable vertex attribute: tangent (shader-location = 4)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
+    }
+    else
+    {
+        // Default vertex attribute: tangent
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, value, SHADER_ATTRIB_VEC4, 4);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
+    }
+
+    if (mesh->texcoords2 != NULL)
+    {
+        // Enable vertex attribute: texcoord2 (shader-location = 5)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2] = rlLoadVertexBuffer(mesh->texcoords2, mesh->vertexCount*2*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, 2, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
+    }
+    else
+    {
+        // Default vertex attribute: texcoord2
+        // WARNING: Default value provided to shader if location available
+        float value[2] = { 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2, value, SHADER_ATTRIB_VEC2, 2);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2);
+    }
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    if (mesh->boneIds != NULL)
+    {
+        // Enable vertex attribute: boneIds (shader-location = 7)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS] = rlLoadVertexBuffer(mesh->boneIds, mesh->vertexCount*4*sizeof(unsigned char), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+    }
+    else
+    {
+        // Default vertex attribute: boneIds
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS, value, SHADER_ATTRIB_VEC4, 4);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS);
+    }
+
+    if (mesh->boneWeights != NULL)
+    {
+        // Enable vertex attribute: boneWeights (shader-location = 8)
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS] = rlLoadVertexBuffer(mesh->boneWeights, mesh->vertexCount*4*sizeof(float), dynamic);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, 4, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
+    }
+    else
+    {
+        // Default vertex attribute: boneWeights
+        // WARNING: Default value provided to shader if location available
+        float value[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+        rlSetVertexAttributeDefault(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS, value, SHADER_ATTRIB_VEC4, 2);
+        rlDisableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS);
+    }
+#endif
+
+    if (mesh->indices != NULL)
+    {
+        mesh->vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES] = rlLoadVertexBufferElement(mesh->indices, mesh->triangleCount*3*sizeof(unsigned short), dynamic);
+    }
+
+    if (mesh->vaoId > 0) TRACELOG(LOG_INFO, "VAO: [ID %i] Mesh uploaded successfully to VRAM (GPU)", mesh->vaoId);
+    else TRACELOG(LOG_INFO, "VBO: Mesh uploaded successfully to VRAM (GPU)");
+
+    rlDisableVertexArray();
+#endif
+}
+
+// Update mesh vertex data in GPU for a specific buffer index
+void UpdateMeshBuffer(Mesh mesh, int index, const void *data, int dataSize, int offset)
+{
+    rlUpdateVertexBuffer(mesh.vboId[index], data, dataSize, offset);
+}
+
+// Draw a 3d mesh with material and transform
+void DrawMesh(Mesh mesh, Material material, Matrix transform)
+{
+#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_11_SOFTWARE)
+    #define GL_VERTEX_ARRAY         0x8074
+    #define GL_NORMAL_ARRAY         0x8075
+    #define GL_COLOR_ARRAY          0x8076
+    #define GL_TEXTURE_COORD_ARRAY  0x8078
+
+    if (mesh.texcoords && material.maps[MATERIAL_MAP_DIFFUSE].texture.id > 0) rlEnableTexture(material.maps[MATERIAL_MAP_DIFFUSE].texture.id);
+
+    if (mesh.animVertices) rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.animVertices);
+    else rlEnableStatePointer(GL_VERTEX_ARRAY, mesh.vertices);
+
+    if (mesh.texcoords) rlEnableStatePointer(GL_TEXTURE_COORD_ARRAY, mesh.texcoords);
+
+    if (mesh.animNormals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.animNormals);
+    else if (mesh.normals) rlEnableStatePointer(GL_NORMAL_ARRAY, mesh.normals);
+
+    if (mesh.colors) rlEnableStatePointer(GL_COLOR_ARRAY, mesh.colors);
+
+    rlPushMatrix();
+        rlMultMatrixf(MatrixToFloat(transform));
+        rlColor4ub(material.maps[MATERIAL_MAP_DIFFUSE].color.r,
+                   material.maps[MATERIAL_MAP_DIFFUSE].color.g,
+                   material.maps[MATERIAL_MAP_DIFFUSE].color.b,
+                   material.maps[MATERIAL_MAP_DIFFUSE].color.a);
+
+        if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, mesh.indices);
+        else rlDrawVertexArray(0, mesh.vertexCount);
+    rlPopMatrix();
+
+    rlDisableStatePointer(GL_VERTEX_ARRAY);
+    rlDisableStatePointer(GL_TEXTURE_COORD_ARRAY);
+    rlDisableStatePointer(GL_NORMAL_ARRAY);
+    rlDisableStatePointer(GL_COLOR_ARRAY);
+
+    rlDisableTexture();
+#endif
+
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    // Bind shader program
+    rlEnableShader(material.shader.id);
+
+    if (material.shader.locs == NULL) return;
+
+    // Send required data to shader (matrices, values)
+    //-----------------------------------------------------
+    // Upload to shader material.colDiffuse
+    if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1)
+    {
+        float values[4] = {
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f
+        };
+
+        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1);
+    }
+
+    // Upload to shader material.colSpecular (if location available)
+    if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1)
+    {
+        float values[4] = {
+            (float)material.maps[MATERIAL_MAP_SPECULAR].color.r/255.0f,
+            (float)material.maps[MATERIAL_MAP_SPECULAR].color.g/255.0f,
+            (float)material.maps[MATERIAL_MAP_SPECULAR].color.b/255.0f,
+            (float)material.maps[MATERIAL_MAP_SPECULAR].color.a/255.0f
+        };
+
+        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1);
+    }
+
+    // Get a copy of current matrices to work with,
+    // just in case stereo render is required, and we need to modify them
+    // NOTE: At this point the modelview matrix just contains the view matrix (camera)
+    // That's because BeginMode3D() sets it and there is no model-drawing function
+    // that modifies it, all use rlPushMatrix() and rlPopMatrix()
+    Matrix matModel = MatrixIdentity();
+    Matrix matView = rlGetMatrixModelview();
+    Matrix matModelView = MatrixIdentity();
+    Matrix matProjection = rlGetMatrixProjection();
+
+    // Upload view and projection matrices (if locations available)
+    if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
+    if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
+
+    // Accumulate several model transformations:
+    //    transform: model transformation provided (includes DrawModel() params combined with model.transform)
+    //    rlGetMatrixTransform(): rlgl internal transform matrix due to push/pop matrix stack
+    matModel = MatrixMultiply(transform, rlGetMatrixTransform());
+
+    // Model transformation matrix is sent to shader uniform location: SHADER_LOC_MATRIX_MODEL
+    if (material.shader.locs[SHADER_LOC_MATRIX_MODEL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MODEL], matModel);
+
+    // Get model-view matrix
+    matModelView = MatrixMultiply(matModel, matView);
+
+    // Upload model normal matrix (if locations available)
+    if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    // Upload Bone Transforms
+    if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices)
+    {
+        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
+    }
+#endif
+    //-----------------------------------------------------
+
+    // Bind active texture maps (if available)
+    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+    {
+        if (material.maps[i].texture.id > 0)
+        {
+            // Select current shader texture slot
+            rlActiveTextureSlot(i);
+
+            // Enable texture for active slot
+            if ((i == MATERIAL_MAP_IRRADIANCE) ||
+                (i == MATERIAL_MAP_PREFILTER) ||
+                (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id);
+            else rlEnableTexture(material.maps[i].texture.id);
+
+            rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1);
+        }
+    }
+
+    // Try binding vertex array objects (VAO) or use VBOs if not possible
+    // WARNING: UploadMesh() enables all vertex attributes available in mesh and sets default attribute values
+    // for shader expected vertex attributes that are not provided by the mesh (i.e. colors)
+    // This could be a dangerous approach because different meshes with different shaders can enable/disable some attributes
+    if (!rlEnableVertexArray(mesh.vaoId))
+    {
+        // Bind mesh VBO data: vertex position (shader-location = 0)
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
+        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
+
+        // Bind mesh VBO data: vertex texcoords (shader-location = 1)
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
+        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
+
+        if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
+        {
+            // Bind mesh VBO data: vertex normals (shader-location = 2)
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
+        }
+
+        // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
+        {
+            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
+            {
+                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
+                rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
+                rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
+            }
+            else
+            {
+                // Set default value for defined vertex attribute in shader but not provided by mesh
+                // WARNING: It could result in GPU undefined behaviour
+                float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+                rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
+                rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
+            }
+        }
+
+        // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
+        }
+
+        // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
+        }
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
+        }
+
+        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
+        }
+#endif
+
+        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
+    }
+
+    int eyeCount = 1;
+    if (rlIsStereoRenderEnabled()) eyeCount = 2;
+
+    for (int eye = 0; eye < eyeCount; eye++)
+    {
+        // Calculate model-view-projection matrix (MVP)
+        Matrix matModelViewProjection = MatrixIdentity();
+        if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection);
+        else
+        {
+            // Setup current eye viewport (half screen width)
+            rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight());
+            matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye));
+        }
+
+        // Send combined model-view-projection matrix to shader
+        rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection);
+
+        // Draw mesh
+        if (mesh.indices != NULL) rlDrawVertexArrayElements(0, mesh.triangleCount*3, 0);
+        else rlDrawVertexArray(0, mesh.vertexCount);
+    }
+
+    // Unbind all bound texture maps
+    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+    {
+        if (material.maps[i].texture.id > 0)
+        {
+            // Select current shader texture slot
+            rlActiveTextureSlot(i);
+
+            // Disable texture for active slot
+            if ((i == MATERIAL_MAP_IRRADIANCE) ||
+                (i == MATERIAL_MAP_PREFILTER) ||
+                (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
+            else rlDisableTexture();
+        }
+    }
+
+    // Disable all possible vertex array objects (or VBOs)
+    rlDisableVertexArray();
+    rlDisableVertexBuffer();
+    rlDisableVertexBufferElement();
+
+    // Disable shader program
+    rlDisableShader();
+
+    // Restore rlgl internal modelview and projection matrices
+    rlSetMatrixModelview(matView);
+    rlSetMatrixProjection(matProjection);
+#endif
+}
+
+// Draw multiple mesh instances with material and different transforms
+void DrawMeshInstanced(Mesh mesh, Material material, const Matrix *transforms, int instances)
+{
+#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+    // Instancing required variables
+    float16 *instanceTransforms = NULL;
+    unsigned int instancesVboId = 0;
+
+    // Bind shader program
+    rlEnableShader(material.shader.id);
+
+    // Send required data to shader (matrices, values)
+    //-----------------------------------------------------
+    // Upload to shader material.colDiffuse
+    if (material.shader.locs[SHADER_LOC_COLOR_DIFFUSE] != -1)
+    {
+        float values[4] = {
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.r/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.g/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.b/255.0f,
+            (float)material.maps[MATERIAL_MAP_DIFFUSE].color.a/255.0f
+        };
+
+        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_DIFFUSE], values, SHADER_UNIFORM_VEC4, 1);
+    }
+
+    // Upload to shader material.colSpecular (if location available)
+    if (material.shader.locs[SHADER_LOC_COLOR_SPECULAR] != -1)
+    {
+        float values[4] = {
+            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.r/255.0f,
+            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.g/255.0f,
+            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.b/255.0f,
+            (float)material.maps[SHADER_LOC_COLOR_SPECULAR].color.a/255.0f
+        };
+
+        rlSetUniform(material.shader.locs[SHADER_LOC_COLOR_SPECULAR], values, SHADER_UNIFORM_VEC4, 1);
+    }
+
+    // Get a copy of current matrices to work with,
+    // just in case stereo render is required, and we need to modify them
+    // NOTE: At this point the modelview matrix just contains the view matrix (camera)
+    // That's because BeginMode3D() sets it and there is no model-drawing function
+    // that modifies it, all use rlPushMatrix() and rlPopMatrix()
+    Matrix matModel = MatrixIdentity();
+    Matrix matView = rlGetMatrixModelview();
+    Matrix matModelView = MatrixIdentity();
+    Matrix matProjection = rlGetMatrixProjection();
+
+    // Upload view and projection matrices (if locations available)
+    if (material.shader.locs[SHADER_LOC_MATRIX_VIEW] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_VIEW], matView);
+    if (material.shader.locs[SHADER_LOC_MATRIX_PROJECTION] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_PROJECTION], matProjection);
+
+    // Create instances buffer
+    instanceTransforms = (float16 *)RL_MALLOC(instances*sizeof(float16));
+
+    // Fill buffer with instances transformations as float16 arrays
+    for (int i = 0; i < instances; i++) instanceTransforms[i] = MatrixToFloatV(transforms[i]);
+
+    // Enable mesh VAO to attach new buffer
+    rlEnableVertexArray(mesh.vaoId);
+
+    // This could alternatively use a static VBO and either glMapBuffer() or glBufferSubData()
+    // It isn't clear which would be reliably faster in all cases and on all platforms,
+    // anecdotally glMapBuffer() seems very slow (syncs) while glBufferSubData() seems
+    // no faster, since we're transferring all the transform matrices anyway
+    instancesVboId = rlLoadVertexBuffer(instanceTransforms, instances*sizeof(float16), false);
+
+    // Instances transformation matrices are sent to shader attribute location: SHADER_LOC_VERTEX_INSTANCE_TX
+    if (material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] != -1)
+    {
+        for (unsigned int i = 0; i < 4; i++)
+        {
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 4, RL_FLOAT, 0, sizeof(Matrix), i*sizeof(Vector4));
+            rlSetVertexAttributeDivisor(material.shader.locs[SHADER_LOC_VERTEX_INSTANCE_TX] + i, 1);
+        }
+    }
+
+    rlDisableVertexBuffer();
+    rlDisableVertexArray();
+
+    // Accumulate internal matrix transform (push/pop) and view matrix
+    // NOTE: In this case, model instance transformation must be computed in the shader
+    matModelView = MatrixMultiply(rlGetMatrixTransform(), matView);
+
+    // Upload model normal matrix (if locations available)
+    if (material.shader.locs[SHADER_LOC_MATRIX_NORMAL] != -1) rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_NORMAL], MatrixTranspose(MatrixInvert(matModel)));
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+    // Upload Bone Transforms
+    if ((material.shader.locs[SHADER_LOC_BONE_MATRICES] != -1) && mesh.boneMatrices)
+    {
+        rlSetUniformMatrices(material.shader.locs[SHADER_LOC_BONE_MATRICES], mesh.boneMatrices, mesh.boneCount);
+    }
+#endif
+
+    //-----------------------------------------------------
+
+    // Bind active texture maps (if available)
+    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+    {
+        if (material.maps[i].texture.id > 0)
+        {
+            // Select current shader texture slot
+            rlActiveTextureSlot(i);
+
+            // Enable texture for active slot
+            if ((i == MATERIAL_MAP_IRRADIANCE) ||
+                (i == MATERIAL_MAP_PREFILTER) ||
+                (i == MATERIAL_MAP_CUBEMAP)) rlEnableTextureCubemap(material.maps[i].texture.id);
+            else rlEnableTexture(material.maps[i].texture.id);
+
+            rlSetUniform(material.shader.locs[SHADER_LOC_MAP_DIFFUSE + i], &i, SHADER_UNIFORM_INT, 1);
+        }
+    }
+
+    // Try binding vertex array objects (VAO)
+    // or use VBOs if not possible
+    if (!rlEnableVertexArray(mesh.vaoId))
+    {
+        // Bind mesh VBO data: vertex position (shader-location = 0)
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_POSITION]);
+        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION], 3, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_POSITION]);
+
+        // Bind mesh VBO data: vertex texcoords (shader-location = 1)
+        rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD]);
+        rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01], 2, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD01]);
+
+        if (material.shader.locs[SHADER_LOC_VERTEX_NORMAL] != -1)
+        {
+            // Bind mesh VBO data: vertex normals (shader-location = 2)
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_NORMAL]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL], 3, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_NORMAL]);
+        }
+
+        // Bind mesh VBO data: vertex colors (shader-location = 3, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_COLOR] != -1)
+        {
+            if (mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR] != 0)
+            {
+                rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_COLOR]);
+                rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR], 4, RL_UNSIGNED_BYTE, 1, 0, 0);
+                rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
+            }
+            else
+            {
+                // Set default value for unused attribute
+                // NOTE: Required when using default shader and no VAO support
+                float value[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+                rlSetVertexAttributeDefault(material.shader.locs[SHADER_LOC_VERTEX_COLOR], value, SHADER_ATTRIB_VEC4, 4);
+                rlDisableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_COLOR]);
+            }
+        }
+
+        // Bind mesh VBO data: vertex tangents (shader-location = 4, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_TANGENT] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TANGENT]);
+        }
+
+        // Bind mesh VBO data: vertex texcoords2 (shader-location = 5, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_TEXCOORD2]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02], 2, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_TEXCOORD02]);
+        }
+
+#ifdef RL_SUPPORT_MESH_GPU_SKINNING
+        // Bind mesh VBO data: vertex bone ids (shader-location = 6, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEIDS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEIDS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS], 4, RL_UNSIGNED_BYTE, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEIDS]);
+        }
+
+        // Bind mesh VBO data: vertex bone weights (shader-location = 7, if available)
+        if (material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS] != -1)
+        {
+            rlEnableVertexBuffer(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_BONEWEIGHTS]);
+            rlSetVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS], 4, RL_FLOAT, 0, 0, 0);
+            rlEnableVertexAttribute(material.shader.locs[SHADER_LOC_VERTEX_BONEWEIGHTS]);
+        }
+#endif
+
+        if (mesh.indices != NULL) rlEnableVertexBufferElement(mesh.vboId[RL_DEFAULT_SHADER_ATTRIB_LOCATION_INDICES]);
+    }
+
+    int eyeCount = 1;
+    if (rlIsStereoRenderEnabled()) eyeCount = 2;
+
+    for (int eye = 0; eye < eyeCount; eye++)
+    {
+        // Calculate model-view-projection matrix (MVP)
+        Matrix matModelViewProjection = MatrixIdentity();
+        if (eyeCount == 1) matModelViewProjection = MatrixMultiply(matModelView, matProjection);
+        else
+        {
+            // Setup current eye viewport (half screen width)
+            rlViewport(eye*rlGetFramebufferWidth()/2, 0, rlGetFramebufferWidth()/2, rlGetFramebufferHeight());
+            matModelViewProjection = MatrixMultiply(MatrixMultiply(matModelView, rlGetMatrixViewOffsetStereo(eye)), rlGetMatrixProjectionStereo(eye));
+        }
+
+        // Send combined model-view-projection matrix to shader
+        rlSetUniformMatrix(material.shader.locs[SHADER_LOC_MATRIX_MVP], matModelViewProjection);
+
+        // Draw mesh instanced
+        if (mesh.indices != NULL) rlDrawVertexArrayElementsInstanced(0, mesh.triangleCount*3, 0, instances);
+        else rlDrawVertexArrayInstanced(0, mesh.vertexCount, instances);
+    }
+
+    // Unbind all bound texture maps
+    for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+    {
+        if (material.maps[i].texture.id > 0)
+        {
+            // Select current shader texture slot
+            rlActiveTextureSlot(i);
+
+            // Disable texture for active slot
+            if ((i == MATERIAL_MAP_IRRADIANCE) ||
+                (i == MATERIAL_MAP_PREFILTER) ||
+                (i == MATERIAL_MAP_CUBEMAP)) rlDisableTextureCubemap();
+            else rlDisableTexture();
+        }
+    }
+
+    // Disable all possible vertex array objects (or VBOs)
+    rlDisableVertexArray();
+    rlDisableVertexBuffer();
+    rlDisableVertexBufferElement();
+
+    // Disable shader program
+    rlDisableShader();
+
+    // Remove instance transforms buffer
+    rlUnloadVertexBuffer(instancesVboId);
+    RL_FREE(instanceTransforms);
+#endif
+}
+
+// Unload mesh from memory (RAM and VRAM)
+void UnloadMesh(Mesh mesh)
+{
+    // Unload rlgl mesh vboId data
+    rlUnloadVertexArray(mesh.vaoId);
+
+    if (mesh.vboId != NULL) for (int i = 0; i < MAX_MESH_VERTEX_BUFFERS; i++) rlUnloadVertexBuffer(mesh.vboId[i]);
+    RL_FREE(mesh.vboId);
+
+    RL_FREE(mesh.vertices);
+    RL_FREE(mesh.texcoords);
+    RL_FREE(mesh.normals);
+    RL_FREE(mesh.colors);
+    RL_FREE(mesh.tangents);
+    RL_FREE(mesh.texcoords2);
+    RL_FREE(mesh.indices);
+
+    RL_FREE(mesh.animVertices);
+    RL_FREE(mesh.animNormals);
+    RL_FREE(mesh.boneWeights);
+    RL_FREE(mesh.boneIds);
+    RL_FREE(mesh.boneMatrices);
+}
+
+// Export mesh data to file
+bool ExportMesh(Mesh mesh, const char *fileName)
+{
+    bool success = false;
+
+    if (IsFileExtension(fileName, ".obj"))
+    {
+        // Estimated data size, it should be enough...
+        int vc = mesh.vertexCount;
+        int dataSize = vc*(int)strlen("v -0000.000000f -0000.000000f -0000.000000f\n") +
+                       vc*(int)strlen("vt -0.000000f -0.000000f\n") +
+                       vc*(int)strlen("vn -0.0000f -0.0000f -0.0000f\n") +
+                       mesh.triangleCount*snprintf(NULL, 0, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", vc, vc, vc, vc, vc, vc, vc, vc, vc);
+
+        // NOTE: Text data buffer size is estimated considering mesh data size
+        char *txtData = (char *)RL_CALLOC(dataSize + 1000, sizeof(char));
+
+        int byteCount = 0;
+        byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n");
+        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
+        byteCount += sprintf(txtData + byteCount, "# // rMeshOBJ exporter v1.0 - Mesh exported as triangle faces and not optimized   //\n");
+        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
+        byteCount += sprintf(txtData + byteCount, "# // more info and bugs-report:  github.com/raysan5/raylib                        //\n");
+        byteCount += sprintf(txtData + byteCount, "# // feedback and support:       ray[at]raylib.com                                //\n");
+        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
+        byteCount += sprintf(txtData + byteCount, "# // Copyright (c) 2018-2026 Ramon Santamaria (@raysan5)                          //\n");
+        byteCount += sprintf(txtData + byteCount, "# //                                                                              //\n");
+        byteCount += sprintf(txtData + byteCount, "# //////////////////////////////////////////////////////////////////////////////////\n\n");
+        byteCount += sprintf(txtData + byteCount, "# Vertex Count:     %i\n", mesh.vertexCount);
+        byteCount += sprintf(txtData + byteCount, "# Triangle Count:   %i\n\n", mesh.triangleCount);
+
+        byteCount += sprintf(txtData + byteCount, "g mesh\n");
+
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
+        {
+            byteCount += sprintf(txtData + byteCount, "v %.6f %.6f %.6f\n", mesh.vertices[v], mesh.vertices[v + 1], mesh.vertices[v + 2]);
+        }
+
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 2)
+        {
+            byteCount += sprintf(txtData + byteCount, "vt %.6f %.6f\n", mesh.texcoords[v], mesh.texcoords[v + 1]);
+        }
+
+        for (int i = 0, v = 0; i < mesh.vertexCount; i++, v += 3)
+        {
+            byteCount += sprintf(txtData + byteCount, "vn %.4f %.4f %.4f\n", mesh.normals[v], mesh.normals[v + 1], mesh.normals[v + 2]);
+        }
+
+        if (mesh.indices != NULL)
+        {
+            for (int i = 0, v = 0; i < mesh.triangleCount; i++, v += 3)
+            {
+                byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n",
+                    mesh.indices[v] + 1, mesh.indices[v] + 1, mesh.indices[v] + 1,
+                    mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1, mesh.indices[v + 1] + 1,
+                    mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1, mesh.indices[v + 2] + 1);
+            }
+        }
+        else
+        {
+            for (int i = 0, v = 1; i < mesh.triangleCount; i++, v += 3)
+            {
+                byteCount += sprintf(txtData + byteCount, "f %i/%i/%i %i/%i/%i %i/%i/%i\n", v, v, v, v + 1, v + 1, v + 1, v + 2, v + 2, v + 2);
+            }
+        }
+
+        // NOTE: Text data length exported is determined by '\0' (NULL) character
+        success = SaveFileText(fileName, txtData);
+
+        RL_FREE(txtData);
+    }
+    else if (IsFileExtension(fileName, ".raw"))
+    {
+        // TODO: Support additional file formats to export mesh vertex data
+    }
+
+    return success;
+}
+
+// Export mesh as code file (.h) defining multiple arrays of vertex attributes
+bool ExportMeshAsCode(Mesh mesh, const char *fileName)
+{
+    bool success = false;
+
+#ifndef TEXT_BYTES_PER_LINE
+    #define TEXT_BYTES_PER_LINE     20
+#endif
+
+    // NOTE: Text data buffer size is fixed to 64MB
+    char *txtData = (char *)RL_CALLOC(64*1024*1024, sizeof(char));  // 64 MB
+
+    int byteCount = 0;
+    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// MeshAsCode exporter v1.0 - Mesh vertex data exported as arrays                     //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
+    byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2023 Ramon Santamaria (@raysan5)                                     //\n");
+    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
+    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
+
+    // Get file name from path and convert variable name to uppercase
+    char varFileName[256] = { 0 };
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
+    for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
+
+    // Add image information
+    byteCount += sprintf(txtData + byteCount, "// Mesh basic information\n");
+    byteCount += sprintf(txtData + byteCount, "#define %s_VERTEX_COUNT    %i\n", varFileName, mesh.vertexCount);
+    byteCount += sprintf(txtData + byteCount, "#define %s_TRIANGLE_COUNT   %i\n\n", varFileName, mesh.triangleCount);
+
+    // Define vertex attributes data as separate arrays
+    //-----------------------------------------------------------------------------------------
+    if (mesh.vertices != NULL)      // Vertex position (XYZ - 3 components per vertex - float)
+    {
+        byteCount += sprintf(txtData + byteCount, "static float %s_VERTEX_DATA[%i] = { ", varFileName, mesh.vertexCount*3);
+        for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.vertices[i]);
+        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.vertices[mesh.vertexCount*3 - 1]);
+    }
+
+    if (mesh.texcoords != NULL)      // Vertex texture coordinates (UV - 2 components per vertex - float)
+    {
+        byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD_DATA[%i] = { ", varFileName, mesh.vertexCount*2);
+        for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords[i]);
+        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords[mesh.vertexCount*2 - 1]);
+    }
+
+    if (mesh.texcoords2 != NULL)      // Vertex texture coordinates (UV - 2 components per vertex - float)
+    {
+        byteCount += sprintf(txtData + byteCount, "static float %s_TEXCOORD2_DATA[%i] = { ", varFileName, mesh.vertexCount*2);
+        for (int i = 0; i < mesh.vertexCount*2 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.texcoords2[i]);
+        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.texcoords2[mesh.vertexCount*2 - 1]);
+    }
+
+    if (mesh.normals != NULL)      // Vertex normals (XYZ - 3 components per vertex - float)
+    {
+        byteCount += sprintf(txtData + byteCount, "static float %s_NORMAL_DATA[%i] = { ", varFileName, mesh.vertexCount*3);
+        for (int i = 0; i < mesh.vertexCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.normals[i]);
+        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.normals[mesh.vertexCount*3 - 1]);
+    }
+
+    if (mesh.tangents != NULL)      // Vertex tangents (XYZW - 4 components per vertex - float)
+    {
+        byteCount += sprintf(txtData + byteCount, "static float %s_TANGENT_DATA[%i] = { ", varFileName, mesh.vertexCount*4);
+        for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.3ff,\n" : "%.3ff, "), mesh.tangents[i]);
+        byteCount += sprintf(txtData + byteCount, "%.3ff };\n\n", mesh.tangents[mesh.vertexCount*4 - 1]);
+    }
+
+    if (mesh.colors != NULL)        // Vertex colors (RGBA - 4 components per vertex - unsigned char)
+    {
+        byteCount += sprintf(txtData + byteCount, "static unsigned char %s_COLOR_DATA[%i] = { ", varFileName, mesh.vertexCount*4);
+        for (int i = 0; i < mesh.vertexCount*4 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), mesh.colors[i]);
+        byteCount += sprintf(txtData + byteCount, "0x%x };\n\n", mesh.colors[mesh.vertexCount*4 - 1]);
+    }
+
+    if (mesh.indices != NULL)       // Vertex indices (3 index per triangle - unsigned short)
+    {
+        byteCount += sprintf(txtData + byteCount, "static unsigned short %s_INDEX_DATA[%i] = { ", varFileName, mesh.triangleCount*3);
+        for (int i = 0; i < mesh.triangleCount*3 - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%i,\n" : "%i, "), mesh.indices[i]);
+        byteCount += sprintf(txtData + byteCount, "%i };\n", mesh.indices[mesh.triangleCount*3 - 1]);
+    }
+    //-----------------------------------------------------------------------------------------
+
+    // NOTE: Text data size exported is determined by '\0' (NULL) character
+    success = SaveFileText(fileName, txtData);
+
+    RL_FREE(txtData);
+
+    //if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
+    //else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
+
+    return success;
+}
+
+#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
+// Process obj materials
+static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount)
+{
+    // Init model mats
+    for (int m = 0; m < materialCount; m++)
+    {
+        // Init material to default
+        // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE
+        materials[m] = LoadMaterialDefault();
+
+        if (mats == NULL) continue;
+
+        // Get default texture, in case no texture is defined
+        // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8
+        materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
+
+        if (mats[m].diffuse_texname != NULL) materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(mats[m].diffuse_texname);  //char *diffuse_texname; // map_Kd
+        else materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(mats[m].diffuse[0]*255.0f), (unsigned char)(mats[m].diffuse[1]*255.0f), (unsigned char)(mats[m].diffuse[2]*255.0f), 255 }; //float diffuse[3];
+        materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
+
+        if (mats[m].specular_texname != NULL) materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(mats[m].specular_texname);  //char *specular_texname; // map_Ks
+        materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(mats[m].specular[0]*255.0f), (unsigned char)(mats[m].specular[1]*255.0f), (unsigned char)(mats[m].specular[2]*255.0f), 255 }; //float specular[3];
+        materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f;
+
+        if (mats[m].bump_texname != NULL) materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(mats[m].bump_texname);  //char *bump_texname; // map_bump, bump
+        materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE;
+        materials[m].maps[MATERIAL_MAP_NORMAL].value = mats[m].shininess;
+
+        materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(mats[m].emission[0]*255.0f), (unsigned char)(mats[m].emission[1]*255.0f), (unsigned char)(mats[m].emission[2]*255.0f), 255 }; //float emission[3];
+
+        if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname);  //char *displacement_texname; // disp
+    }
+}
+#endif
+
+// Load materials from model file
+Material *LoadMaterials(const char *fileName, int *materialCount)
+{
+    Material *materials = NULL;
+    unsigned int count = 0;
+
+    // TODO: Support IQM and GLTF for materials parsing
+
+#if defined(SUPPORT_FILEFORMAT_MTL)
+    if (IsFileExtension(fileName, ".mtl"))
+    {
+        tinyobj_material_t *mats = NULL;
+
+        int result = tinyobj_parse_mtl_file(&mats, &count, fileName);
+        if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName);
+
+        materials = (Material *)RL_MALLOC(count*sizeof(Material));
+        ProcessMaterialsOBJ(materials, mats, count);
+
+        tinyobj_materials_free(mats, count);
+    }
+#else
+    TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to load material file", fileName);
+#endif
+
+    *materialCount = count;
+    return materials;
+}
+
+// Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps)
+Material LoadMaterialDefault(void)
+{
+    Material material = { 0 };
+    material.maps = (MaterialMap *)RL_CALLOC(MAX_MATERIAL_MAPS, sizeof(MaterialMap));
+
+    // Using rlgl default shader
+    material.shader.id = rlGetShaderIdDefault();
+    material.shader.locs = rlGetShaderLocsDefault();
+
+    // Using rlgl default texture (1x1 pixel, UNCOMPRESSED_R8G8B8A8, 1 mipmap)
+    material.maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
+    //material.maps[MATERIAL_MAP_NORMAL].texture;         // NOTE: By default, not set
+    //material.maps[MATERIAL_MAP_SPECULAR].texture;       // NOTE: By default, not set
+
+    material.maps[MATERIAL_MAP_DIFFUSE].color = WHITE;    // Diffuse color
+    material.maps[MATERIAL_MAP_SPECULAR].color = WHITE;   // Specular color
+
+    return material;
+}
+
+// Check if a material is valid (map textures loaded in GPU)
+bool IsMaterialValid(Material material)
+{
+    bool result = false;
+
+    if ((material.maps != NULL) &&      // Validate material contain some map
+        (material.shader.id > 0)) result = true; // Validate material shader is valid
+
+    // TODO: Check if available maps contain loaded textures
+
+    return result;
+}
+
+// Unload material from memory
+void UnloadMaterial(Material material)
+{
+    // Unload material shader (avoid unloading default shader, managed by raylib)
+    if (material.shader.id != rlGetShaderIdDefault()) UnloadShader(material.shader);
+
+    // Unload loaded texture maps (avoid unloading default texture, managed by raylib)
+    if (material.maps != NULL)
+    {
+        for (int i = 0; i < MAX_MATERIAL_MAPS; i++)
+        {
+            if (material.maps[i].texture.id != rlGetTextureIdDefault()) rlUnloadTexture(material.maps[i].texture.id);
+        }
+    }
+
+    RL_FREE(material.maps);
+}
+
+// Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
+// NOTE: Previous texture should be manually unloaded
+void SetMaterialTexture(Material *material, int mapType, Texture2D texture)
+{
+    material->maps[mapType].texture = texture;
+}
+
+// Set the material for a mesh
+void SetModelMeshMaterial(Model *model, int meshId, int materialId)
+{
+    if (meshId >= model->meshCount) TRACELOG(LOG_WARNING, "MESH: Id greater than mesh count");
+    else if (materialId >= model->materialCount) TRACELOG(LOG_WARNING, "MATERIAL: Id greater than material count");
+    else  model->meshMaterial[meshId] = materialId;
+}
+
+// Load model animations from file
+ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount)
+{
+    ModelAnimation *animations = NULL;
+
+#if defined(SUPPORT_FILEFORMAT_IQM)
+    if (IsFileExtension(fileName, ".iqm")) animations = LoadModelAnimationsIQM(fileName, animCount);
+#endif
+#if defined(SUPPORT_FILEFORMAT_M3D)
+    if (IsFileExtension(fileName, ".m3d")) animations = LoadModelAnimationsM3D(fileName, animCount);
+#endif
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+    if (IsFileExtension(fileName, ".gltf;.glb")) animations = LoadModelAnimationsGLTF(fileName, animCount);
+#endif
+
+    return animations;
+}
+
+// Update model animated bones transform matrices for a given frame
+// NOTE: Updated data is not uploaded to GPU but kept at model.meshes[i].boneMatrices[boneId],
+// to be uploaded to shader at drawing, in case GPU skinning is enabled
+void UpdateModelAnimationBones(Model model, ModelAnimation anim, int frame)
+{
+    if ((anim.frameCount > 0) && (anim.bones != NULL) && (anim.framePoses != NULL))
+    {
+        if (frame >= anim.frameCount) frame = frame%anim.frameCount;
+
+        // Get first mesh which have bones
+        int firstMeshWithBones = -1;
+
+        for (int i = 0; i < model.meshCount; i++)
+        {
+            if (model.meshes[i].boneMatrices)
+            {
+                if (firstMeshWithBones == -1)
+                {
+                    firstMeshWithBones = i;
+                    break;
+                }
+            }
+        }
+
+        if (firstMeshWithBones != -1)
+        {
+            // Update all bones and boneMatrices of first mesh with bones
+            for (int boneId = 0; boneId < anim.boneCount; boneId++)
+            {
+                Transform *bindTransform = &model.bindPose[boneId];
+                Matrix bindMatrix = MatrixMultiply(MatrixMultiply(
+                    MatrixScale(bindTransform->scale.x, bindTransform->scale.y, bindTransform->scale.z),
+                    QuaternionToMatrix(bindTransform->rotation)),
+                    MatrixTranslate(bindTransform->translation.x, bindTransform->translation.y, bindTransform->translation.z));
+
+                Transform *targetTransform = &anim.framePoses[frame][boneId];
+                Matrix targetMatrix = MatrixMultiply(MatrixMultiply(
+                    MatrixScale(targetTransform->scale.x, targetTransform->scale.y, targetTransform->scale.z),
+                    QuaternionToMatrix(targetTransform->rotation)),
+                    MatrixTranslate(targetTransform->translation.x, targetTransform->translation.y, targetTransform->translation.z));
+
+                model.meshes[firstMeshWithBones].boneMatrices[boneId] = MatrixMultiply(MatrixInvert(bindMatrix), targetMatrix);
+            }
+
+            // Update remaining meshes with bones
+            // NOTE: Using deep copy because shallow copy results in double free with 'UnloadModel()'
+            for (int i = firstMeshWithBones + 1; i < model.meshCount; i++)
+            {
+                if (model.meshes[i].boneMatrices)
+                {
+                    memcpy(model.meshes[i].boneMatrices,
+                        model.meshes[firstMeshWithBones].boneMatrices,
+                        model.meshes[i].boneCount*sizeof(model.meshes[i].boneMatrices[0]));
+                }
+            }
+        }
+    }
+}
+
+// at least 2x speed up vs the old method
+// Update model animated vertex data (positions and normals) for a given frame
+// NOTE: Updated data is uploaded to GPU
+void UpdateModelAnimation(Model model, ModelAnimation anim, int frame)
+{
+    UpdateModelAnimationBones(model,anim,frame);
+
+    for (int m = 0; m < model.meshCount; m++)
+    {
+        Mesh mesh = model.meshes[m];
+        Vector3 animVertex = { 0 };
+        Vector3 animNormal = { 0 };
+        int boneId = 0;
+        int boneCounter = 0;
+        float boneWeight = 0.0;
+        bool updated = false; // Flag to check when anim vertex information is updated
+        const int vValues = mesh.vertexCount*3;
+
+        // Skip if missing bone data, causes segfault without on some models
+        if ((mesh.boneWeights == NULL) || (mesh.boneIds == NULL)) continue;
+
+        for (int vCounter = 0; vCounter < vValues; vCounter += 3)
+        {
+            mesh.animVertices[vCounter] = 0;
+            mesh.animVertices[vCounter + 1] = 0;
+            mesh.animVertices[vCounter + 2] = 0;
+            if (mesh.animNormals != NULL)
+            {
+                mesh.animNormals[vCounter] = 0;
+                mesh.animNormals[vCounter + 1] = 0;
+                mesh.animNormals[vCounter + 2] = 0;
+            }
+
+            // Iterates over 4 bones per vertex
+            for (int j = 0; j < 4; j++, boneCounter++)
+            {
+                boneWeight = mesh.boneWeights[boneCounter];
+                boneId = mesh.boneIds[boneCounter];
+
+                // Early stop when no transformation will be applied
+                if (boneWeight == 0.0f) continue;
+                animVertex = (Vector3){ mesh.vertices[vCounter], mesh.vertices[vCounter + 1], mesh.vertices[vCounter + 2] };
+                animVertex = Vector3Transform(animVertex,model.meshes[m].boneMatrices[boneId]);
+                mesh.animVertices[vCounter] += animVertex.x*boneWeight;
+                mesh.animVertices[vCounter+1] += animVertex.y*boneWeight;
+                mesh.animVertices[vCounter+2] += animVertex.z*boneWeight;
+                updated = true;
+
+                // Normals processing
+                // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
+                if ((mesh.normals != NULL) && (mesh.animNormals != NULL ))
+                {
+                    animNormal = (Vector3){ mesh.normals[vCounter], mesh.normals[vCounter + 1], mesh.normals[vCounter + 2] };
+                    animNormal = Vector3Transform(animNormal, MatrixTranspose(MatrixInvert(model.meshes[m].boneMatrices[boneId])));
+                    mesh.animNormals[vCounter] += animNormal.x*boneWeight;
+                    mesh.animNormals[vCounter + 1] += animNormal.y*boneWeight;
+                    mesh.animNormals[vCounter + 2] += animNormal.z*boneWeight;
+                }
+            }
+        }
+
+        if (updated)
+        {
+            rlUpdateVertexBuffer(mesh.vboId[0], mesh.animVertices, mesh.vertexCount*3*sizeof(float), 0); // Update vertex position
+            if (mesh.normals != NULL) rlUpdateVertexBuffer(mesh.vboId[2], mesh.animNormals, mesh.vertexCount*3*sizeof(float), 0); // Update vertex normals
+        }
+    }
+}
+
+// Unload animation array data
+void UnloadModelAnimations(ModelAnimation *animations, int animCount)
+{
+    for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]);
+    RL_FREE(animations);
+}
+
+// Unload animation data
+void UnloadModelAnimation(ModelAnimation anim)
+{
+    for (int i = 0; i < anim.frameCount; i++) RL_FREE(anim.framePoses[i]);
+
+    RL_FREE(anim.bones);
+    RL_FREE(anim.framePoses);
+}
+
+// Check model animation skeleton match
+// NOTE: Only number of bones and parent connections are checked
+bool IsModelAnimationValid(Model model, ModelAnimation anim)
+{
+    int result = true;
+
+    if (model.boneCount != anim.boneCount) result = false;
+    else
+    {
+        for (int i = 0; i < model.boneCount; i++)
+        {
+            if (model.bones[i].parent != anim.bones[i].parent) { result = false; break; }
+        }
+    }
+
+    return result;
+}
+
+#if defined(SUPPORT_MESH_GENERATION)
+// Generate polygonal mesh
+Mesh GenMeshPoly(int sides, float radius)
+{
+    Mesh mesh = { 0 };
+
+    if (sides < 3) return mesh; // Security check
+
+    int vertexCount = sides*3;
+
+    // Vertices definition
+    Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
+
+    float d = 0.0f, dStep = 360.0f/sides;
+    for (int v = 0; v < vertexCount - 2; v += 3)
+    {
+        vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f };
+        vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius };
+        vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius };
+        d += dStep;
+    }
+
+    // Normals definition
+    Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
+    for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
+
+    // TexCoords definition
+    Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
+    for (int n = 0; n < vertexCount; n++) texcoords[n] = (Vector2){ 0.0f, 0.0f };
+
+    mesh.vertexCount = vertexCount;
+    mesh.triangleCount = sides;
+    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+
+    // Mesh vertices position array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.vertices[3*i] = vertices[i].x;
+        mesh.vertices[3*i + 1] = vertices[i].y;
+        mesh.vertices[3*i + 2] = vertices[i].z;
+    }
+
+    // Mesh texcoords array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.texcoords[2*i] = texcoords[i].x;
+        mesh.texcoords[2*i + 1] = texcoords[i].y;
+    }
+
+    // Mesh normals array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.normals[3*i] = normals[i].x;
+        mesh.normals[3*i + 1] = normals[i].y;
+        mesh.normals[3*i + 2] = normals[i].z;
+    }
+
+    RL_FREE(vertices);
+    RL_FREE(normals);
+    RL_FREE(texcoords);
+
+    // Upload vertex data to GPU (static mesh)
+    // NOTE: mesh.vboId array is allocated inside UploadMesh()
+    UploadMesh(&mesh, false);
+
+    return mesh;
+}
+
+// Generate plane mesh (with subdivisions)
+Mesh GenMeshPlane(float width, float length, int resX, int resZ)
+{
+    Mesh mesh = { 0 };
+
+#define CUSTOM_MESH_GEN_PLANE
+#if defined(CUSTOM_MESH_GEN_PLANE)
+    resX++;
+    resZ++;
+
+    // Vertices definition
+    int vertexCount = resX*resZ; // vertices get reused for the faces
+
+    Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
+    for (int z = 0; z < resZ; z++)
+    {
+        // [-length/2, length/2]
+        float zPos = ((float)z/(resZ - 1) - 0.5f)*length;
+        for (int x = 0; x < resX; x++)
+        {
+            // [-width/2, width/2]
+            float xPos = ((float)x/(resX - 1) - 0.5f)*width;
+            vertices[x + z*resX] = (Vector3){ xPos, 0.0f, zPos };
+        }
+    }
+
+    // Normals definition
+    Vector3 *normals = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3));
+    for (int n = 0; n < vertexCount; n++) normals[n] = (Vector3){ 0.0f, 1.0f, 0.0f };   // Vector3.up;
+
+    // TexCoords definition
+    Vector2 *texcoords = (Vector2 *)RL_MALLOC(vertexCount*sizeof(Vector2));
+    for (int v = 0; v < resZ; v++)
+    {
+        for (int u = 0; u < resX; u++)
+        {
+            texcoords[u + v*resX] = (Vector2){ (float)u/(resX - 1), (float)v/(resZ - 1) };
+        }
+    }
+
+    // Triangles definition (indices)
+    int numFaces = (resX - 1)*(resZ - 1);
+    int *triangles = (int *)RL_MALLOC(numFaces*6*sizeof(int));
+    int t = 0;
+    for (int face = 0; face < numFaces; face++)
+    {
+        // Retrieve lower left corner from face ind
+        int i = face + face/(resX - 1);
+
+        triangles[t++] = i + resX;
+        triangles[t++] = i + 1;
+        triangles[t++] = i;
+
+        triangles[t++] = i + resX;
+        triangles[t++] = i + resX + 1;
+        triangles[t++] = i + 1;
+    }
+
+    mesh.vertexCount = vertexCount;
+    mesh.triangleCount = numFaces*2;
+    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.indices = (unsigned short *)RL_MALLOC(mesh.triangleCount*3*sizeof(unsigned short));
+
+    // Mesh vertices position array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.vertices[3*i] = vertices[i].x;
+        mesh.vertices[3*i + 1] = vertices[i].y;
+        mesh.vertices[3*i + 2] = vertices[i].z;
+    }
+
+    // Mesh texcoords array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.texcoords[2*i] = texcoords[i].x;
+        mesh.texcoords[2*i + 1] = texcoords[i].y;
+    }
+
+    // Mesh normals array
+    for (int i = 0; i < mesh.vertexCount; i++)
+    {
+        mesh.normals[3*i] = normals[i].x;
+        mesh.normals[3*i + 1] = normals[i].y;
+        mesh.normals[3*i + 2] = normals[i].z;
+    }
+
+    // Mesh indices array initialization
+    for (int i = 0; i < mesh.triangleCount*3; i++) mesh.indices[i] = triangles[i];
+
+    RL_FREE(vertices);
+    RL_FREE(normals);
+    RL_FREE(texcoords);
+    RL_FREE(triangles);
+
+#else       // Use par_shapes library to generate plane mesh
+
+    par_shapes_mesh *plane = par_shapes_create_plane(resX, resZ);   // No normals/texcoords generated!!!
+    par_shapes_scale(plane, width, length, 1.0f);
+    par_shapes_rotate(plane, -PI/2.0f, (float[]){ 1, 0, 0 });
+    par_shapes_translate(plane, -width/2, 0.0f, length/2);
+
+    mesh.vertices = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(plane->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(plane->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = plane->ntriangles*3;
+    mesh.triangleCount = plane->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = plane->points[plane->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = plane->points[plane->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = plane->points[plane->triangles[k]*3 + 2];
+
+        mesh.normals[k*3] = plane->normals[plane->triangles[k]*3];
+        mesh.normals[k*3 + 1] = plane->normals[plane->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = plane->normals[plane->triangles[k]*3 + 2];
+
+        mesh.texcoords[k*2] = plane->tcoords[plane->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = plane->tcoords[plane->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(plane);
+#endif
+
+    // Upload vertex data to GPU (static mesh)
+    UploadMesh(&mesh, false);
+
+    return mesh;
+}
+
+// Generated cuboid mesh
+Mesh GenMeshCube(float width, float height, float length)
+{
+    Mesh mesh = { 0 };
+
+#define CUSTOM_MESH_GEN_CUBE
+#if defined(CUSTOM_MESH_GEN_CUBE)
+    float vertices[] = {
+        -width/2, -height/2, length/2,
+        width/2, -height/2, length/2,
+        width/2, height/2, length/2,
+        -width/2, height/2, length/2,
+        -width/2, -height/2, -length/2,
+        -width/2, height/2, -length/2,
+        width/2, height/2, -length/2,
+        width/2, -height/2, -length/2,
+        -width/2, height/2, -length/2,
+        -width/2, height/2, length/2,
+        width/2, height/2, length/2,
+        width/2, height/2, -length/2,
+        -width/2, -height/2, -length/2,
+        width/2, -height/2, -length/2,
+        width/2, -height/2, length/2,
+        -width/2, -height/2, length/2,
+        width/2, -height/2, -length/2,
+        width/2, height/2, -length/2,
+        width/2, height/2, length/2,
+        width/2, -height/2, length/2,
+        -width/2, -height/2, -length/2,
+        -width/2, -height/2, length/2,
+        -width/2, height/2, length/2,
+        -width/2, height/2, -length/2
+    };
+
+    float texcoords[] = {
+        0.0f, 0.0f,
+        1.0f, 0.0f,
+        1.0f, 1.0f,
+        0.0f, 1.0f,
+        1.0f, 0.0f,
+        1.0f, 1.0f,
+        0.0f, 1.0f,
+        0.0f, 0.0f,
+        0.0f, 1.0f,
+        0.0f, 0.0f,
+        1.0f, 0.0f,
+        1.0f, 1.0f,
+        1.0f, 1.0f,
+        0.0f, 1.0f,
+        0.0f, 0.0f,
+        1.0f, 0.0f,
+        1.0f, 0.0f,
+        1.0f, 1.0f,
+        0.0f, 1.0f,
+        0.0f, 0.0f,
+        0.0f, 0.0f,
+        1.0f, 0.0f,
+        1.0f, 1.0f,
+        0.0f, 1.0f
+    };
+
+    float normals[] = {
+        0.0f, 0.0f, 1.0f,
+        0.0f, 0.0f, 1.0f,
+        0.0f, 0.0f, 1.0f,
+        0.0f, 0.0f, 1.0f,
+        0.0f, 0.0f,-1.0f,
+        0.0f, 0.0f,-1.0f,
+        0.0f, 0.0f,-1.0f,
+        0.0f, 0.0f,-1.0f,
+        0.0f, 1.0f, 0.0f,
+        0.0f, 1.0f, 0.0f,
+        0.0f, 1.0f, 0.0f,
+        0.0f, 1.0f, 0.0f,
+        0.0f,-1.0f, 0.0f,
+        0.0f,-1.0f, 0.0f,
+        0.0f,-1.0f, 0.0f,
+        0.0f,-1.0f, 0.0f,
+        1.0f, 0.0f, 0.0f,
+        1.0f, 0.0f, 0.0f,
+        1.0f, 0.0f, 0.0f,
+        1.0f, 0.0f, 0.0f,
+        -1.0f, 0.0f, 0.0f,
+        -1.0f, 0.0f, 0.0f,
+        -1.0f, 0.0f, 0.0f,
+        -1.0f, 0.0f, 0.0f
+    };
+
+    mesh.vertices = (float *)RL_MALLOC(24*3*sizeof(float));
+    memcpy(mesh.vertices, vertices, 24*3*sizeof(float));
+
+    mesh.texcoords = (float *)RL_MALLOC(24*2*sizeof(float));
+    memcpy(mesh.texcoords, texcoords, 24*2*sizeof(float));
+
+    mesh.normals = (float *)RL_MALLOC(24*3*sizeof(float));
+    memcpy(mesh.normals, normals, 24*3*sizeof(float));
+
+    mesh.indices = (unsigned short *)RL_MALLOC(36*sizeof(unsigned short));
+
+    int k = 0;
+
+    // Indices can be initialized right now
+    for (int i = 0; i < 36; i += 6)
+    {
+        mesh.indices[i] = 4*k;
+        mesh.indices[i + 1] = 4*k + 1;
+        mesh.indices[i + 2] = 4*k + 2;
+        mesh.indices[i + 3] = 4*k;
+        mesh.indices[i + 4] = 4*k + 2;
+        mesh.indices[i + 5] = 4*k + 3;
+
+        k++;
+    }
+
+    mesh.vertexCount = 24;
+    mesh.triangleCount = 12;
+
+#else               // Use par_shapes library to generate cube mesh
+/*
+// Platonic solids:
+par_shapes_mesh *par_shapes_create_tetrahedron();       // 4 sides polyhedron (pyramid)
+par_shapes_mesh *par_shapes_create_cube();              // 6 sides polyhedron (cube)
+par_shapes_mesh *par_shapes_create_octahedron();        // 8 sides polyhedron (diamond)
+par_shapes_mesh *par_shapes_create_dodecahedron();      // 12 sides polyhedron
+par_shapes_mesh *par_shapes_create_icosahedron();       // 20 sides polyhedron
+*/
+    // Platonic solid generation: cube (6 sides)
+    // NOTE: No normals/texcoords generated by default
+    par_shapes_mesh *cube = par_shapes_create_cube();
+    cube->tcoords = PAR_MALLOC(float, 2*cube->npoints);
+    for (int i = 0; i < 2*cube->npoints; i++) cube->tcoords[i] = 0.0f;
+    par_shapes_scale(cube, width, height, length);
+    par_shapes_translate(cube, -width/2, 0.0f, -length/2);
+    par_shapes_compute_normals(cube);
+
+    mesh.vertices = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(cube->ntriangles*3*2*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(cube->ntriangles*3*3*sizeof(float));
+
+    mesh.vertexCount = cube->ntriangles*3;
+    mesh.triangleCount = cube->ntriangles;
+
+    for (int k = 0; k < mesh.vertexCount; k++)
+    {
+        mesh.vertices[k*3] = cube->points[cube->triangles[k]*3];
+        mesh.vertices[k*3 + 1] = cube->points[cube->triangles[k]*3 + 1];
+        mesh.vertices[k*3 + 2] = cube->points[cube->triangles[k]*3 + 2];
+
+        mesh.normals[k*3] = cube->normals[cube->triangles[k]*3];
+        mesh.normals[k*3 + 1] = cube->normals[cube->triangles[k]*3 + 1];
+        mesh.normals[k*3 + 2] = cube->normals[cube->triangles[k]*3 + 2];
+
+        mesh.texcoords[k*2] = cube->tcoords[cube->triangles[k]*2];
+        mesh.texcoords[k*2 + 1] = cube->tcoords[cube->triangles[k]*2 + 1];
+    }
+
+    par_shapes_free_mesh(cube);
+#endif
+
+    // Upload vertex data to GPU (static mesh)
+    UploadMesh(&mesh, false);
+
+    return mesh;
+}
+
+// Generate sphere mesh (standard sphere)
+Mesh GenMeshSphere(float radius, int rings, int slices)
+{
+    Mesh mesh = { 0 };
+
+    if ((rings >= 3) && (slices >= 3))
+    {
+        par_shapes_set_epsilon_degenerate_sphere(0.0);
+        par_shapes_mesh *sphere = par_shapes_create_parametric_sphere(slices, rings);
+        par_shapes_scale(sphere, radius, radius, radius);
+        // NOTE: Soft normals are computed internally
+
+        mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = sphere->ntriangles*3;
+        mesh.triangleCount = sphere->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
+            mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(sphere);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: sphere");
+
+    return mesh;
+}
+
+// Generate hemisphere mesh (half sphere, no bottom cap)
+Mesh GenMeshHemiSphere(float radius, int rings, int slices)
+{
+    Mesh mesh = { 0 };
+
+    if ((rings >= 3) && (slices >= 3))
+    {
+        if (radius < 0.0f) radius = 0.0f;
+
+        par_shapes_mesh *sphere = par_shapes_create_hemisphere(slices, rings);
+        par_shapes_scale(sphere, radius, radius, radius);
+        // NOTE: Soft normals are computed internally
+
+        mesh.vertices = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(sphere->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(sphere->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = sphere->ntriangles*3;
+        mesh.triangleCount = sphere->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = sphere->points[sphere->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = sphere->points[sphere->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = sphere->points[sphere->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = sphere->normals[sphere->triangles[k]*3];
+            mesh.normals[k*3 + 1] = sphere->normals[sphere->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = sphere->normals[sphere->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = sphere->tcoords[sphere->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = sphere->tcoords[sphere->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(sphere);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: hemisphere");
+
+    return mesh;
+}
+
+// Generate cylinder mesh
+Mesh GenMeshCylinder(float radius, float height, int slices)
+{
+    Mesh mesh = { 0 };
+
+    if (slices >= 3)
+    {
+        // Instance a cylinder that sits on the Z=0 plane using the given tessellation
+        // levels across the UV domain.  Think of "slices" like a number of pizza
+        // slices, and "stacks" like a number of stacked rings
+        // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
+        par_shapes_mesh *cylinder = par_shapes_create_cylinder(slices, 8);
+        par_shapes_scale(cylinder, radius, radius, height);
+        par_shapes_rotate(cylinder, -PI/2.0f, (float[]){ 1, 0, 0 });
+
+        // Generate an orientable disk shape (top cap)
+        par_shapes_mesh *capTop = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, 1 });
+        capTop->tcoords = PAR_MALLOC(float, 2*capTop->npoints);
+        for (int i = 0; i < 2*capTop->npoints; i++) capTop->tcoords[i] = 0.0f;
+        par_shapes_rotate(capTop, -PI/2.0f, (float[]){ 1, 0, 0 });
+        par_shapes_rotate(capTop, 90*DEG2RAD, (float[]){ 0, 1, 0 });
+        par_shapes_translate(capTop, 0, height, 0);
+
+        // Generate an orientable disk shape (bottom cap)
+        par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
+        capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
+        for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
+        par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
+        par_shapes_rotate(capBottom, -90*DEG2RAD, (float[]){ 0, 1, 0 });
+
+        par_shapes_merge_and_free(cylinder, capTop);
+        par_shapes_merge_and_free(cylinder, capBottom);
+
+        mesh.vertices = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(cylinder->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(cylinder->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = cylinder->ntriangles*3;
+        mesh.triangleCount = cylinder->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = cylinder->points[cylinder->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = cylinder->points[cylinder->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = cylinder->points[cylinder->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = cylinder->normals[cylinder->triangles[k]*3];
+            mesh.normals[k*3 + 1] = cylinder->normals[cylinder->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = cylinder->normals[cylinder->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = cylinder->tcoords[cylinder->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = cylinder->tcoords[cylinder->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(cylinder);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cylinder");
+
+    return mesh;
+}
+
+// Generate cone/pyramid mesh
+Mesh GenMeshCone(float radius, float height, int slices)
+{
+    Mesh mesh = { 0 };
+
+    if (slices >= 3)
+    {
+        // Instance a cone that sits on the Z=0 plane using the given tessellation
+        // levels across the UV domain.  Think of "slices" like a number of pizza
+        // slices, and "stacks" like a number of stacked rings
+        // Height and radius are both 1.0, but they can easily be changed with par_shapes_scale
+        par_shapes_mesh *cone = par_shapes_create_cone(slices, 8);
+        par_shapes_scale(cone, radius, radius, height);
+        par_shapes_rotate(cone, -PI/2.0f, (float[]){ 1, 0, 0 });
+        par_shapes_rotate(cone, PI/2.0f, (float[]){ 0, 1, 0 });
+
+        // Generate an orientable disk shape (bottom cap)
+        par_shapes_mesh *capBottom = par_shapes_create_disk(radius, slices, (float[]){ 0, 0, 0 }, (float[]){ 0, 0, -1 });
+        capBottom->tcoords = PAR_MALLOC(float, 2*capBottom->npoints);
+        for (int i = 0; i < 2*capBottom->npoints; i++) capBottom->tcoords[i] = 0.95f;
+        par_shapes_rotate(capBottom, PI/2.0f, (float[]){ 1, 0, 0 });
+
+        par_shapes_merge_and_free(cone, capBottom);
+
+        mesh.vertices = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(cone->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(cone->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = cone->ntriangles*3;
+        mesh.triangleCount = cone->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = cone->points[cone->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = cone->points[cone->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = cone->points[cone->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = cone->normals[cone->triangles[k]*3];
+            mesh.normals[k*3 + 1] = cone->normals[cone->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = cone->normals[cone->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = cone->tcoords[cone->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = cone->tcoords[cone->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(cone);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: cone");
+
+    return mesh;
+}
+
+// Generate torus mesh
+Mesh GenMeshTorus(float radius, float size, int radSeg, int sides)
+{
+    Mesh mesh = { 0 };
+
+    if ((sides >= 3) && (radSeg >= 3))
+    {
+        if (radius > 1.0f) radius = 1.0f;
+        else if (radius < 0.1f) radius = 0.1f;
+
+        // Create a donut that sits on the Z=0 plane with the specified inner radius
+        // The outer radius can be controlled with par_shapes_scale
+        par_shapes_mesh *torus = par_shapes_create_torus(radSeg, sides, radius);
+        par_shapes_scale(torus, size/2, size/2, size/2);
+
+        mesh.vertices = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(torus->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(torus->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = torus->ntriangles*3;
+        mesh.triangleCount = torus->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = torus->points[torus->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = torus->points[torus->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = torus->points[torus->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = torus->normals[torus->triangles[k]*3];
+            mesh.normals[k*3 + 1] = torus->normals[torus->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = torus->normals[torus->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = torus->tcoords[torus->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = torus->tcoords[torus->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(torus);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: torus");
+
+    return mesh;
+}
+
+// Generate trefoil knot mesh
+Mesh GenMeshKnot(float radius, float size, int radSeg, int sides)
+{
+    Mesh mesh = { 0 };
+
+    if ((sides >= 3) && (radSeg >= 3))
+    {
+        if (radius > 3.0f) radius = 3.0f;
+        else if (radius < 0.5f) radius = 0.5f;
+
+        par_shapes_mesh *knot = par_shapes_create_trefoil_knot(radSeg, sides, radius);
+        par_shapes_scale(knot, size, size, size);
+
+        mesh.vertices = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
+        mesh.texcoords = (float *)RL_MALLOC(knot->ntriangles*3*2*sizeof(float));
+        mesh.normals = (float *)RL_MALLOC(knot->ntriangles*3*3*sizeof(float));
+
+        mesh.vertexCount = knot->ntriangles*3;
+        mesh.triangleCount = knot->ntriangles;
+
+        for (int k = 0; k < mesh.vertexCount; k++)
+        {
+            mesh.vertices[k*3] = knot->points[knot->triangles[k]*3];
+            mesh.vertices[k*3 + 1] = knot->points[knot->triangles[k]*3 + 1];
+            mesh.vertices[k*3 + 2] = knot->points[knot->triangles[k]*3 + 2];
+
+            mesh.normals[k*3] = knot->normals[knot->triangles[k]*3];
+            mesh.normals[k*3 + 1] = knot->normals[knot->triangles[k]*3 + 1];
+            mesh.normals[k*3 + 2] = knot->normals[knot->triangles[k]*3 + 2];
+
+            mesh.texcoords[k*2] = knot->tcoords[knot->triangles[k]*2];
+            mesh.texcoords[k*2 + 1] = knot->tcoords[knot->triangles[k]*2 + 1];
+        }
+
+        par_shapes_free_mesh(knot);
+
+        // Upload vertex data to GPU (static mesh)
+        UploadMesh(&mesh, false);
+    }
+    else TRACELOG(LOG_WARNING, "MESH: Failed to generate mesh: knot");
+
+    return mesh;
+}
+
+// Generate a mesh from heightmap
+// NOTE: Vertex data is uploaded to GPU
+Mesh GenMeshHeightmap(Image heightmap, Vector3 size)
+{
+    #define GRAY_VALUE(c) ((float)(c.r + c.g + c.b)/3.0f)
+
+    Mesh mesh = { 0 };
+
+    int mapX = heightmap.width;
+    int mapZ = heightmap.height;
+
+    Color *pixels = LoadImageColors(heightmap);
+
+    // NOTE: One vertex per pixel
+    mesh.triangleCount = (mapX - 1)*(mapZ - 1)*2;    // One quad every four pixels
+
+    mesh.vertexCount = mesh.triangleCount*3;
+
+    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
+    mesh.colors = NULL;
+
+    int vCounter = 0;       // Used to count vertices float by float
+    int tcCounter = 0;      // Used to count texcoords float by float
+    int nCounter = 0;       // Used to count normals float by float
+
+    Vector3 scaleFactor = { size.x/(mapX - 1), size.y/255.0f, size.z/(mapZ - 1) };
+
+    Vector3 vA = { 0 };
+    Vector3 vB = { 0 };
+    Vector3 vC = { 0 };
+    Vector3 vN = { 0 };
+
+    for (int z = 0; z < mapZ-1; z++)
+    {
+        for (int x = 0; x < mapX-1; x++)
+        {
+            // Fill vertices array with data
+            //----------------------------------------------------------
+
+            // one triangle - 3 vertex
+            mesh.vertices[vCounter] = (float)x*scaleFactor.x;
+            mesh.vertices[vCounter + 1] = GRAY_VALUE(pixels[x + z*mapX])*scaleFactor.y;
+            mesh.vertices[vCounter + 2] = (float)z*scaleFactor.z;
+
+            mesh.vertices[vCounter + 3] = (float)x*scaleFactor.x;
+            mesh.vertices[vCounter + 4] = GRAY_VALUE(pixels[x + (z + 1)*mapX])*scaleFactor.y;
+            mesh.vertices[vCounter + 5] = (float)(z + 1)*scaleFactor.z;
+
+            mesh.vertices[vCounter + 6] = (float)(x + 1)*scaleFactor.x;
+            mesh.vertices[vCounter + 7] = GRAY_VALUE(pixels[(x + 1) + z*mapX])*scaleFactor.y;
+            mesh.vertices[vCounter + 8] = (float)z*scaleFactor.z;
+
+            // Another triangle - 3 vertex
+            mesh.vertices[vCounter + 9] = mesh.vertices[vCounter + 6];
+            mesh.vertices[vCounter + 10] = mesh.vertices[vCounter + 7];
+            mesh.vertices[vCounter + 11] = mesh.vertices[vCounter + 8];
+
+            mesh.vertices[vCounter + 12] = mesh.vertices[vCounter + 3];
+            mesh.vertices[vCounter + 13] = mesh.vertices[vCounter + 4];
+            mesh.vertices[vCounter + 14] = mesh.vertices[vCounter + 5];
+
+            mesh.vertices[vCounter + 15] = (float)(x + 1)*scaleFactor.x;
+            mesh.vertices[vCounter + 16] = GRAY_VALUE(pixels[(x + 1) + (z + 1)*mapX])*scaleFactor.y;
+            mesh.vertices[vCounter + 17] = (float)(z + 1)*scaleFactor.z;
+            vCounter += 18;     // 6 vertex, 18 floats
+
+            // Fill texcoords array with data
+            //--------------------------------------------------------------
+            mesh.texcoords[tcCounter] = (float)x/(mapX - 1);
+            mesh.texcoords[tcCounter + 1] = (float)z/(mapZ - 1);
+
+            mesh.texcoords[tcCounter + 2] = (float)x/(mapX - 1);
+            mesh.texcoords[tcCounter + 3] = (float)(z + 1)/(mapZ - 1);
+
+            mesh.texcoords[tcCounter + 4] = (float)(x + 1)/(mapX - 1);
+            mesh.texcoords[tcCounter + 5] = (float)z/(mapZ - 1);
+
+            mesh.texcoords[tcCounter + 6] = mesh.texcoords[tcCounter + 4];
+            mesh.texcoords[tcCounter + 7] = mesh.texcoords[tcCounter + 5];
+
+            mesh.texcoords[tcCounter + 8] = mesh.texcoords[tcCounter + 2];
+            mesh.texcoords[tcCounter + 9] = mesh.texcoords[tcCounter + 3];
+
+            mesh.texcoords[tcCounter + 10] = (float)(x + 1)/(mapX - 1);
+            mesh.texcoords[tcCounter + 11] = (float)(z + 1)/(mapZ - 1);
+            tcCounter += 12;    // 6 texcoords, 12 floats
+
+            // Fill normals array with data
+            //--------------------------------------------------------------
+            for (int i = 0; i < 18; i += 9)
+            {
+                vA.x = mesh.vertices[nCounter + i];
+                vA.y = mesh.vertices[nCounter + i + 1];
+                vA.z = mesh.vertices[nCounter + i + 2];
+
+                vB.x = mesh.vertices[nCounter + i + 3];
+                vB.y = mesh.vertices[nCounter + i + 4];
+                vB.z = mesh.vertices[nCounter + i + 5];
+
+                vC.x = mesh.vertices[nCounter + i + 6];
+                vC.y = mesh.vertices[nCounter + i + 7];
+                vC.z = mesh.vertices[nCounter + i + 8];
+
+                vN = Vector3Normalize(Vector3CrossProduct(Vector3Subtract(vB, vA), Vector3Subtract(vC, vA)));
+
+                mesh.normals[nCounter + i] = vN.x;
+                mesh.normals[nCounter + i + 1] = vN.y;
+                mesh.normals[nCounter + i + 2] = vN.z;
+
+                mesh.normals[nCounter + i + 3] = vN.x;
+                mesh.normals[nCounter + i + 4] = vN.y;
+                mesh.normals[nCounter + i + 5] = vN.z;
+
+                mesh.normals[nCounter + i + 6] = vN.x;
+                mesh.normals[nCounter + i + 7] = vN.y;
+                mesh.normals[nCounter + i + 8] = vN.z;
+            }
+
+            nCounter += 18;     // 6 vertex, 18 floats
+        }
+    }
+
+    UnloadImageColors(pixels);  // Unload pixels color data
+
+    // Upload vertex data to GPU (static mesh)
+    UploadMesh(&mesh, false);
+
+    return mesh;
+}
+
+// Generate a cubes mesh from pixel data
+// NOTE: Vertex data is uploaded to GPU
+Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize)
+{
+    #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
+
+    Mesh mesh = { 0 };
+
+    Color *pixels = LoadImageColors(cubicmap);
+
+    // NOTE: Max possible number of triangles numCubes*(12 triangles by cube)
+    int maxTriangles = cubicmap.width*cubicmap.height*12;
+
+    int vCounter = 0;       // Used to count vertices
+    int tcCounter = 0;      // Used to count texcoords
+    int nCounter = 0;       // Used to count normals
+
+    float w = cubeSize.x;
+    float h = cubeSize.z;
+    float h2 = cubeSize.y;
+
+    Vector3 *mapVertices = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
+    Vector2 *mapTexcoords = (Vector2 *)RL_MALLOC(maxTriangles*3*sizeof(Vector2));
+    Vector3 *mapNormals = (Vector3 *)RL_MALLOC(maxTriangles*3*sizeof(Vector3));
+
+    // Define the 6 normals of the cube, we will combine them accordingly later...
+    Vector3 n1 = { 1.0f, 0.0f, 0.0f };
+    Vector3 n2 = { -1.0f, 0.0f, 0.0f };
+    Vector3 n3 = { 0.0f, 1.0f, 0.0f };
+    Vector3 n4 = { 0.0f, -1.0f, 0.0f };
+    Vector3 n5 = { 0.0f, 0.0f, -1.0f };
+    Vector3 n6 = { 0.0f, 0.0f, 1.0f };
+
+    // NOTE: We use texture rectangles to define different textures for top-bottom-front-back-right-left (6)
+    typedef struct RectangleF {
+        float x;
+        float y;
+        float width;
+        float height;
+    } RectangleF;
+
+    RectangleF rightTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
+    RectangleF leftTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
+    RectangleF frontTexUV = { 0.0f, 0.0f, 0.5f, 0.5f };
+    RectangleF backTexUV = { 0.5f, 0.0f, 0.5f, 0.5f };
+    RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f };
+    RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f };
+
+    for (int z = 0; z < cubicmap.height; ++z)
+    {
+        for (int x = 0; x < cubicmap.width; x++)
+        {
+            // Define the 8 vertex of the cube, we will combine them accordingly later...
+            Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) };
+            Vector3 v2 = { w*(x - 0.5f), h2, h*(z + 0.5f) };
+            Vector3 v3 = { w*(x + 0.5f), h2, h*(z + 0.5f) };
+            Vector3 v4 = { w*(x + 0.5f), h2, h*(z - 0.5f) };
+            Vector3 v5 = { w*(x + 0.5f), 0, h*(z - 0.5f) };
+            Vector3 v6 = { w*(x - 0.5f), 0, h*(z - 0.5f) };
+            Vector3 v7 = { w*(x - 0.5f), 0, h*(z + 0.5f) };
+            Vector3 v8 = { w*(x + 0.5f), 0, h*(z + 0.5f) };
+
+            // We check pixel color to be WHITE -> draw full cube
+            if (COLOR_EQUAL(pixels[z*cubicmap.width + x], WHITE))
+            {
+                // Define triangles and checking collateral cubes
+                //------------------------------------------------
+
+                // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
+                // WARNING: Not required for a WHITE cubes, created to allow seeing the map from outside
+                mapVertices[vCounter] = v1;
+                mapVertices[vCounter + 1] = v2;
+                mapVertices[vCounter + 2] = v3;
+                mapVertices[vCounter + 3] = v1;
+                mapVertices[vCounter + 4] = v3;
+                mapVertices[vCounter + 5] = v4;
+                vCounter += 6;
+
+                mapNormals[nCounter] = n3;
+                mapNormals[nCounter + 1] = n3;
+                mapNormals[nCounter + 2] = n3;
+                mapNormals[nCounter + 3] = n3;
+                mapNormals[nCounter + 4] = n3;
+                mapNormals[nCounter + 5] = n3;
+                nCounter += 6;
+
+                mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
+                mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
+                mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
+                mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
+                mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
+                mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
+                tcCounter += 6;
+
+                // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
+                mapVertices[vCounter] = v6;
+                mapVertices[vCounter + 1] = v8;
+                mapVertices[vCounter + 2] = v7;
+                mapVertices[vCounter + 3] = v6;
+                mapVertices[vCounter + 4] = v5;
+                mapVertices[vCounter + 5] = v8;
+                vCounter += 6;
+
+                mapNormals[nCounter] = n4;
+                mapNormals[nCounter + 1] = n4;
+                mapNormals[nCounter + 2] = n4;
+                mapNormals[nCounter + 3] = n4;
+                mapNormals[nCounter + 4] = n4;
+                mapNormals[nCounter + 5] = n4;
+                nCounter += 6;
+
+                mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
+                mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
+                mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
+                mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
+                mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
+                mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
+                tcCounter += 6;
+
+                // Checking cube on bottom of current cube
+                if (((z < cubicmap.height - 1) && COLOR_EQUAL(pixels[(z + 1)*cubicmap.width + x], BLACK)) || (z == cubicmap.height - 1))
+                {
+                    // Define front triangles (2 tris, 6 vertex) --> v2 v7 v3, v3 v7 v8
+                    // NOTE: Collateral occluded faces are not generated
+                    mapVertices[vCounter] = v2;
+                    mapVertices[vCounter + 1] = v7;
+                    mapVertices[vCounter + 2] = v3;
+                    mapVertices[vCounter + 3] = v3;
+                    mapVertices[vCounter + 4] = v7;
+                    mapVertices[vCounter + 5] = v8;
+                    vCounter += 6;
+
+                    mapNormals[nCounter] = n6;
+                    mapNormals[nCounter + 1] = n6;
+                    mapNormals[nCounter + 2] = n6;
+                    mapNormals[nCounter + 3] = n6;
+                    mapNormals[nCounter + 4] = n6;
+                    mapNormals[nCounter + 5] = n6;
+                    nCounter += 6;
+
+                    mapTexcoords[tcCounter] = (Vector2){ frontTexUV.x, frontTexUV.y };
+                    mapTexcoords[tcCounter + 1] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
+                    mapTexcoords[tcCounter + 2] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
+                    mapTexcoords[tcCounter + 3] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y };
+                    mapTexcoords[tcCounter + 4] = (Vector2){ frontTexUV.x, frontTexUV.y + frontTexUV.height };
+                    mapTexcoords[tcCounter + 5] = (Vector2){ frontTexUV.x + frontTexUV.width, frontTexUV.y + frontTexUV.height };
+                    tcCounter += 6;
+                }
+
+                // Checking cube on top of current cube
+                if (((z > 0) && COLOR_EQUAL(pixels[(z - 1)*cubicmap.width + x], BLACK)) || (z == 0))
+                {
+                    // Define back triangles (2 tris, 6 vertex) --> v1 v5 v6, v1 v4 v5
+                    // NOTE: Collateral occluded faces are not generated
+                    mapVertices[vCounter] = v1;
+                    mapVertices[vCounter + 1] = v5;
+                    mapVertices[vCounter + 2] = v6;
+                    mapVertices[vCounter + 3] = v1;
+                    mapVertices[vCounter + 4] = v4;
+                    mapVertices[vCounter + 5] = v5;
+                    vCounter += 6;
+
+                    mapNormals[nCounter] = n5;
+                    mapNormals[nCounter + 1] = n5;
+                    mapNormals[nCounter + 2] = n5;
+                    mapNormals[nCounter + 3] = n5;
+                    mapNormals[nCounter + 4] = n5;
+                    mapNormals[nCounter + 5] = n5;
+                    nCounter += 6;
+
+                    mapTexcoords[tcCounter] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
+                    mapTexcoords[tcCounter + 1] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
+                    mapTexcoords[tcCounter + 2] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y + backTexUV.height };
+                    mapTexcoords[tcCounter + 3] = (Vector2){ backTexUV.x + backTexUV.width, backTexUV.y };
+                    mapTexcoords[tcCounter + 4] = (Vector2){ backTexUV.x, backTexUV.y };
+                    mapTexcoords[tcCounter + 5] = (Vector2){ backTexUV.x, backTexUV.y + backTexUV.height };
+                    tcCounter += 6;
+                }
+
+                // Checking cube on right of current cube
+                if (((x < cubicmap.width - 1) && COLOR_EQUAL(pixels[z*cubicmap.width + (x + 1)], BLACK)) || (x == cubicmap.width - 1))
+                {
+                    // Define right triangles (2 tris, 6 vertex) --> v3 v8 v4, v4 v8 v5
+                    // NOTE: Collateral occluded faces are not generated
+                    mapVertices[vCounter] = v3;
+                    mapVertices[vCounter + 1] = v8;
+                    mapVertices[vCounter + 2] = v4;
+                    mapVertices[vCounter + 3] = v4;
+                    mapVertices[vCounter + 4] = v8;
+                    mapVertices[vCounter + 5] = v5;
+                    vCounter += 6;
+
+                    mapNormals[nCounter] = n1;
+                    mapNormals[nCounter + 1] = n1;
+                    mapNormals[nCounter + 2] = n1;
+                    mapNormals[nCounter + 3] = n1;
+                    mapNormals[nCounter + 4] = n1;
+                    mapNormals[nCounter + 5] = n1;
+                    nCounter += 6;
+
+                    mapTexcoords[tcCounter] = (Vector2){ rightTexUV.x, rightTexUV.y };
+                    mapTexcoords[tcCounter + 1] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
+                    mapTexcoords[tcCounter + 2] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
+                    mapTexcoords[tcCounter + 3] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y };
+                    mapTexcoords[tcCounter + 4] = (Vector2){ rightTexUV.x, rightTexUV.y + rightTexUV.height };
+                    mapTexcoords[tcCounter + 5] = (Vector2){ rightTexUV.x + rightTexUV.width, rightTexUV.y + rightTexUV.height };
+                    tcCounter += 6;
+                }
+
+                // Checking cube on left of current cube
+                if (((x > 0) && COLOR_EQUAL(pixels[z*cubicmap.width + (x - 1)], BLACK)) || (x == 0))
+                {
+                    // Define left triangles (2 tris, 6 vertex) --> v1 v7 v2, v1 v6 v7
+                    // NOTE: Collateral occluded faces are not generated
+                    mapVertices[vCounter] = v1;
+                    mapVertices[vCounter + 1] = v7;
+                    mapVertices[vCounter + 2] = v2;
+                    mapVertices[vCounter + 3] = v1;
+                    mapVertices[vCounter + 4] = v6;
+                    mapVertices[vCounter + 5] = v7;
+                    vCounter += 6;
+
+                    mapNormals[nCounter] = n2;
+                    mapNormals[nCounter + 1] = n2;
+                    mapNormals[nCounter + 2] = n2;
+                    mapNormals[nCounter + 3] = n2;
+                    mapNormals[nCounter + 4] = n2;
+                    mapNormals[nCounter + 5] = n2;
+                    nCounter += 6;
+
+                    mapTexcoords[tcCounter] = (Vector2){ leftTexUV.x, leftTexUV.y };
+                    mapTexcoords[tcCounter + 1] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
+                    mapTexcoords[tcCounter + 2] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y };
+                    mapTexcoords[tcCounter + 3] = (Vector2){ leftTexUV.x, leftTexUV.y };
+                    mapTexcoords[tcCounter + 4] = (Vector2){ leftTexUV.x, leftTexUV.y + leftTexUV.height };
+                    mapTexcoords[tcCounter + 5] = (Vector2){ leftTexUV.x + leftTexUV.width, leftTexUV.y + leftTexUV.height };
+                    tcCounter += 6;
+                }
+            }
+            // We check pixel color to be BLACK, we will only draw floor and roof
+            else if (COLOR_EQUAL(pixels[z*cubicmap.width + x], BLACK))
+            {
+                // Define top triangles (2 tris, 6 vertex --> v1-v2-v3, v1-v3-v4)
+                mapVertices[vCounter] = v1;
+                mapVertices[vCounter + 1] = v3;
+                mapVertices[vCounter + 2] = v2;
+                mapVertices[vCounter + 3] = v1;
+                mapVertices[vCounter + 4] = v4;
+                mapVertices[vCounter + 5] = v3;
+                vCounter += 6;
+
+                mapNormals[nCounter] = n4;
+                mapNormals[nCounter + 1] = n4;
+                mapNormals[nCounter + 2] = n4;
+                mapNormals[nCounter + 3] = n4;
+                mapNormals[nCounter + 4] = n4;
+                mapNormals[nCounter + 5] = n4;
+                nCounter += 6;
+
+                mapTexcoords[tcCounter] = (Vector2){ topTexUV.x, topTexUV.y };
+                mapTexcoords[tcCounter + 1] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
+                mapTexcoords[tcCounter + 2] = (Vector2){ topTexUV.x, topTexUV.y + topTexUV.height };
+                mapTexcoords[tcCounter + 3] = (Vector2){ topTexUV.x, topTexUV.y };
+                mapTexcoords[tcCounter + 4] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y };
+                mapTexcoords[tcCounter + 5] = (Vector2){ topTexUV.x + topTexUV.width, topTexUV.y + topTexUV.height };
+                tcCounter += 6;
+
+                // Define bottom triangles (2 tris, 6 vertex --> v6-v8-v7, v6-v5-v8)
+                mapVertices[vCounter] = v6;
+                mapVertices[vCounter + 1] = v7;
+                mapVertices[vCounter + 2] = v8;
+                mapVertices[vCounter + 3] = v6;
+                mapVertices[vCounter + 4] = v8;
+                mapVertices[vCounter + 5] = v5;
+                vCounter += 6;
+
+                mapNormals[nCounter] = n3;
+                mapNormals[nCounter + 1] = n3;
+                mapNormals[nCounter + 2] = n3;
+                mapNormals[nCounter + 3] = n3;
+                mapNormals[nCounter + 4] = n3;
+                mapNormals[nCounter + 5] = n3;
+                nCounter += 6;
+
+                mapTexcoords[tcCounter] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
+                mapTexcoords[tcCounter + 1] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y + bottomTexUV.height };
+                mapTexcoords[tcCounter + 2] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
+                mapTexcoords[tcCounter + 3] = (Vector2){ bottomTexUV.x + bottomTexUV.width, bottomTexUV.y };
+                mapTexcoords[tcCounter + 4] = (Vector2){ bottomTexUV.x, bottomTexUV.y + bottomTexUV.height };
+                mapTexcoords[tcCounter + 5] = (Vector2){ bottomTexUV.x, bottomTexUV.y };
+                tcCounter += 6;
+            }
+        }
+    }
+
+    // Move data from mapVertices temp arrays to vertices float array
+    mesh.vertexCount = vCounter;
+    mesh.triangleCount = vCounter/3;
+
+    mesh.vertices = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.normals = (float *)RL_MALLOC(mesh.vertexCount*3*sizeof(float));
+    mesh.texcoords = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float));
+    mesh.colors = NULL;
+
+    int fCounter = 0;
+
+    // Move vertices data
+    for (int i = 0; i < vCounter; i++)
+    {
+        mesh.vertices[fCounter] = mapVertices[i].x;
+        mesh.vertices[fCounter + 1] = mapVertices[i].y;
+        mesh.vertices[fCounter + 2] = mapVertices[i].z;
+        fCounter += 3;
+    }
+
+    fCounter = 0;
+
+    // Move normals data
+    for (int i = 0; i < nCounter; i++)
+    {
+        mesh.normals[fCounter] = mapNormals[i].x;
+        mesh.normals[fCounter + 1] = mapNormals[i].y;
+        mesh.normals[fCounter + 2] = mapNormals[i].z;
+        fCounter += 3;
+    }
+
+    fCounter = 0;
+
+    // Move texcoords data
+    for (int i = 0; i < tcCounter; i++)
+    {
+        mesh.texcoords[fCounter] = mapTexcoords[i].x;
+        mesh.texcoords[fCounter + 1] = mapTexcoords[i].y;
+        fCounter += 2;
+    }
+
+    RL_FREE(mapVertices);
+    RL_FREE(mapNormals);
+    RL_FREE(mapTexcoords);
+
+    UnloadImageColors(pixels);   // Unload pixels color data
+
+    // Upload vertex data to GPU (static mesh)
+    UploadMesh(&mesh, false);
+
+    return mesh;
+}
+#endif      // SUPPORT_MESH_GENERATION
+
+// Compute mesh bounding box limits
+// NOTE: minVertex and maxVertex should be transformed by model transform matrix
+BoundingBox GetMeshBoundingBox(Mesh mesh)
+{
+    // Get min and max vertex to construct bounds (AABB)
+    Vector3 minVertex = { 0 };
+    Vector3 maxVertex = { 0 };
+
+    if (mesh.vertices != NULL)
+    {
+        minVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+        maxVertex = (Vector3){ mesh.vertices[0], mesh.vertices[1], mesh.vertices[2] };
+
+        for (int i = 1; i < mesh.vertexCount; i++)
+        {
+            minVertex = Vector3Min(minVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
+            maxVertex = Vector3Max(maxVertex, (Vector3){ mesh.vertices[i*3], mesh.vertices[i*3 + 1], mesh.vertices[i*3 + 2] });
+        }
+    }
+
+    // Create the bounding box
+    BoundingBox box = { 0 };
+    box.min = minVertex;
+    box.max = maxVertex;
+
+    return box;
+}
+
+// Compute mesh tangents
+void GenMeshTangents(Mesh *mesh)
+{
+    // Check if input mesh data is useful
+    if ((mesh == NULL) || (mesh->vertices == NULL) || (mesh->texcoords == NULL) || (mesh->normals == NULL))
+    {
+        TRACELOG(LOG_WARNING, "MESH: Tangents generation requires vertices, texcoords and normals vertex attribute data");
+        return;
+    }
+
+    // Allocate or reallocate tangents data
+    if (mesh->tangents == NULL) mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
+    else
+    {
+        RL_FREE(mesh->tangents);
+        mesh->tangents = (float *)RL_MALLOC(mesh->vertexCount*4*sizeof(float));
+    }
+
+    // Allocate temporary arrays for tangents calculation
+    Vector3 *tan1 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
+    Vector3 *tan2 = (Vector3 *)RL_CALLOC(mesh->vertexCount, sizeof(Vector3));
+
+    if (tan1 == NULL || tan2 == NULL)
+    {
+        TRACELOG(LOG_WARNING, "MESH: Failed to allocate temporary memory for tangent calculation");
+        if (tan1) RL_FREE(tan1);
+        if (tan2) RL_FREE(tan2);
+        return;
+    }
+
+    // Process all triangles of the mesh
+    // 'triangleCount' must be always valid
+    for (int t = 0; t < mesh->triangleCount; t++)
+    {
+        // Get triangle vertex indices
+        int i0 = 0, i1 = 0, i2 = 0;
+
+        if (mesh->indices != NULL)
+        {
+            // Use indices if available
+            i0 = mesh->indices[t*3 + 0];
+            i1 = mesh->indices[t*3 + 1];
+            i2 = mesh->indices[t*3 + 2];
+        }
+        else
+        {
+            // Sequential access for non-indexed mesh
+            i0 = t*3 + 0;
+            i1 = t*3 + 1;
+            i2 = t*3 + 2;
+        }
+
+        // Get triangle vertices position
+        Vector3 v1 = { mesh->vertices[i0*3 + 0], mesh->vertices[i0*3 + 1], mesh->vertices[i0*3 + 2] };
+        Vector3 v2 = { mesh->vertices[i1*3 + 0], mesh->vertices[i1*3 + 1], mesh->vertices[i1*3 + 2] };
+        Vector3 v3 = { mesh->vertices[i2*3 + 0], mesh->vertices[i2*3 + 1], mesh->vertices[i2*3 + 2] };
+
+        // Get triangle texcoords
+        Vector2 uv1 = { mesh->texcoords[i0*2 + 0], mesh->texcoords[i0*2 + 1] };
+        Vector2 uv2 = { mesh->texcoords[i1*2 + 0], mesh->texcoords[i1*2 + 1] };
+        Vector2 uv3 = { mesh->texcoords[i2*2 + 0], mesh->texcoords[i2*2 + 1] };
+
+        // Calculate triangle edges
+        float x1 = v2.x - v1.x;
+        float y1 = v2.y - v1.y;
+        float z1 = v2.z - v1.z;
+        float x2 = v3.x - v1.x;
+        float y2 = v3.y - v1.y;
+        float z2 = v3.z - v1.z;
+
+        // Calculate texture coordinate differences
+        float s1 = uv2.x - uv1.x;
+        float t1 = uv2.y - uv1.y;
+        float s2 = uv3.x - uv1.x;
+        float t2 = uv3.y - uv1.y;
+
+        // Calculate denominator and check for degenerate UV
+        float div = s1*t2 - s2*t1;
+        float r = (fabsf(div) < 0.0001f)? 0.0f : 1.0f/div;
+
+        // Calculate tangent and bitangent directions
+        Vector3 sdir = { (t2*x1 - t1*x2)*r, (t2*y1 - t1*y2)*r, (t2*z1 - t1*z2)*r };
+        Vector3 tdir = { (s1*x2 - s2*x1)*r, (s1*y2 - s2*y1)*r, (s1*z2 - s2*z1)*r };
+
+        // Accumulate tangents and bitangents for each vertex of the triangle
+        tan1[i0] = Vector3Add(tan1[i0], sdir);
+        tan1[i1] = Vector3Add(tan1[i1], sdir);
+        tan1[i2] = Vector3Add(tan1[i2], sdir);
+
+        tan2[i0] = Vector3Add(tan2[i0], tdir);
+        tan2[i1] = Vector3Add(tan2[i1], tdir);
+        tan2[i2] = Vector3Add(tan2[i2], tdir);
+    }
+
+    // Calculate final tangents for each vertex
+    for (int i = 0; i < mesh->vertexCount; i++)
+    {
+        Vector3 normal = { mesh->normals[i*3 + 0], mesh->normals[i*3 + 1], mesh->normals[i*3 + 2] };
+        Vector3 tangent = tan1[i];
+
+        // Handle zero tangent (can happen with degenerate UVs)
+        if (Vector3Length(tangent) < 0.0001f)
+        {
+            // Create a tangent perpendicular to the normal
+            if (fabsf(normal.z) > 0.707f) tangent = (Vector3){ 1.0f, 0.0f, 0.0f };
+            else tangent = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
+
+            mesh->tangents[i*4 + 0] = tangent.x;
+            mesh->tangents[i*4 + 1] = tangent.y;
+            mesh->tangents[i*4 + 2] = tangent.z;
+            mesh->tangents[i*4 + 3] = 1.0f;
+            continue;
+        }
+
+        // Gram-Schmidt orthogonalization to make tangent orthogonal to normal
+        // T_prime = T - N*dot(N, T)
+        Vector3 orthogonalized = Vector3Subtract(tangent, Vector3Scale(normal, Vector3DotProduct(normal, tangent)));
+
+        // Handle cases where orthogonalized vector is too small
+        if (Vector3Length(orthogonalized) < 0.0001f)
+        {
+            // Create a tangent perpendicular to the normal
+            if (fabsf(normal.z) > 0.707f) orthogonalized = (Vector3){ 1.0f, 0.0f, 0.0f };
+            else orthogonalized = Vector3Normalize((Vector3){ -normal.y, normal.x, 0.0f });
+        }
+        else
+        {
+            // Normalize the orthogonalized tangent
+            orthogonalized = Vector3Normalize(orthogonalized);
+        }
+
+        // Store the calculated tangent
+        mesh->tangents[i*4 + 0] = orthogonalized.x;
+        mesh->tangents[i*4 + 1] = orthogonalized.y;
+        mesh->tangents[i*4 + 2] = orthogonalized.z;
+
+        // Calculate the handedness (w component)
+        mesh->tangents[i*4 + 3] = (Vector3DotProduct(Vector3CrossProduct(normal, orthogonalized), tan2[i]) < 0.0f)? -1.0f : 1.0f;
+    }
+
+    // Free temporary arrays
+    RL_FREE(tan1);
+    RL_FREE(tan2);
+
+    // Update vertex buffers if available
+    if (mesh->vboId != NULL)
+    {
+        if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0)
+        {
+            // Update existing tangent vertex buffer
+            rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0);
+        }
+        else
+        {
+            // Create new tangent vertex buffer
+            mesh->vboId[SHADER_LOC_VERTEX_TANGENT] = rlLoadVertexBuffer(mesh->tangents, mesh->vertexCount*4*sizeof(float), false);
+        }
+
+        // Set up vertex attributes for shader
+        rlEnableVertexArray(mesh->vaoId);
+        rlSetVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT, 4, RL_FLOAT, 0, 0, 0);
+        rlEnableVertexAttribute(RL_DEFAULT_SHADER_ATTRIB_LOCATION_TANGENT);
+        rlDisableVertexArray();
+    }
+
+    TRACELOG(LOG_INFO, "MESH: Tangents data computed and uploaded for provided mesh");
+}
+
+// Draw a model (with texture if set)
+void DrawModel(Model model, Vector3 position, float scale, Color tint)
+{
+    Vector3 vScale = { scale, scale, scale };
+    Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f };
+
+    DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint);
+}
+
+// Draw a model with extended parameters
+void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
+{
+    // Calculate transformation matrix from function parameters
+    // Get transform matrix (rotation -> scale -> translation)
+    Matrix matScale = MatrixScale(scale.x, scale.y, scale.z);
+    Matrix matRotation = MatrixRotate(rotationAxis, rotationAngle*DEG2RAD);
+    Matrix matTranslation = MatrixTranslate(position.x, position.y, position.z);
+
+    Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation);
+
+    // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform)
+    model.transform = MatrixMultiply(model.transform, matTransform);
+
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        Color color = model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color;
+
+        Color colorTint = WHITE;
+        colorTint.r = (unsigned char)(((int)color.r*(int)tint.r)/255);
+        colorTint.g = (unsigned char)(((int)color.g*(int)tint.g)/255);
+        colorTint.b = (unsigned char)(((int)color.b*(int)tint.b)/255);
+        colorTint.a = (unsigned char)(((int)color.a*(int)tint.a)/255);
+
+        model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = colorTint;
+        DrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
+        model.materials[model.meshMaterial[i]].maps[MATERIAL_MAP_DIFFUSE].color = color;
+    }
+}
+
+// Draw a model wires (with texture if set)
+void DrawModelWires(Model model, Vector3 position, float scale, Color tint)
+{
+    rlEnableWireMode();
+
+    DrawModel(model, position, scale, tint);
+
+    rlDisableWireMode();
+}
+
+// Draw a model wires (with texture if set) with extended parameters
+void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
+{
+    rlEnableWireMode();
+
+    DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
+
+    rlDisableWireMode();
+}
+
+// Draw a model points
+// WARNING: OpenGL ES 2.0 does not support point mode drawing
+void DrawModelPoints(Model model, Vector3 position, float scale, Color tint)
+{
+    rlEnablePointMode();
+    rlDisableBackfaceCulling();
+
+    DrawModel(model, position, scale, tint);
+
+    rlEnableBackfaceCulling();
+    rlDisablePointMode();
+}
+
+// Draw a model points
+// WARNING: OpenGL ES 2.0 does not support point mode drawing
+void DrawModelPointsEx(Model model, Vector3 position, Vector3 rotationAxis, float rotationAngle, Vector3 scale, Color tint)
+{
+    rlEnablePointMode();
+    rlDisableBackfaceCulling();
+
+    DrawModelEx(model, position, rotationAxis, rotationAngle, scale, tint);
+
+    rlEnableBackfaceCulling();
+    rlDisablePointMode();
+}
+
+// Draw a billboard
+void DrawBillboard(Camera camera, Texture2D texture, Vector3 position, float scale, Color tint)
+{
+    Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
+
+    DrawBillboardRec(camera, texture, source, position, (Vector2){ scale*fabsf((float)source.width/source.height), scale }, tint);
+}
+
+// Draw a billboard (part of a texture defined by a rectangle)
+void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector2 size, Color tint)
+{
+    // NOTE: Billboard locked on axis-Y
+    Vector3 up = { 0.0f, 1.0f, 0.0f };
+
+    DrawBillboardPro(camera, texture, source, position, up, size, Vector2Scale(size, 0.5), 0.0f, tint);
+}
+
+// Draw a billboard with additional parameters
+void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint)
+{
+    // Compute the up vector and the right vector
+    Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up);
+    Vector3 right = { matView.m0, matView.m4, matView.m8 };
+    right = Vector3Scale(right, size.x);
+    up = Vector3Scale(up, size.y);
+
+    // Flip the content of the billboard while maintaining the counterclockwise edge rendering order
+    if (size.x < 0.0f)
+    {
+        source.x -= size.x;
+        source.width *= -1.0;
+        right = Vector3Negate(right);
+        origin.x *= -1.0f;
+    }
+    if (size.y < 0.0f)
+    {
+        source.y -= size.y;
+        source.height *= -1.0;
+        up = Vector3Negate(up);
+        origin.y *= -1.0f;
+    }
+
+    // Draw the texture region described by source on the following rectangle in 3D space:
+    //
+    //                size.x          <--.
+    //  3 ^---------------------------+ 2 \ rotation
+    //    |                           |   /
+    //    |                           |
+    //    |   origin.x   position     |
+    // up |..............             | size.y
+    //    |             .             |
+    //    |             . origin.y    |
+    //    |             .             |
+    //  0 +---------------------------> 1
+    //                right
+    Vector3 forward;
+    if (rotation != 0.0) forward = Vector3CrossProduct(right, up);
+
+    Vector3 origin3D = Vector3Add(Vector3Scale(Vector3Normalize(right), origin.x), Vector3Scale(Vector3Normalize(up), origin.y));
+
+    Vector3 points[4];
+    points[0] = Vector3Zero();
+    points[1] = right;
+    points[2] = Vector3Add(up, right);
+    points[3] = up;
+
+    for (int i = 0; i < 4; i++)
+    {
+        points[i] = Vector3Subtract(points[i], origin3D);
+        if (rotation != 0.0) points[i] = Vector3RotateByAxisAngle(points[i], forward, rotation*DEG2RAD);
+        points[i] = Vector3Add(points[i], position);
+    }
+
+    Vector2 texcoords[4];
+    texcoords[0] = (Vector2){ (float)source.x/texture.width, (float)(source.y + source.height)/texture.height };
+    texcoords[1] = (Vector2){ (float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height };
+    texcoords[2] = (Vector2){ (float)(source.x + source.width)/texture.width, (float)source.y/texture.height };
+    texcoords[3] = (Vector2){ (float)source.x/texture.width, (float)source.y/texture.height };
+
+    rlSetTexture(texture.id);
+    rlBegin(RL_QUADS);
+
+        rlColor4ub(tint.r, tint.g, tint.b, tint.a);
+        for (int i = 0; i < 4; i++)
+        {
+            rlTexCoord2f(texcoords[i].x, texcoords[i].y);
+            rlVertex3f(points[i].x, points[i].y, points[i].z);
+        }
+
+    rlEnd();
+    rlSetTexture(0);
+}
+
+// Draw a bounding box with wires
+void DrawBoundingBox(BoundingBox box, Color color)
+{
+    Vector3 size = { 0 };
+
+    size.x = fabsf(box.max.x - box.min.x);
+    size.y = fabsf(box.max.y - box.min.y);
+    size.z = fabsf(box.max.z - box.min.z);
+
+    Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
+
+    DrawCubeWires(center, size.x, size.y, size.z, color);
+}
+
+// Check collision between two spheres
+bool CheckCollisionSpheres(Vector3 center1, float radius1, Vector3 center2, float radius2)
+{
+    bool collision = false;
+
+    // Simple way to check for collision, just checking distance between two points
+    // Unfortunately, sqrtf() is a costly operation, so we avoid it with following solution
+    /*
+    float dx = center1.x - center2.x;      // X distance between centers
+    float dy = center1.y - center2.y;      // Y distance between centers
+    float dz = center1.z - center2.z;      // Z distance between centers
+
+    float distance = sqrtf(dx*dx + dy*dy + dz*dz);  // Distance between centers
+
+    if (distance <= (radius1 + radius2)) collision = true;
+    */
+
+    // Check for distances squared to avoid sqrtf()
+    if (Vector3DotProduct(Vector3Subtract(center2, center1), Vector3Subtract(center2, center1)) <= (radius1 + radius2)*(radius1 + radius2)) collision = true;
+
+    return collision;
+}
+
+// Check collision between two boxes
+// NOTE: Boxes are defined by two points minimum and maximum
+bool CheckCollisionBoxes(BoundingBox box1, BoundingBox box2)
+{
+    bool collision = true;
+
+    if ((box1.max.x >= box2.min.x) && (box1.min.x <= box2.max.x))
+    {
+        if ((box1.max.y < box2.min.y) || (box1.min.y > box2.max.y)) collision = false;
+        if ((box1.max.z < box2.min.z) || (box1.min.z > box2.max.z)) collision = false;
+    }
+    else collision = false;
+
+    return collision;
+}
+
+// Check collision between box and sphere
+bool CheckCollisionBoxSphere(BoundingBox box, Vector3 center, float radius)
+{
+    bool collision = false;
+
+    float dmin = 0;
+
+    if (center.x < box.min.x) dmin += powf(center.x - box.min.x, 2);
+    else if (center.x > box.max.x) dmin += powf(center.x - box.max.x, 2);
+
+    if (center.y < box.min.y) dmin += powf(center.y - box.min.y, 2);
+    else if (center.y > box.max.y) dmin += powf(center.y - box.max.y, 2);
+
+    if (center.z < box.min.z) dmin += powf(center.z - box.min.z, 2);
+    else if (center.z > box.max.z) dmin += powf(center.z - box.max.z, 2);
+
+    if (dmin <= (radius*radius)) collision = true;
+
+    return collision;
+}
+
+// Get collision info between ray and sphere
+RayCollision GetRayCollisionSphere(Ray ray, Vector3 center, float radius)
+{
+    RayCollision collision = { 0 };
+
+    Vector3 raySpherePos = Vector3Subtract(center, ray.position);
+    float vector = Vector3DotProduct(raySpherePos, ray.direction);
+    float distance = Vector3Length(raySpherePos);
+    float d = radius*radius - (distance*distance - vector*vector);
+
+    collision.hit = d >= 0.0f;
+
+    // Check if ray origin is inside the sphere to calculate the correct collision point
+    if (distance < radius)
+    {
+        collision.distance = vector + sqrtf(d);
+
+        // Calculate collision point
+        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
+
+        // Calculate collision normal (pointing outwards)
+        collision.normal = Vector3Negate(Vector3Normalize(Vector3Subtract(collision.point, center)));
+    }
+    else
+    {
+        collision.distance = vector - sqrtf(d);
+
+        // Calculate collision point
+        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
+
+        // Calculate collision normal (pointing inwards)
+        collision.normal = Vector3Normalize(Vector3Subtract(collision.point, center));
+    }
+
+    return collision;
+}
+
+// Get collision info between ray and box
+RayCollision GetRayCollisionBox(Ray ray, BoundingBox box)
+{
+    RayCollision collision = { 0 };
+
+    // Note: If ray.position is inside the box, the distance is negative (as if the ray was reversed)
+    // Reversing ray.direction will give use the correct result
+    bool insideBox = (ray.position.x > box.min.x) && (ray.position.x < box.max.x) &&
+                     (ray.position.y > box.min.y) && (ray.position.y < box.max.y) &&
+                     (ray.position.z > box.min.z) && (ray.position.z < box.max.z);
+
+    if (insideBox) ray.direction = Vector3Negate(ray.direction);
+
+    float t[11] = { 0 };
+
+    t[8] = 1.0f/ray.direction.x;
+    t[9] = 1.0f/ray.direction.y;
+    t[10] = 1.0f/ray.direction.z;
+
+    t[0] = (box.min.x - ray.position.x)*t[8];
+    t[1] = (box.max.x - ray.position.x)*t[8];
+    t[2] = (box.min.y - ray.position.y)*t[9];
+    t[3] = (box.max.y - ray.position.y)*t[9];
+    t[4] = (box.min.z - ray.position.z)*t[10];
+    t[5] = (box.max.z - ray.position.z)*t[10];
+    t[6] = (float)fmax(fmax(fmin(t[0], t[1]), fmin(t[2], t[3])), fmin(t[4], t[5]));
+    t[7] = (float)fmin(fmin(fmax(t[0], t[1]), fmax(t[2], t[3])), fmax(t[4], t[5]));
+
+    collision.hit = !((t[7] < 0) || (t[6] > t[7]));
+    collision.distance = t[6];
+    collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, collision.distance));
+
+    // Get box center point
+    collision.normal = Vector3Lerp(box.min, box.max, 0.5f);
+    // Get vector center point->hit point
+    collision.normal = Vector3Subtract(collision.point, collision.normal);
+    // Scale vector to unit cube
+    // NOTE: We use an additional .01 to fix numerical errors
+    collision.normal = Vector3Scale(collision.normal, 2.01f);
+    collision.normal = Vector3Divide(collision.normal, Vector3Subtract(box.max, box.min));
+    // The relevant elements of the vector are now slightly larger than 1.0f (or smaller than -1.0f)
+    // and the others are somewhere between -1.0 and 1.0 casting to int is exactly our wanted normal!
+    collision.normal.x = (float)((int)collision.normal.x);
+    collision.normal.y = (float)((int)collision.normal.y);
+    collision.normal.z = (float)((int)collision.normal.z);
+
+    collision.normal = Vector3Normalize(collision.normal);
+
+    if (insideBox)
+    {
+        // Reset ray.direction
+        ray.direction = Vector3Negate(ray.direction);
+        // Fix result
+        collision.distance *= -1.0f;
+        collision.normal = Vector3Negate(collision.normal);
+    }
+
+    return collision;
+}
+
+// Get collision info between ray and mesh
+RayCollision GetRayCollisionMesh(Ray ray, Mesh mesh, Matrix transform)
+{
+    RayCollision collision = { 0 };
+
+    // Check if mesh vertex data on CPU for testing
+    if (mesh.vertices != NULL)
+    {
+        int triangleCount = mesh.triangleCount;
+
+        // Test against all triangles in mesh
+        for (int i = 0; i < triangleCount; i++)
+        {
+            Vector3 a = { 0 };
+            Vector3 b = { 0 };
+            Vector3 c = { 0 };
+            Vector3 *vertdata = (Vector3 *)mesh.vertices;
+
+            if (mesh.indices)
+            {
+                a = vertdata[mesh.indices[i*3 + 0]];
+                b = vertdata[mesh.indices[i*3 + 1]];
+                c = vertdata[mesh.indices[i*3 + 2]];
+            }
+            else
+            {
+                a = vertdata[i*3 + 0];
+                b = vertdata[i*3 + 1];
+                c = vertdata[i*3 + 2];
+            }
+
+            a = Vector3Transform(a, transform);
+            b = Vector3Transform(b, transform);
+            c = Vector3Transform(c, transform);
+
+            RayCollision triHitInfo = GetRayCollisionTriangle(ray, a, b, c);
+
+            if (triHitInfo.hit)
+            {
+                // Save the closest hit triangle
+                if ((!collision.hit) || (collision.distance > triHitInfo.distance)) collision = triHitInfo;
+            }
+        }
+    }
+
+    return collision;
+}
+
+// Get collision info between ray and triangle
+// NOTE: The points are expected to be in counter-clockwise winding
+// NOTE: Based on https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
+RayCollision GetRayCollisionTriangle(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3)
+{
+    #define EPSILON 0.000001f        // A small number
+
+    RayCollision collision = { 0 };
+    Vector3 edge1 = { 0 };
+    Vector3 edge2 = { 0 };
+    Vector3 p = { 0 };
+    Vector3 q = { 0 };
+    Vector3 tv = { 0 };
+    float det = 0.0f, invDet = 0.0f, u = 0.0f, v = 0.0f, t = 0.0f;
+
+    // Find vectors for two edges sharing V1
+    edge1 = Vector3Subtract(p2, p1);
+    edge2 = Vector3Subtract(p3, p1);
+
+    // Begin calculating determinant - also used to calculate u parameter
+    p = Vector3CrossProduct(ray.direction, edge2);
+
+    // If determinant is near zero, ray lies in plane of triangle or ray is parallel to plane of triangle
+    det = Vector3DotProduct(edge1, p);
+
+    // Avoid culling!
+    if ((det > -EPSILON) && (det < EPSILON)) return collision;
+
+    invDet = 1.0f/det;
+
+    // Calculate distance from V1 to ray origin
+    tv = Vector3Subtract(ray.position, p1);
+
+    // Calculate u parameter and test bound
+    u = Vector3DotProduct(tv, p)*invDet;
+
+    // The intersection lies outside the triangle
+    if ((u < 0.0f) || (u > 1.0f)) return collision;
+
+    // Prepare to test v parameter
+    q = Vector3CrossProduct(tv, edge1);
+
+    // Calculate V parameter and test bound
+    v = Vector3DotProduct(ray.direction, q)*invDet;
+
+    // The intersection lies outside the triangle
+    if ((v < 0.0f) || ((u + v) > 1.0f)) return collision;
+
+    t = Vector3DotProduct(edge2, q)*invDet;
+
+    if (t > EPSILON)
+    {
+        // Ray hit, get hit point and normal
+        collision.hit = true;
+        collision.distance = t;
+        collision.normal = Vector3Normalize(Vector3CrossProduct(edge1, edge2));
+        collision.point = Vector3Add(ray.position, Vector3Scale(ray.direction, t));
+    }
+
+    return collision;
+}
+
+// Get collision info between ray and quad
+// NOTE: The points are expected to be in counter-clockwise winding
+RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4)
+{
+    RayCollision collision = { 0 };
+
+    collision = GetRayCollisionTriangle(ray, p1, p2, p4);
+
+    if (!collision.hit) collision = GetRayCollisionTriangle(ray, p2, p3, p4);
+
+    return collision;
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+#if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF)
+// Build pose from parent joints
+// NOTE: Required for animations loading (required by IQM and GLTF)
+static void BuildPoseFromParentJoints(BoneInfo *bones, int boneCount, Transform *transforms)
+{
+    for (int i = 0; i < boneCount; i++)
+    {
+        if (bones[i].parent >= 0)
+        {
+            if (bones[i].parent > i)
+            {
+                TRACELOG(LOG_WARNING, "Assumes bones are toplogically sorted, but bone %d has parent %d. Skipping.", i, bones[i].parent);
+                continue;
+            }
+            transforms[i].rotation = QuaternionMultiply(transforms[bones[i].parent].rotation, transforms[i].rotation);
+            transforms[i].scale = Vector3Multiply(transforms[i].scale, transforms[bones[i].parent].scale);
+            transforms[i].translation = Vector3Multiply(transforms[i].translation, transforms[bones[i].parent].scale);
+            transforms[i].translation = Vector3RotateByQuaternion(transforms[i].translation, transforms[bones[i].parent].rotation);
+            transforms[i].translation = Vector3Add(transforms[i].translation, transforms[bones[i].parent].translation);
+        }
+    }
+}
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_OBJ)
+// Load OBJ mesh data
+//
+// Keep the following information in mind when reading this
+//  - A mesh is created for every material present in the obj file
+//  - the model.meshCount is therefore the materialCount returned from tinyobj
+//  - the mesh is automatically triangulated by tinyobj
+static Model LoadOBJ(const char *fileName)
+{
+    tinyobj_attrib_t objAttributes = { 0 };
+    tinyobj_shape_t *objShapes = NULL;
+    unsigned int objShapeCount = 0;
+
+    tinyobj_material_t *objMaterials = NULL;
+    unsigned int objMaterialCount = 0;
+
+    Model model = { 0 };
+    model.transform = MatrixIdentity();
+
+    char *fileText = LoadFileText(fileName);
+
+    if (fileText == NULL)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Unable to read obj file", fileName);
+        return model;
+    }
+
+    char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
+    strncpy(currentDir, GetWorkingDirectory(), MAX_FILEPATH_LENGTH - 1); // Save current working directory
+    const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
+    if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
+
+    unsigned int dataSize = (unsigned int)strlen(fileText);
+
+    unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
+    int ret = tinyobj_parse_obj(&objAttributes, &objShapes, &objShapeCount, &objMaterials, &objMaterialCount, fileText, dataSize, flags);
+
+    if (ret != TINYOBJ_SUCCESS)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: Unable to read obj data %s", fileName);
+        return model;
+    }
+
+    UnloadFileText(fileText);
+
+    unsigned int faceVertIndex = 0;
+    unsigned int nextShape = 1;
+    int lastMaterial = -1;
+    unsigned int meshIndex = 0;
+
+    // Count meshes
+    unsigned int nextShapeEnd = objAttributes.num_face_num_verts;
+
+    // See how many verts till the next shape
+    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
+
+    // Walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        if (faceId >= nextShapeEnd)
+        {
+            // Try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // This is actually the total number of face verts in the file, not faces
+            meshIndex++;
+        }
+        else if ((lastMaterial != -1) && (objAttributes.material_ids[faceId] != lastMaterial))
+        {
+            meshIndex++; // If this is a new material, we need to allocate a new mesh
+        }
+
+        lastMaterial = objAttributes.material_ids[faceId];
+        faceVertIndex += objAttributes.face_num_verts[faceId];
+    }
+
+    // Allocate the base meshes and materials
+    model.meshCount = meshIndex + 1;
+    model.meshes = (Mesh *)MemAlloc(sizeof(Mesh)*model.meshCount);
+
+    if (objMaterialCount > 0)
+    {
+        model.materialCount = objMaterialCount;
+        model.materials = (Material *)MemAlloc(sizeof(Material)*objMaterialCount);
+    }
+    else // We must allocate at least one material
+    {
+        model.materialCount = 1;
+        model.materials = (Material *)MemAlloc(sizeof(Material)*1);
+    }
+
+    model.meshMaterial = (int *)MemAlloc(sizeof(int)*model.meshCount);
+
+    // See how many verts are in each mesh
+    unsigned int *localMeshVertexCounts = (unsigned int *)MemAlloc(sizeof(unsigned int)*model.meshCount);
+
+    faceVertIndex = 0;
+    nextShapeEnd = objAttributes.num_face_num_verts;
+    lastMaterial = -1;
+    meshIndex = 0;
+    unsigned int localMeshVertexCount = 0;
+
+    nextShape = 1;
+    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
+
+    // Walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        bool newMesh = false; // Do we need a new mesh?
+        if (faceId >= nextShapeEnd)
+        {
+            // Try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // this is actually the total number of face verts in the file, not faces
+
+            newMesh = true;
+        }
+        else if ((lastMaterial != -1) && (objAttributes.material_ids[faceId] != lastMaterial))
+        {
+            newMesh = true;
+        }
+
+        lastMaterial = objAttributes.material_ids[faceId];
+
+        if (newMesh)
+        {
+            localMeshVertexCounts[meshIndex] = localMeshVertexCount;
+
+            localMeshVertexCount = 0;
+            meshIndex++;
+        }
+
+        faceVertIndex += objAttributes.face_num_verts[faceId];
+        localMeshVertexCount += objAttributes.face_num_verts[faceId];
+    }
+
+    localMeshVertexCounts[meshIndex] = localMeshVertexCount;
+
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        // Allocate the buffers for each mesh
+        unsigned int vertexCount = localMeshVertexCounts[i];
+
+        model.meshes[i].vertexCount = vertexCount;
+        model.meshes[i].triangleCount = vertexCount/3;
+
+        model.meshes[i].vertices = (float *)MemAlloc(sizeof(float)*vertexCount*3);
+        model.meshes[i].normals = (float *)MemAlloc(sizeof(float)*vertexCount*3);
+    #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+        model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
+        model.meshes[i].colors = (unsigned char *)MemAlloc(sizeof(unsigned char)*vertexCount*4);
+    #else
+        if (objAttributes.texcoords != NULL && objAttributes.num_texcoords > 0) model.meshes[i].texcoords = (float *)MemAlloc(sizeof(float)*vertexCount*2);
+        else model.meshes[i].texcoords = NULL;
+        model.meshes[i].colors = NULL;
+    #endif
+    }
+
+    MemFree(localMeshVertexCounts);
+    localMeshVertexCounts = NULL;
+
+    // Fill meshes
+    faceVertIndex = 0;
+
+    nextShapeEnd = objAttributes.num_face_num_verts;
+
+    // See how many verts till the next shape
+    nextShape = 1;
+    if (objShapeCount > 1) nextShapeEnd = objShapes[nextShape].face_offset;
+    lastMaterial = -1;
+    meshIndex = 0;
+    localMeshVertexCount = 0;
+
+    // Walk all the faces
+    for (unsigned int faceId = 0; faceId < objAttributes.num_faces; faceId++)
+    {
+        bool newMesh = false; // Do we need a new mesh?
+        if (faceId >= nextShapeEnd)
+        {
+            // Try to find the last vert in the next shape
+            nextShape++;
+            if (nextShape < objShapeCount) nextShapeEnd = objShapes[nextShape].face_offset;
+            else nextShapeEnd = objAttributes.num_face_num_verts; // This is actually the total number of face verts in the file, not faces
+            newMesh = true;
+        }
+
+        // If this is a new material, we need to allocate a new mesh
+        if (lastMaterial != -1 && objAttributes.material_ids[faceId] != lastMaterial) newMesh = true;
+        lastMaterial = objAttributes.material_ids[faceId];
+
+        if (newMesh)
+        {
+            localMeshVertexCount = 0;
+            meshIndex++;
+        }
+
+        int matId = 0;
+        if ((lastMaterial >= 0) && (lastMaterial < (int)objMaterialCount)) matId = lastMaterial;
+
+        model.meshMaterial[meshIndex] = matId;
+
+        for (int f = 0; f < objAttributes.face_num_verts[faceId]; f++)
+        {
+            int vertIndex = objAttributes.faces[faceVertIndex].v_idx;
+            int normalIndex = objAttributes.faces[faceVertIndex].vn_idx;
+            int texcordIndex = objAttributes.faces[faceVertIndex].vt_idx;
+
+            for (int i = 0; i < 3; i++) model.meshes[meshIndex].vertices[localMeshVertexCount*3 + i] = objAttributes.vertices[vertIndex*3 + i];
+
+            if ((objAttributes.texcoords != NULL) && (texcordIndex != TINYOBJ_INVALID_INDEX) && (texcordIndex >= 0) && (model.meshes[meshIndex].texcoords))
+            {
+                for (int i = 0; i < 2; i++) model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + i] = objAttributes.texcoords[texcordIndex*2 + i];
+                model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1] = 1.0f - model.meshes[meshIndex].texcoords[localMeshVertexCount*2 + 1];
+            }
+
+            if ((objAttributes.normals != NULL) && (normalIndex != TINYOBJ_INVALID_INDEX) && (normalIndex >= 0))
+            {
+                for (int i = 0; i < 3; i++) model.meshes[meshIndex].normals[localMeshVertexCount*3 + i] = objAttributes.normals[normalIndex*3 + i];
+            }
+            else
+            {
+                model.meshes[meshIndex].normals[localMeshVertexCount*3 + 0] = 0.0f;
+                model.meshes[meshIndex].normals[localMeshVertexCount*3 + 1] = 1.0f;
+                model.meshes[meshIndex].normals[localMeshVertexCount*3 + 2] = 0.0f;
+            }
+        #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+            for (int i = 0; i < 4; i++) model.meshes[meshIndex].colors[localMeshVertexCount*4 + i] = 255;
+        #endif
+            faceVertIndex++;
+            localMeshVertexCount++;
+        }
+    }
+
+    if (objMaterialCount > 0) ProcessMaterialsOBJ(model.materials, objMaterials, objMaterialCount);
+    else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh
+
+    tinyobj_attrib_free(&objAttributes);
+    tinyobj_shapes_free(objShapes, objShapeCount);
+    tinyobj_materials_free(objMaterials, objMaterialCount);
+
+    // Restore current working directory
+    if (CHDIR(currentDir) != 0)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);
+    }
+
+    return model;
+}
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_IQM)
+// Load IQM mesh data
+static Model LoadIQM(const char *fileName)
+{
+    #define IQM_MAGIC     "INTERQUAKEMODEL" // IQM file magic number
+    #define IQM_VERSION          2          // only IQM version 2 supported
+
+    #define BONE_NAME_LENGTH    32          // BoneInfo name string length
+    #define MESH_NAME_LENGTH    32          // Mesh name string length
+    #define MATERIAL_NAME_LENGTH 32         // Material name string length
+
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+    unsigned char *fileDataPtr = fileData;
+
+    // IQM file structs
+    //-----------------------------------------------------------------------------------
+    typedef struct IQMHeader {
+        char magic[16];
+        unsigned int version;
+        unsigned int dataSize;
+        unsigned int flags;
+        unsigned int num_text, ofs_text;
+        unsigned int num_meshes, ofs_meshes;
+        unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
+        unsigned int num_triangles, ofs_triangles, ofs_adjacency;
+        unsigned int num_joints, ofs_joints;
+        unsigned int num_poses, ofs_poses;
+        unsigned int num_anims, ofs_anims;
+        unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
+        unsigned int num_comment, ofs_comment;
+        unsigned int num_extensions, ofs_extensions;
+    } IQMHeader;
+
+    typedef struct IQMMesh {
+        unsigned int name;
+        unsigned int material;
+        unsigned int first_vertex, num_vertexes;
+        unsigned int first_triangle, num_triangles;
+    } IQMMesh;
+
+    typedef struct IQMTriangle {
+        unsigned int vertex[3];
+    } IQMTriangle;
+
+    typedef struct IQMJoint {
+        unsigned int name;
+        int parent;
+        float translate[3], rotate[4], scale[3];
+    } IQMJoint;
+
+    typedef struct IQMVertexArray {
+        unsigned int type;
+        unsigned int flags;
+        unsigned int format;
+        unsigned int size;
+        unsigned int offset;
+    } IQMVertexArray;
+
+    // NOTE: Below IQM structures are not used but listed for reference
+    /*
+    typedef struct IQMAdjacency {
+        unsigned int triangle[3];
+    } IQMAdjacency;
+
+    typedef struct IQMPose {
+        int parent;
+        unsigned int mask;
+        float channeloffset[10];
+        float channelscale[10];
+    } IQMPose;
+
+    typedef struct IQMAnim {
+        unsigned int name;
+        unsigned int first_frame, num_frames;
+        float framerate;
+        unsigned int flags;
+    } IQMAnim;
+
+    typedef struct IQMBounds {
+        float bbmin[3], bbmax[3];
+        float xyradius, radius;
+    } IQMBounds;
+    */
+    //-----------------------------------------------------------------------------------
+
+    // IQM vertex data types
+    enum {
+        IQM_POSITION     = 0,
+        IQM_TEXCOORD     = 1,
+        IQM_NORMAL       = 2,
+        IQM_TANGENT      = 3,       // NOTE: Tangents unused by default
+        IQM_BLENDINDEXES = 4,
+        IQM_BLENDWEIGHTS = 5,
+        IQM_COLOR        = 6,
+        IQM_CUSTOM       = 0x10     // NOTE: Custom vertex values unused by default
+    };
+
+    Model model = { 0 };
+
+    IQMMesh *imesh = NULL;
+    IQMTriangle *tri = NULL;
+    IQMVertexArray *va = NULL;
+    IQMJoint *ijoint = NULL;
+
+    float *vertex = NULL;
+    float *normal = NULL;
+    float *text = NULL;
+    char *blendi = NULL;
+    unsigned char *blendw = NULL;
+    unsigned char *color = NULL;
+
+    // In case file can not be read, return an empty model
+    if (fileDataPtr == NULL) return model;
+
+    const char *basePath = GetDirectoryPath(fileName);
+
+    // Read IQM header
+    IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
+
+    if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
+        UnloadFileData(fileData);
+        return model;
+    }
+
+    if (iqmHeader->version != IQM_VERSION)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
+        UnloadFileData(fileData);
+        return model;
+    }
+
+    //fileDataPtr += sizeof(IQMHeader);       // Move file data pointer
+
+    // Meshes data processing
+    imesh = (IQMMesh *)RL_MALLOC(iqmHeader->num_meshes*sizeof(IQMMesh));
+    //fseek(iqmFile, iqmHeader->ofs_meshes, SEEK_SET);
+    //fread(imesh, sizeof(IQMMesh)*iqmHeader->num_meshes, 1, iqmFile);
+    memcpy(imesh, fileDataPtr + iqmHeader->ofs_meshes, iqmHeader->num_meshes*sizeof(IQMMesh));
+
+    model.meshCount = iqmHeader->num_meshes;
+    model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+
+    model.materialCount = model.meshCount;
+    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+
+    char name[MESH_NAME_LENGTH] = { 0 };
+    char material[MATERIAL_NAME_LENGTH] = { 0 };
+
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].name, SEEK_SET);
+        //fread(name, sizeof(char), MESH_NAME_LENGTH, iqmFile);
+        memcpy(name, fileDataPtr + iqmHeader->ofs_text + imesh[i].name, MESH_NAME_LENGTH*sizeof(char));
+
+        //fseek(iqmFile, iqmHeader->ofs_text + imesh[i].material, SEEK_SET);
+        //fread(material, sizeof(char), MATERIAL_NAME_LENGTH, iqmFile);
+        memcpy(material, fileDataPtr + iqmHeader->ofs_text + imesh[i].material, MATERIAL_NAME_LENGTH*sizeof(char));
+
+        model.materials[i] = LoadMaterialDefault();
+        model.materials[i].maps[MATERIAL_MAP_ALBEDO].texture = LoadTexture(TextFormat("%s/%s", basePath, material));
+
+        model.meshMaterial[i] = i;
+
+        TRACELOG(LOG_DEBUG, "MODEL: [%s] mesh name (%s), material (%s)", fileName, name, material);
+
+        model.meshes[i].vertexCount = imesh[i].num_vertexes;
+
+        model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));       // Default vertex positions
+        model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));        // Default vertex normals
+        model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float));      // Default vertex texcoords
+
+        model.meshes[i].boneIds = (unsigned char *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(unsigned char));  // Up-to 4 bones supported!
+        model.meshes[i].boneWeights = (float *)RL_CALLOC(model.meshes[i].vertexCount*4, sizeof(float));      // Up-to 4 bones supported!
+
+        model.meshes[i].triangleCount = imesh[i].num_triangles;
+        model.meshes[i].indices = (unsigned short *)RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short));
+
+        // Animated vertex data, what we actually process for rendering
+        // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning)
+        model.meshes[i].animVertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
+        model.meshes[i].animNormals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float));
+    }
+
+    // Triangles data processing
+    tri = (IQMTriangle *)RL_MALLOC(iqmHeader->num_triangles*sizeof(IQMTriangle));
+    //fseek(iqmFile, iqmHeader->ofs_triangles, SEEK_SET);
+    //fread(tri, sizeof(IQMTriangle), iqmHeader->num_triangles, iqmFile);
+    memcpy(tri, fileDataPtr + iqmHeader->ofs_triangles, iqmHeader->num_triangles*sizeof(IQMTriangle));
+
+    for (int m = 0; m < model.meshCount; m++)
+    {
+        int tcounter = 0;
+
+        for (unsigned int i = imesh[m].first_triangle; i < (imesh[m].first_triangle + imesh[m].num_triangles); i++)
+        {
+            // IQM triangles indexes are stored in counter-clockwise, but raylib processes the index in linear order,
+            // expecting they point to the counter-clockwise vertex triangle, so we need to reverse triangle indexes
+            // NOTE: raylib renders vertex data in counter-clockwise order (standard convention) by default
+            model.meshes[m].indices[tcounter + 2] = tri[i].vertex[0] - imesh[m].first_vertex;
+            model.meshes[m].indices[tcounter + 1] = tri[i].vertex[1] - imesh[m].first_vertex;
+            model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
+            tcounter += 3;
+        }
+    }
+
+    // Vertex arrays data processing
+    va = (IQMVertexArray *)RL_MALLOC(iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
+    //fseek(iqmFile, iqmHeader->ofs_vertexarrays, SEEK_SET);
+    //fread(va, sizeof(IQMVertexArray), iqmHeader->num_vertexarrays, iqmFile);
+    memcpy(va, fileDataPtr + iqmHeader->ofs_vertexarrays, iqmHeader->num_vertexarrays*sizeof(IQMVertexArray));
+
+    for (unsigned int i = 0; i < iqmHeader->num_vertexarrays; i++)
+    {
+        switch (va[i].type)
+        {
+            case IQM_POSITION:
+            {
+                vertex = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(vertex, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
+                memcpy(vertex, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    int vCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
+                    {
+                        model.meshes[m].vertices[vCounter] = vertex[i];
+                        model.meshes[m].animVertices[vCounter] = vertex[i];
+                        vCounter++;
+                    }
+                }
+            } break;
+            case IQM_NORMAL:
+            {
+                normal = (float *)RL_MALLOC(iqmHeader->num_vertexes*3*sizeof(float));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(normal, iqmHeader->num_vertexes*3*sizeof(float), 1, iqmFile);
+                memcpy(normal, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*3*sizeof(float));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    int vCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
+                    {
+                        model.meshes[m].normals[vCounter] = normal[i];
+                        model.meshes[m].animNormals[vCounter] = normal[i];
+                        vCounter++;
+                    }
+                }
+            } break;
+            case IQM_TEXCOORD:
+            {
+                text = (float *)RL_MALLOC(iqmHeader->num_vertexes*2*sizeof(float));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(text, iqmHeader->num_vertexes*2*sizeof(float), 1, iqmFile);
+                memcpy(text, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*2*sizeof(float));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    int vCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
+                    {
+                        model.meshes[m].texcoords[vCounter] = text[i];
+                        vCounter++;
+                    }
+                }
+            } break;
+            case IQM_BLENDINDEXES:
+            {
+                blendi = (char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(char));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(blendi, iqmHeader->num_vertexes*4*sizeof(char), 1, iqmFile);
+                memcpy(blendi, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(char));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    int boneCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
+                    {
+                        model.meshes[m].boneIds[boneCounter] = blendi[i];
+                        boneCounter++;
+                    }
+                }
+            } break;
+            case IQM_BLENDWEIGHTS:
+            {
+                blendw = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
+                memcpy(blendw, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    int boneCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
+                    {
+                        model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f;
+                        boneCounter++;
+                    }
+                }
+            } break;
+            case IQM_COLOR:
+            {
+                color = (unsigned char *)RL_MALLOC(iqmHeader->num_vertexes*4*sizeof(unsigned char));
+                //fseek(iqmFile, va[i].offset, SEEK_SET);
+                //fread(blendw, iqmHeader->num_vertexes*4*sizeof(unsigned char), 1, iqmFile);
+                memcpy(color, fileDataPtr + va[i].offset, iqmHeader->num_vertexes*4*sizeof(unsigned char));
+
+                for (unsigned int m = 0; m < iqmHeader->num_meshes; m++)
+                {
+                    model.meshes[m].colors = (unsigned char *)RL_CALLOC(model.meshes[m].vertexCount*4, sizeof(unsigned char));
+
+                    int vCounter = 0;
+                    for (unsigned int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
+                    {
+                        model.meshes[m].colors[vCounter] = color[i];
+                        vCounter++;
+                    }
+                }
+            } break;
+        }
+    }
+
+    // Bones (joints) data processing
+    ijoint = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
+    //fseek(iqmFile, iqmHeader->ofs_joints, SEEK_SET);
+    //fread(ijoint, sizeof(IQMJoint), iqmHeader->num_joints, iqmFile);
+    memcpy(ijoint, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
+
+    model.boneCount = iqmHeader->num_joints;
+    model.bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_joints*sizeof(BoneInfo));
+    model.bindPose = (Transform *)RL_MALLOC(iqmHeader->num_joints*sizeof(Transform));
+
+    for (unsigned int i = 0; i < iqmHeader->num_joints; i++)
+    {
+        // Bones
+        model.bones[i].parent = ijoint[i].parent;
+        //fseek(iqmFile, iqmHeader->ofs_text + ijoint[i].name, SEEK_SET);
+        //fread(model.bones[i].name, sizeof(char), BONE_NAME_LENGTH, iqmFile);
+        memcpy(model.bones[i].name, fileDataPtr + iqmHeader->ofs_text + ijoint[i].name, BONE_NAME_LENGTH*sizeof(char));
+
+        // Bind pose (base pose)
+        model.bindPose[i].translation.x = ijoint[i].translate[0];
+        model.bindPose[i].translation.y = ijoint[i].translate[1];
+        model.bindPose[i].translation.z = ijoint[i].translate[2];
+
+        model.bindPose[i].rotation.x = ijoint[i].rotate[0];
+        model.bindPose[i].rotation.y = ijoint[i].rotate[1];
+        model.bindPose[i].rotation.z = ijoint[i].rotate[2];
+        model.bindPose[i].rotation.w = ijoint[i].rotate[3];
+
+        model.bindPose[i].scale.x = ijoint[i].scale[0];
+        model.bindPose[i].scale.y = ijoint[i].scale[1];
+        model.bindPose[i].scale.z = ijoint[i].scale[2];
+    }
+
+    BuildPoseFromParentJoints(model.bones, model.boneCount, model.bindPose);
+
+    for (int i = 0; i < model.meshCount; i++)
+    {
+        model.meshes[i].boneCount = model.boneCount;
+        model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
+
+        for (int j = 0; j < model.meshes[i].boneCount; j++)
+        {
+            model.meshes[i].boneMatrices[j] = MatrixIdentity();
+        }
+    }
+
+    UnloadFileData(fileData);
+
+    RL_FREE(imesh);
+    RL_FREE(tri);
+    RL_FREE(va);
+    RL_FREE(vertex);
+    RL_FREE(normal);
+    RL_FREE(text);
+    RL_FREE(blendi);
+    RL_FREE(blendw);
+    RL_FREE(ijoint);
+    RL_FREE(color);
+
+    return model;
+}
+
+// Load IQM animation data
+static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount)
+{
+    #define IQM_MAGIC       "INTERQUAKEMODEL"   // IQM file magic number
+    #define IQM_VERSION     2                   // only IQM version 2 supported
+
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+    unsigned char *fileDataPtr = fileData;
+
+    typedef struct IQMHeader {
+        char magic[16];
+        unsigned int version;
+        unsigned int dataSize;
+        unsigned int flags;
+        unsigned int num_text, ofs_text;
+        unsigned int num_meshes, ofs_meshes;
+        unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
+        unsigned int num_triangles, ofs_triangles, ofs_adjacency;
+        unsigned int num_joints, ofs_joints;
+        unsigned int num_poses, ofs_poses;
+        unsigned int num_anims, ofs_anims;
+        unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
+        unsigned int num_comment, ofs_comment;
+        unsigned int num_extensions, ofs_extensions;
+    } IQMHeader;
+
+    typedef struct IQMJoint {
+        unsigned int name;
+        int parent;
+        float translate[3], rotate[4], scale[3];
+    } IQMJoint;
+
+    typedef struct IQMPose {
+        int parent;
+        unsigned int mask;
+        float channeloffset[10];
+        float channelscale[10];
+    } IQMPose;
+
+    typedef struct IQMAnim {
+        unsigned int name;
+        unsigned int first_frame, num_frames;
+        float framerate;
+        unsigned int flags;
+    } IQMAnim;
+
+    // In case file can not be read, return an empty model
+    if (fileDataPtr == NULL) return NULL;
+
+    // Read IQM header
+    IQMHeader *iqmHeader = (IQMHeader *)fileDataPtr;
+
+    if (memcmp(iqmHeader->magic, IQM_MAGIC, sizeof(IQM_MAGIC)) != 0)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file is not a valid model", fileName);
+        UnloadFileData(fileData);
+        return NULL;
+    }
+
+    if (iqmHeader->version != IQM_VERSION)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] IQM file version not supported (%i)", fileName, iqmHeader->version);
+        UnloadFileData(fileData);
+        return NULL;
+    }
+
+    // Get bones data
+    IQMPose *poses = (IQMPose *)RL_MALLOC(iqmHeader->num_poses*sizeof(IQMPose));
+    //fseek(iqmFile, iqmHeader->ofs_poses, SEEK_SET);
+    //fread(poses, sizeof(IQMPose), iqmHeader->num_poses, iqmFile);
+    memcpy(poses, fileDataPtr + iqmHeader->ofs_poses, iqmHeader->num_poses*sizeof(IQMPose));
+
+    // Get animations data
+    *animCount = iqmHeader->num_anims;
+    IQMAnim *anim = (IQMAnim *)RL_MALLOC(iqmHeader->num_anims*sizeof(IQMAnim));
+    //fseek(iqmFile, iqmHeader->ofs_anims, SEEK_SET);
+    //fread(anim, sizeof(IQMAnim), iqmHeader->num_anims, iqmFile);
+    memcpy(anim, fileDataPtr + iqmHeader->ofs_anims, iqmHeader->num_anims*sizeof(IQMAnim));
+
+    ModelAnimation *animations = (ModelAnimation *)RL_MALLOC(iqmHeader->num_anims*sizeof(ModelAnimation));
+
+    // frameposes
+    unsigned short *framedata = (unsigned short *)RL_MALLOC(iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
+    //fseek(iqmFile, iqmHeader->ofs_frames, SEEK_SET);
+    //fread(framedata, sizeof(unsigned short), iqmHeader->num_frames*iqmHeader->num_framechannels, iqmFile);
+    memcpy(framedata, fileDataPtr + iqmHeader->ofs_frames, iqmHeader->num_frames*iqmHeader->num_framechannels*sizeof(unsigned short));
+
+    // joints
+    IQMJoint *joints = (IQMJoint *)RL_MALLOC(iqmHeader->num_joints*sizeof(IQMJoint));
+    memcpy(joints, fileDataPtr + iqmHeader->ofs_joints, iqmHeader->num_joints*sizeof(IQMJoint));
+
+    for (unsigned int a = 0; a < iqmHeader->num_anims; a++)
+    {
+        animations[a].frameCount = anim[a].num_frames;
+        animations[a].boneCount = iqmHeader->num_poses;
+        animations[a].bones = (BoneInfo *)RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo));
+        animations[a].framePoses = (Transform **)RL_MALLOC(anim[a].num_frames*sizeof(Transform *));
+        memcpy(animations[a].name, fileDataPtr + iqmHeader->ofs_text + anim[a].name, 32);
+        TRACELOG(LOG_INFO, "IQM Anim %s", animations[a].name);
+        //animations[a].framerate = anim.framerate; // TODO: Use animation framerate data?
+
+        for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
+        {
+            // If animations and skeleton are in the same file, copy bone names to anim
+            if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
+            else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
+            animations[a].bones[j].parent = poses[j].parent;
+        }
+
+        for (unsigned int j = 0; j < anim[a].num_frames; j++) animations[a].framePoses[j] = (Transform *)RL_MALLOC(iqmHeader->num_poses*sizeof(Transform));
+
+        int dcounter = anim[a].first_frame*iqmHeader->num_framechannels;
+
+        for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
+        {
+            for (unsigned int i = 0; i < iqmHeader->num_poses; i++)
+            {
+                animations[a].framePoses[frame][i].translation.x = poses[i].channeloffset[0];
+
+                if (poses[i].mask & 0x01)
+                {
+                    animations[a].framePoses[frame][i].translation.x += framedata[dcounter]*poses[i].channelscale[0];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].translation.y = poses[i].channeloffset[1];
+
+                if (poses[i].mask & 0x02)
+                {
+                    animations[a].framePoses[frame][i].translation.y += framedata[dcounter]*poses[i].channelscale[1];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].translation.z = poses[i].channeloffset[2];
+
+                if (poses[i].mask & 0x04)
+                {
+                    animations[a].framePoses[frame][i].translation.z += framedata[dcounter]*poses[i].channelscale[2];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].rotation.x = poses[i].channeloffset[3];
+
+                if (poses[i].mask & 0x08)
+                {
+                    animations[a].framePoses[frame][i].rotation.x += framedata[dcounter]*poses[i].channelscale[3];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].rotation.y = poses[i].channeloffset[4];
+
+                if (poses[i].mask & 0x10)
+                {
+                    animations[a].framePoses[frame][i].rotation.y += framedata[dcounter]*poses[i].channelscale[4];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].rotation.z = poses[i].channeloffset[5];
+
+                if (poses[i].mask & 0x20)
+                {
+                    animations[a].framePoses[frame][i].rotation.z += framedata[dcounter]*poses[i].channelscale[5];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].rotation.w = poses[i].channeloffset[6];
+
+                if (poses[i].mask & 0x40)
+                {
+                    animations[a].framePoses[frame][i].rotation.w += framedata[dcounter]*poses[i].channelscale[6];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].scale.x = poses[i].channeloffset[7];
+
+                if (poses[i].mask & 0x80)
+                {
+                    animations[a].framePoses[frame][i].scale.x += framedata[dcounter]*poses[i].channelscale[7];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].scale.y = poses[i].channeloffset[8];
+
+                if (poses[i].mask & 0x100)
+                {
+                    animations[a].framePoses[frame][i].scale.y += framedata[dcounter]*poses[i].channelscale[8];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].scale.z = poses[i].channeloffset[9];
+
+                if (poses[i].mask & 0x200)
+                {
+                    animations[a].framePoses[frame][i].scale.z += framedata[dcounter]*poses[i].channelscale[9];
+                    dcounter++;
+                }
+
+                animations[a].framePoses[frame][i].rotation = QuaternionNormalize(animations[a].framePoses[frame][i].rotation);
+            }
+        }
+
+        // Build frameposes
+        for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
+        {
+            for (int i = 0; i < animations[a].boneCount; i++)
+            {
+                if (animations[a].bones[i].parent >= 0)
+                {
+                    animations[a].framePoses[frame][i].rotation = QuaternionMultiply(animations[a].framePoses[frame][animations[a].bones[i].parent].rotation, animations[a].framePoses[frame][i].rotation);
+                    animations[a].framePoses[frame][i].translation = Vector3RotateByQuaternion(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].rotation);
+                    animations[a].framePoses[frame][i].translation = Vector3Add(animations[a].framePoses[frame][i].translation, animations[a].framePoses[frame][animations[a].bones[i].parent].translation);
+                    animations[a].framePoses[frame][i].scale = Vector3Multiply(animations[a].framePoses[frame][i].scale, animations[a].framePoses[frame][animations[a].bones[i].parent].scale);
+                }
+            }
+        }
+    }
+
+    UnloadFileData(fileData);
+
+    RL_FREE(joints);
+    RL_FREE(framedata);
+    RL_FREE(poses);
+    RL_FREE(anim);
+
+    return animations;
+}
+
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_GLTF)
+// Load file data callback for cgltf
+static cgltf_result LoadFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, const char *path, cgltf_size *size, void **data)
+{
+    int filesize;
+    unsigned char *filedata = LoadFileData(path, &filesize);
+
+    if (filedata == NULL) return cgltf_result_io_error;
+
+    *size = filesize;
+    *data = filedata;
+
+    return cgltf_result_success;
+}
+
+// Release file data callback for cgltf
+static void ReleaseFileGLTFCallback(const struct cgltf_memory_options *memoryOptions, const struct cgltf_file_options *fileOptions, void *data)
+{
+    UnloadFileData((unsigned char *)data);
+}
+
+// Load image from different glTF provided methods (uri, path, buffer_view)
+static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPath)
+{
+    Image image = { 0 };
+
+    if (cgltfImage == NULL) return image;
+
+    if (cgltfImage->uri != NULL)     // Check if image data is provided as an uri (base64 or path)
+    {
+        if ((strlen(cgltfImage->uri) > 5) &&
+            (cgltfImage->uri[0] == 'd') &&
+            (cgltfImage->uri[1] == 'a') &&
+            (cgltfImage->uri[2] == 't') &&
+            (cgltfImage->uri[3] == 'a') &&
+            (cgltfImage->uri[4] == ':'))     // Check if image is provided as base64 text data
+        {
+            // Data URI Format: data:<mediatype>;base64,<data>
+
+            // Find the comma
+            int i = 0;
+            while ((cgltfImage->uri[i] != ',') && (cgltfImage->uri[i] != 0)) i++;
+
+            if (cgltfImage->uri[i] == 0) TRACELOG(LOG_WARNING, "IMAGE: glTF data URI is not a valid image");
+            else
+            {
+                int base64Size = (int)strlen(cgltfImage->uri + i + 1);
+                while (cgltfImage->uri[i + base64Size] == '=') base64Size--;    // Ignore optional paddings
+                int numberOfEncodedBits = base64Size*6 - (base64Size*6) % 8 ;   // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
+                int outSize = numberOfEncodedBits/8 ;                           // Actual encoded bytes
+                void *data = NULL;
+
+                cgltf_options options = { 0 };
+                options.file.read = LoadFileGLTFCallback;
+                options.file.release = ReleaseFileGLTFCallback;
+                cgltf_result result = cgltf_load_buffer_base64(&options, outSize, cgltfImage->uri + i + 1, &data);
+
+                if (result == cgltf_result_success)
+                {
+                    image = LoadImageFromMemory(".png", (unsigned char *)data, outSize);
+                    RL_FREE(data);
+                }
+            }
+        }
+        else     // Check if image is provided as image path
+        {
+            image = LoadImage(TextFormat("%s/%s", texPath, cgltfImage->uri));
+        }
+    }
+    else if ((cgltfImage->buffer_view != NULL) && (cgltfImage->buffer_view->buffer->data != NULL))    // Check if image is provided as data buffer
+    {
+        unsigned char *data = (unsigned char *)RL_MALLOC(cgltfImage->buffer_view->size);
+        int offset = (int)cgltfImage->buffer_view->offset;
+        int stride = (int)cgltfImage->buffer_view->stride? (int)cgltfImage->buffer_view->stride : 1;
+
+        // Copy buffer data to memory for loading
+        for (unsigned int i = 0; i < cgltfImage->buffer_view->size; i++)
+        {
+            data[i] = ((unsigned char *)cgltfImage->buffer_view->buffer->data)[offset];
+            offset += stride;
+        }
+
+        // Check mime_type for image: (cgltfImage->mime_type == "image/png")
+        // NOTE: Detected that some models define mime_type as "image\\/png"
+        if ((strcmp(cgltfImage->mime_type, "image\\/png") == 0) || (strcmp(cgltfImage->mime_type, "image/png") == 0))
+        {
+            image = LoadImageFromMemory(".png", data, (int)cgltfImage->buffer_view->size);
+        }
+        else if ((strcmp(cgltfImage->mime_type, "image\\/jpeg") == 0) || (strcmp(cgltfImage->mime_type, "image/jpeg") == 0))
+        {
+            image = LoadImageFromMemory(".jpg", data, (int)cgltfImage->buffer_view->size);
+        }
+        else TRACELOG(LOG_WARNING, "MODEL: glTF image data MIME type not recognized", TextFormat("%s/%s", texPath, cgltfImage->uri));
+
+        RL_FREE(data);
+    }
+
+    return image;
+}
+
+// Load bone info from GLTF skin data
+static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, int *boneCount)
+{
+    *boneCount = (int)skin.joints_count;
+    BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo));
+
+    for (unsigned int i = 0; i < skin.joints_count; i++)
+    {
+        cgltf_node node = *skin.joints[i];
+        if (node.name != NULL) strncpy(bones[i].name, node.name, sizeof(bones[i].name) - 1);
+
+        // Find parent bone index
+        int parentIndex = -1;
+
+        for (unsigned int j = 0; j < skin.joints_count; j++)
+        {
+            if (skin.joints[j] == node.parent)
+            {
+                parentIndex = (int)j;
+                break;
+            }
+        }
+
+        bones[i].parent = parentIndex;
+    }
+
+    return bones;
+}
+
+// Load glTF file into model struct, .gltf and .glb supported
+static Model LoadGLTF(const char *fileName)
+{
+    /*********************************************************************************************
+
+        Function implemented by Wilhem Barbier(@wbrbr), with modifications by Tyler Bezera(@gamerfiend)
+        Transform handling implemented by Paul Melis (@paulmelis)
+        Reviewed by Ramon Santamaria (@raysan5)
+
+        FEATURES:
+          - Supports .gltf and .glb files
+          - Supports embedded (base64) or external textures
+          - Supports PBR metallic/roughness flow, loads material textures, values and colors
+                     PBR specular/glossiness flow and extended texture flows not supported
+          - Supports multiple meshes per model (every primitives is loaded as a separate mesh)
+          - Supports basic animations
+          - Transforms, including parent-child relations, are applied on the mesh data,
+            but the hierarchy is not kept (as it can't be represented)
+          - Mesh instances in the glTF file (i.e. same mesh linked from multiple nodes)
+            are turned into separate raylib Meshes
+
+        RESTRICTIONS:
+          - Only triangle meshes supported
+          - Vertex attribute types and formats supported:
+              > Vertices (position): vec3: float
+              > Normals: vec3: float
+              > Texcoords: vec2: float
+              > Colors: vec4: u8, u16, f32 (normalized)
+              > Indices: u16, u32 (truncated to u16)
+          - Scenes defined in the glTF file are ignored. All nodes in the file are used
+
+    ***********************************************************************************************/
+
+    // Macro to simplify attributes loading code
+    #define LOAD_ATTRIBUTE(accesor, numComp, srcType, dstPtr) LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, srcType)
+
+    #define LOAD_ATTRIBUTE_CAST(accesor, numComp, srcType, dstPtr, dstType) \
+    { \
+        int n = 0; \
+        srcType *buffer = (srcType *)accesor->buffer_view->buffer->data + accesor->buffer_view->offset/sizeof(srcType) + accesor->offset/sizeof(srcType); \
+        for (unsigned int k = 0; k < accesor->count; k++) \
+        {\
+            for (int l = 0; l < numComp; l++) \
+            {\
+                dstPtr[numComp*k + l] = (dstType)buffer[n + l];\
+            }\
+            n += (int)(accesor->stride/sizeof(srcType));\
+        }\
+    }
+
+    Model model = { 0 };
+
+    // glTF file loading
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+    if (fileData == NULL) return model;
+
+    // glTF data loading
+    cgltf_options options = { 0 };
+    options.file.read = LoadFileGLTFCallback;
+    options.file.release = ReleaseFileGLTFCallback;
+    cgltf_data *data = NULL;
+    cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
+
+    if (result == cgltf_result_success)
+    {
+        if (data->file_type == cgltf_file_type_glb) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glb) loaded successfully", fileName);
+        else if (data->file_type == cgltf_file_type_gltf) TRACELOG(LOG_INFO, "MODEL: [%s] Model basic data (glTF) loaded successfully", fileName);
+        else TRACELOG(LOG_WARNING, "MODEL: [%s] Model format not recognized", fileName);
+
+        TRACELOG(LOG_INFO, "    > Meshes count: %i", data->meshes_count);
+        TRACELOG(LOG_INFO, "    > Materials count: %i (+1 default)", data->materials_count);
+        TRACELOG(LOG_DEBUG, "    > Buffers count: %i", data->buffers_count);
+        TRACELOG(LOG_DEBUG, "    > Images count: %i", data->images_count);
+        TRACELOG(LOG_DEBUG, "    > Textures count: %i", data->textures_count);
+
+        // Force reading data buffers (fills buffer_view->buffer->data)
+        // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded
+        result = cgltf_load_buffers(&options, data, fileName);
+        if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName);
+
+        int primitivesCount = 0;
+
+        // NOTE: We will load every primitive in the glTF as a separate raylib Mesh
+        // Determine total number of meshes needed from the node hierarchy
+        for (unsigned int i = 0; i < data->nodes_count; i++)
+        {
+            cgltf_node *node = &(data->nodes[i]);
+            cgltf_mesh *mesh = node->mesh;
+            if (!mesh) continue;
+
+            for (unsigned int p = 0; p < mesh->primitives_count; p++)
+            {
+                if (mesh->primitives[p].type == cgltf_primitive_type_triangles) primitivesCount++;
+            }
+        }
+        TRACELOG(LOG_DEBUG, "    > Primitives (triangles only) count based on hierarchy : %i", primitivesCount);
+
+        // Load our model data: meshes and materials
+        model.meshCount = primitivesCount;
+        model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+
+        // NOTE: We keep an extra slot for default material, in case some mesh requires it
+        model.materialCount = (int)data->materials_count + 1;
+        model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+        model.materials[0] = LoadMaterialDefault();     // Load default material (index: 0)
+
+        // Load mesh-material indices, by default all meshes are mapped to material index: 0
+        model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+
+        // Load materials data
+        //----------------------------------------------------------------------------------------------------
+        for (unsigned int i = 0, j = 1; i < data->materials_count; i++, j++)
+        {
+            model.materials[j] = LoadMaterialDefault();
+            const char *texPath = GetDirectoryPath(fileName);
+
+            // Check glTF material flow: PBR metallic/roughness flow
+            // NOTE: Alternatively, materials can follow PBR specular/glossiness flow
+            if (data->materials[i].has_pbr_metallic_roughness)
+            {
+                // Load base color texture (albedo)
+                if (data->materials[i].pbr_metallic_roughness.base_color_texture.texture)
+                {
+                    Image imAlbedo = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.base_color_texture.texture->image, texPath);
+                    if (imAlbedo.data != NULL)
+                    {
+                        model.materials[j].maps[MATERIAL_MAP_ALBEDO].texture = LoadTextureFromImage(imAlbedo);
+                        UnloadImage(imAlbedo);
+                    }
+                }
+                // Load base color factor (tint)
+                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.r = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[0]*255);
+                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.g = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[1]*255);
+                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.b = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[2]*255);
+                model.materials[j].maps[MATERIAL_MAP_ALBEDO].color.a = (unsigned char)(data->materials[i].pbr_metallic_roughness.base_color_factor[3]*255);
+
+                // Load metallic/roughness texture
+                if (data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture)
+                {
+                    Image imMetallicRoughness = LoadImageFromCgltfImage(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture->image, texPath);
+                    if (imMetallicRoughness.data != NULL)
+                    {
+                        Image imMetallic = { 0 };
+                        Image imRoughness = { 0 };
+
+                        imMetallic.data = RL_MALLOC(imMetallicRoughness.width*imMetallicRoughness.height);
+                        imRoughness.data = RL_MALLOC(imMetallicRoughness.width*imMetallicRoughness.height);
+
+                        imMetallic.width = imRoughness.width = imMetallicRoughness.width;
+                        imMetallic.height = imRoughness.height = imMetallicRoughness.height;
+
+                        imMetallic.format = imRoughness.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+                        imMetallic.mipmaps = imRoughness.mipmaps = 1;
+
+                        for (int x = 0; x < imRoughness.width; x++)
+                        {
+                            for (int y = 0; y < imRoughness.height; y++)
+                            {
+                                Color color = GetImageColor(imMetallicRoughness, x, y);
+
+                                ((unsigned char *)imRoughness.data)[y*imRoughness.width + x] = color.g; // Roughness color channel
+                                ((unsigned char *)imMetallic.data)[y*imMetallic.width + x] = color.b; // Metallic color channel
+                            }
+                        }
+
+                        model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(imRoughness);
+                        model.materials[j].maps[MATERIAL_MAP_METALNESS].texture = LoadTextureFromImage(imMetallic);
+
+                        UnloadImage(imRoughness);
+                        UnloadImage(imMetallic);
+                        UnloadImage(imMetallicRoughness);
+                    }
+
+                    // Load metallic/roughness material properties
+                    float roughness = data->materials[i].pbr_metallic_roughness.roughness_factor;
+                    model.materials[j].maps[MATERIAL_MAP_ROUGHNESS].value = roughness;
+
+                    float metallic = data->materials[i].pbr_metallic_roughness.metallic_factor;
+                    model.materials[j].maps[MATERIAL_MAP_METALNESS].value = metallic;
+                }
+
+                // Load normal texture
+                if (data->materials[i].normal_texture.texture)
+                {
+                    Image imNormal = LoadImageFromCgltfImage(data->materials[i].normal_texture.texture->image, texPath);
+                    if (imNormal.data != NULL)
+                    {
+                        model.materials[j].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(imNormal);
+                        UnloadImage(imNormal);
+                    }
+                }
+
+                // Load ambient occlusion texture
+                if (data->materials[i].occlusion_texture.texture)
+                {
+                    Image imOcclusion = LoadImageFromCgltfImage(data->materials[i].occlusion_texture.texture->image, texPath);
+                    if (imOcclusion.data != NULL)
+                    {
+                        model.materials[j].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(imOcclusion);
+                        UnloadImage(imOcclusion);
+                    }
+                }
+
+                // Load emissive texture
+                if (data->materials[i].emissive_texture.texture)
+                {
+                    Image imEmissive = LoadImageFromCgltfImage(data->materials[i].emissive_texture.texture->image, texPath);
+                    if (imEmissive.data != NULL)
+                    {
+                        model.materials[j].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(imEmissive);
+                        UnloadImage(imEmissive);
+                    }
+
+                    // Load emissive color factor
+                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.r = (unsigned char)(data->materials[i].emissive_factor[0]*255);
+                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.g = (unsigned char)(data->materials[i].emissive_factor[1]*255);
+                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.b = (unsigned char)(data->materials[i].emissive_factor[2]*255);
+                    model.materials[j].maps[MATERIAL_MAP_EMISSION].color.a = 255;
+                }
+            }
+
+            // Other possible materials not supported by raylib pipeline:
+            // has_clearcoat, has_transmission, has_volume, has_ior, has specular, has_sheen
+        }
+        //----------------------------------------------------------------------------------------------------
+
+        // Load meshes data
+        //
+        // NOTE: Visit each node in the hierarchy and process any mesh linked from it
+        //  - Each primitive within a glTF node becomes a raylib Mesh
+        //  - The local-to-world transform of each node is used to transform the points/normals/tangents of the created Mesh(es)
+        //  - Any glTF mesh linked from more than one Node (i.e. instancing) is turned into multiple Mesh's, as each Node will have its own transform applied
+        //
+        // WARNING: The code below disregards the scenes defined in the file, all nodes are used
+        //----------------------------------------------------------------------------------------------------
+        int meshIndex = 0;
+        for (unsigned int i = 0; i < data->nodes_count; i++)
+        {
+            cgltf_node *node = &(data->nodes[i]);
+
+            cgltf_mesh *mesh = node->mesh;
+            if (!mesh) continue;
+
+            cgltf_float worldTransform[16];
+            cgltf_node_transform_world(node, worldTransform);
+
+            Matrix worldMatrix = {
+                worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12],
+                worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13],
+                worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14],
+                worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15]
+            };
+
+            Matrix worldMatrixNormals = MatrixTranspose(MatrixInvert(worldMatrix));
+
+            for (unsigned int p = 0; p < mesh->primitives_count; p++)
+            {
+                // NOTE: We only support primitives defined by triangles
+                // Other alternatives: points, lines, line_strip, triangle_strip
+                if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
+
+                // NOTE: Attributes data could be provided in several data formats (8, 8u, 16u, 32...),
+                // Only some formats for each attribute type are supported, read info at the top of this function!
+
+                for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
+                {
+                    // Check the different attributes for every primitive
+                    if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_position)      // POSITION, vec3, float
+                    {
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        // WARNING: SPECS: POSITION accessor MUST have its min and max properties defined
+
+                        if (model.meshes[meshIndex].vertices != NULL) TRACELOG(LOG_WARNING, "MODEL: [%s] Vertices attribute data already loaded", fileName);
+                        else
+                        {
+                            if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f))
+                            {
+                                // Init raylib mesh vertices to copy glTF attribute data
+                                model.meshes[meshIndex].vertexCount = (int)attribute->count;
+                                model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load 3 components of float data type into mesh.vertices
+                                LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].vertices)
+
+                                // Transform the vertices
+                                float *vertices = model.meshes[meshIndex].vertices;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k+1], vertices[3*k+2] }, worldMatrix);
+                                    vertices[3*k] = vt.x;
+                                    vertices[3*k+1] = vt.y;
+                                    vertices[3*k+2] = vt.z;
+                                }
+                            }
+                            else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16u))
+                            {
+                                // Init raylib mesh vertices to copy glTF attribute data
+                                model.meshes[meshIndex].vertexCount = (int)attribute->count;
+                                model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short));
+                                LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp);
+
+                                // Convert data to raylib vertex data type (float) the matrix will scale it to the correct size as a float
+                                for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].vertices[t] = (float)temp[t];
+
+                                RL_FREE(temp);
+
+                                // Transform the vertices
+                                float *vertices = model.meshes[meshIndex].vertices;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k + 1], vertices[3*k + 2] }, worldMatrix);
+                                    vertices[3*k] = vt.x;
+                                    vertices[3*k + 1] = vt.y;
+                                    vertices[3*k + 2] = vt.z;
+                                }
+                            }
+                            else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16))
+                            {
+                                // Init raylib mesh vertices to copy glTF attribute data
+                                model.meshes[meshIndex].vertexCount = (int)attribute->count;
+                                model.meshes[meshIndex].vertices = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                short *temp = (short *)RL_MALLOC(attribute->count*3*sizeof(short));
+                                LOAD_ATTRIBUTE(attribute, 3, short, temp);
+
+                                // Convert data to raylib vertex data type (float) the matrix will scale it to the correct size as a float
+                                for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].vertices[t] = (float)temp[t];
+
+                                RL_FREE(temp);
+
+                                // Transform the vertices
+                                float *vertices = model.meshes[meshIndex].vertices;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 vt = Vector3Transform((Vector3){ vertices[3*k], vertices[3*k + 1], vertices[3*k + 2] }, worldMatrix);
+                                    vertices[3*k] = vt.x;
+                                    vertices[3*k + 1] = vt.y;
+                                    vertices[3*k + 2] = vt.z;
+                                }
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Vertices attribute data format not supported, use vec3 float", fileName);
+                        }
+                    }
+                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_normal)   // NORMAL, vec3, float
+                    {
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        if (model.meshes[meshIndex].normals != NULL) TRACELOG(LOG_WARNING, "MODEL: [%s] Normals attribute data already loaded", fileName);
+                        else
+                        {
+                            if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f))
+                            {
+                                // Init raylib mesh normals to copy glTF attribute data
+                                model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load 3 components of float data type into mesh.normals
+                                LOAD_ATTRIBUTE(attribute, 3, float, model.meshes[meshIndex].normals)
+
+                                // Transform the normals
+                                float *normals = model.meshes[meshIndex].normals;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 nt = Vector3Transform((Vector3){ normals[3*k], normals[3*k+1], normals[3*k+2] }, worldMatrixNormals);
+                                    normals[3*k] = nt.x;
+                                    normals[3*k+1] = nt.y;
+                                    normals[3*k+2] = nt.z;
+                                }
+                            }
+                            else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_16))
+                            {
+                                // Init raylib mesh normals to copy glTF attribute data
+                                model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                short *temp = (short *)RL_MALLOC(attribute->count*3*sizeof(short));
+                                LOAD_ATTRIBUTE(attribute, 3, short, temp);
+
+                                // Convert data to raylib normal data type (float)
+                                for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+                                RL_FREE(temp);
+
+                                // Transform the normals
+                                float *normals = model.meshes[meshIndex].normals;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+                                    normals[3*k] = nt.x;
+                                    normals[3*k + 1] = nt.y;
+                                    normals[3*k + 2] = nt.z;
+                                }
+                            }
+                            else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_8u))
+                            {
+                                // Init raylib mesh normals to copy glTF attribute data
+                                model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char));
+                                LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp);
+
+                                // Convert data to raylib normal data type (float)
+                                for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+                                RL_FREE(temp);
+
+                                // Transform the normals
+                                float *normals = model.meshes[meshIndex].normals;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+                                    normals[3*k] = nt.x;
+                                    normals[3*k + 1] = nt.y;
+                                    normals[3*k + 2] = nt.z;
+                                }
+                            }
+                            else if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_8))
+                            {
+                                // Init raylib mesh normals to copy glTF attribute data
+                                model.meshes[meshIndex].normals = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                char *temp = (char *)RL_MALLOC(attribute->count*3*sizeof(char));
+                                LOAD_ATTRIBUTE(attribute, 3, char, temp);
+
+                                // Convert data to raylib normal data type (float)
+                                for (unsigned int t = 0; t < attribute->count*3; t++) model.meshes[meshIndex].normals[t] = (float)temp[t];
+
+                                RL_FREE(temp);
+
+                                // Transform the normals
+                                float *normals = model.meshes[meshIndex].normals;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 nt = Vector3Normalize(Vector3Transform((Vector3){ normals[3*k], normals[3*k + 1], normals[3*k + 2] }, worldMatrixNormals));
+                                    normals[3*k] = nt.x;
+                                    normals[3*k + 1] = nt.y;
+                                    normals[3*k + 2] = nt.z;
+                                }
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Normals attribute data format not supported, use vec3 float", fileName);
+                        }
+                    }
+                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_tangent)   // TANGENT, vec4, float, w is tangent basis sign
+                    {
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        if (model.meshes[meshIndex].tangents != NULL) TRACELOG(LOG_WARNING, "MODEL: [%s] Tangents attribute data already loaded", fileName);
+                        else
+                        {
+                            if ((attribute->type == cgltf_type_vec4) && (attribute->component_type == cgltf_component_type_r_32f))
+                            {
+                                // Init raylib mesh tangent to copy glTF attribute data
+                                model.meshes[meshIndex].tangents = (float *)RL_MALLOC(attribute->count*4*sizeof(float));
+
+                                // Load 4 components of float data type into mesh.tangents
+                                LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].tangents)
+
+                                // Transform the tangents
+                                float *tangents = model.meshes[meshIndex].tangents;
+                                for (unsigned int k = 0; k < attribute->count; k++)
+                                {
+                                    Vector3 tt = Vector3Transform((Vector3){ tangents[4*k], tangents[4*k+1], tangents[4*k+2] }, worldMatrix);
+                                    tangents[4*k] = tt.x;
+                                    tangents[4*k+1] = tt.y;
+                                    tangents[4*k+2] = tt.z;
+                                }
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Tangents attribute data format not supported, use vec4 float", fileName);
+                        }
+                    }
+                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_texcoord) // TEXCOORD_n, vec2, float/u8n/u16n
+                    {
+                        // Support up to 2 texture coordinates attributes
+                        float *texcoordPtr = NULL;
+
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        if (attribute->type == cgltf_type_vec2)
+                        {
+                            if (attribute->component_type == cgltf_component_type_r_32f)  // vec2, float
+                            {
+                                // Init raylib mesh texcoords to copy glTF attribute data
+                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
+
+                                // Load 3 components of float data type into mesh.texcoords
+                                LOAD_ATTRIBUTE(attribute, 2, float, texcoordPtr)
+                            }
+                            else if (attribute->component_type == cgltf_component_type_r_8u) // vec2, u8n
+                            {
+                                // Init raylib mesh texcoords to copy glTF attribute data
+                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*2*sizeof(unsigned char));
+                                LOAD_ATTRIBUTE(attribute, 2, unsigned char, temp);
+
+                                // Convert data to raylib texcoord data type (float)
+                                for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/255.0f;
+
+                                RL_FREE(temp);
+                            }
+                            else if (attribute->component_type == cgltf_component_type_r_16u) // vec2, u16n
+                            {
+                                // Init raylib mesh texcoords to copy glTF attribute data
+                                texcoordPtr = (float *)RL_MALLOC(attribute->count*2*sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*2*sizeof(unsigned short));
+                                LOAD_ATTRIBUTE(attribute, 2, unsigned short, temp);
+
+                                // Convert data to raylib texcoord data type (float)
+                                for (unsigned int t = 0; t < attribute->count*2; t++) texcoordPtr[t] = (float)temp[t]/65535.0f;
+
+                                RL_FREE(temp);
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported", fileName);
+                        }
+                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Texcoords attribute data format not supported, use vec2 float", fileName);
+
+                        int index = mesh->primitives[p].attributes[j].index;
+                        if (index == 0) model.meshes[meshIndex].texcoords = texcoordPtr;
+                        else if (index == 1) model.meshes[meshIndex].texcoords2 = texcoordPtr;
+                        else
+                        {
+                            TRACELOG(LOG_WARNING, "MODEL: [%s] No more than 2 texture coordinates attributes supported", fileName);
+                            if (texcoordPtr != NULL) RL_FREE(texcoordPtr);
+                        }
+                    }
+                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_color)    // COLOR_n, vec3/vec4, float/u8n/u16n
+                    {
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        // WARNING: SPECS: All components of each COLOR_n accessor element MUST be clamped to [0.0, 1.0] range
+
+                        if (model.meshes[meshIndex].colors != NULL) TRACELOG(LOG_WARNING, "MODEL: [%s] Colors attribute data already loaded", fileName);
+                        else
+                        {
+                            if (attribute->type == cgltf_type_vec3)  // RGB
+                            {
+                                if (attribute->component_type == cgltf_component_type_r_8u)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load data into a temp buffer to be converted to raylib data type
+                                    unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*3*sizeof(unsigned char));
+                                    LOAD_ATTRIBUTE(attribute, 3, unsigned char, temp);
+
+                                    // Convert data to raylib color data type (4 bytes)
+                                    for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
+                                    {
+                                        model.meshes[meshIndex].colors[c] = temp[k];
+                                        model.meshes[meshIndex].colors[c + 1] = temp[k + 1];
+                                        model.meshes[meshIndex].colors[c + 2] = temp[k + 2];
+                                        model.meshes[meshIndex].colors[c + 3] = 255;
+                                    }
+
+                                    RL_FREE(temp);
+                                }
+                                else if (attribute->component_type == cgltf_component_type_r_16u)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load data into a temp buffer to be converted to raylib data type
+                                    unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*3*sizeof(unsigned short));
+                                    LOAD_ATTRIBUTE(attribute, 3, unsigned short, temp);
+
+                                    // Convert data to raylib color data type (4 bytes)
+                                    for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
+                                    {
+                                        model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[k]/65535.0f)*255.0f);
+                                        model.meshes[meshIndex].colors[c + 1] = (unsigned char)(((float)temp[k + 1]/65535.0f)*255.0f);
+                                        model.meshes[meshIndex].colors[c + 2] = (unsigned char)(((float)temp[k + 2]/65535.0f)*255.0f);
+                                        model.meshes[meshIndex].colors[c + 3] = 255;
+                                    }
+
+                                    RL_FREE(temp);
+                                }
+                                else if (attribute->component_type == cgltf_component_type_r_32f)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load data into a temp buffer to be converted to raylib data type
+                                    float *temp = (float *)RL_MALLOC(attribute->count*3*sizeof(float));
+                                    LOAD_ATTRIBUTE(attribute, 3, float, temp);
+
+                                    // Convert data to raylib color data type (4 bytes)
+                                    for (unsigned int c = 0, k = 0; c < (attribute->count*4 - 3); c += 4, k += 3)
+                                    {
+                                        model.meshes[meshIndex].colors[c] = (unsigned char)(temp[k]*255.0f);
+                                        model.meshes[meshIndex].colors[c + 1] = (unsigned char)(temp[k + 1]*255.0f);
+                                        model.meshes[meshIndex].colors[c + 2] = (unsigned char)(temp[k + 2]*255.0f);
+                                        model.meshes[meshIndex].colors[c + 3] = 255;
+                                    }
+
+                                    RL_FREE(temp);
+                                }
+                                else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
+                            }
+                            else if (attribute->type == cgltf_type_vec4) // RGBA
+                            {
+                                if (attribute->component_type == cgltf_component_type_r_8u)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load 4 components of unsigned char data type into mesh.colors
+                                    LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].colors)
+                                }
+                                else if (attribute->component_type == cgltf_component_type_r_16u)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load data into a temp buffer to be converted to raylib data type
+                                    unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short));
+                                    LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
+
+                                    // Convert data to raylib color data type (4 bytes)
+                                    for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(((float)temp[c]/65535.0f)*255.0f);
+
+                                    RL_FREE(temp);
+                                }
+                                else if (attribute->component_type == cgltf_component_type_r_32f)
+                                {
+                                    // Init raylib mesh color to copy glTF attribute data
+                                    model.meshes[meshIndex].colors = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+
+                                    // Load data into a temp buffer to be converted to raylib data type
+                                    float *temp = (float *)RL_MALLOC(attribute->count*4*sizeof(float));
+                                    LOAD_ATTRIBUTE(attribute, 4, float, temp);
+
+                                    // Convert data to raylib color data type (4 bytes), we expect the color data normalized
+                                    for (unsigned int c = 0; c < attribute->count*4; c++) model.meshes[meshIndex].colors[c] = (unsigned char)(temp[c]*255.0f);
+
+                                    RL_FREE(temp);
+                                }
+                                else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Color attribute data format not supported", fileName);
+                        }
+                    }
+
+                    // NOTE: Attributes related to animations data are processed after mesh data loading
+                }
+
+                // Load primitive indices data (if provided)
+                if ((mesh->primitives[p].indices != NULL) && (mesh->primitives[p].indices->buffer_view != NULL))
+                {
+                    cgltf_accessor *attribute = mesh->primitives[p].indices;
+
+                    model.meshes[meshIndex].triangleCount = (int)attribute->count/3;
+
+                    if (model.meshes[meshIndex].indices != NULL) TRACELOG(LOG_WARNING, "MODEL: [%s] Indices attribute data already loaded", fileName);
+                    else
+                    {
+                        if (attribute->component_type == cgltf_component_type_r_16u)
+                        {
+                            // Init raylib mesh indices to copy glTF attribute data
+                            model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
+
+                            // Load unsigned short data type into mesh.indices
+                            LOAD_ATTRIBUTE(attribute, 1, unsigned short, model.meshes[meshIndex].indices)
+                        }
+                        else if (attribute->component_type == cgltf_component_type_r_8u)
+                        {
+                            // Init raylib mesh indices to copy glTF attribute data
+                            model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
+                            LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned char, model.meshes[meshIndex].indices, unsigned short)
+
+                        }
+                        else if (attribute->component_type == cgltf_component_type_r_32u)
+                        {
+                            // Init raylib mesh indices to copy glTF attribute data
+                            model.meshes[meshIndex].indices = (unsigned short *)RL_MALLOC(attribute->count*sizeof(unsigned short));
+                            LOAD_ATTRIBUTE_CAST(attribute, 1, unsigned int, model.meshes[meshIndex].indices, unsigned short);
+
+                            TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data converted from u32 to u16, possible loss of data", fileName);
+                        }
+                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Indices data format not supported, use u16", fileName);
+                    }
+                }
+                else model.meshes[meshIndex].triangleCount = model.meshes[meshIndex].vertexCount/3;    // Unindexed mesh
+
+                // Assign to the primitive mesh the corresponding material index
+                // NOTE: If no material defined, mesh uses the already assigned default material (index: 0)
+                for (unsigned int m = 0; m < data->materials_count; m++)
+                {
+                    // The primitive actually keeps the pointer to the corresponding material,
+                    // raylib instead assigns to the mesh the by its index, as loaded in model.materials array
+                    // To get the index, we check if material pointers match, and we assign the corresponding index,
+                    // skipping index 0, the default material
+                    if (&data->materials[m] == mesh->primitives[p].material)
+                    {
+                        model.meshMaterial[meshIndex] = m + 1;
+                        break;
+                    }
+                }
+
+                meshIndex++;       // Move to next mesh
+            }
+        }
+        //----------------------------------------------------------------------------------------------------
+
+        // Load animation data
+        // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skins
+        // REF: https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#skinned-mesh-attributes
+        //
+        // LIMITATIONS:
+        //  - Only supports 1 armature per file, and skips loading it if there are multiple armatures
+        //  - Only supports linear interpolation (default method in Blender when checked "Always Sample Animations" when exporting a GLTF file)
+        //  - Only supports translation/rotation/scale animation channel.path, weights not considered (i.e. morph targets)
+        //----------------------------------------------------------------------------------------------------
+        if (data->skins_count > 0)
+        {
+            cgltf_skin skin = data->skins[0];
+            model.bones = LoadBoneInfoGLTF(skin, &model.boneCount);
+            model.bindPose = (Transform *)RL_MALLOC(model.boneCount*sizeof(Transform));
+
+            for (int i = 0; i < model.boneCount; i++)
+            {
+                cgltf_node *node = skin.joints[i];
+                cgltf_float worldTransform[16];
+                cgltf_node_transform_world(node, worldTransform);
+                Matrix worldMatrix = {
+                    worldTransform[0], worldTransform[4], worldTransform[8], worldTransform[12],
+                    worldTransform[1], worldTransform[5], worldTransform[9], worldTransform[13],
+                    worldTransform[2], worldTransform[6], worldTransform[10], worldTransform[14],
+                    worldTransform[3], worldTransform[7], worldTransform[11], worldTransform[15]
+                };
+                MatrixDecompose(worldMatrix, &(model.bindPose[i].translation), &(model.bindPose[i].rotation), &(model.bindPose[i].scale));
+            }
+
+            if (data->skins_count > 1) TRACELOG(LOG_WARNING, "MODEL: [%s] can only load one skin (armature) per model, but gltf skins_count == %i", fileName, data->skins_count);
+        }
+
+        meshIndex = 0;
+        for (unsigned int i = 0; i < data->nodes_count; i++)
+        {
+            cgltf_node *node = &(data->nodes[i]);
+
+            cgltf_mesh *mesh = node->mesh;
+            if (!mesh) continue;
+
+            for (unsigned int p = 0; p < mesh->primitives_count; p++)
+            {
+                bool hasJoints = false;
+
+                // NOTE: We only support primitives defined by triangles
+                if (mesh->primitives[p].type != cgltf_primitive_type_triangles) continue;
+
+                for (unsigned int j = 0; j < mesh->primitives[p].attributes_count; j++)
+                {
+                    // NOTE: JOINTS_1 + WEIGHT_1 will be used for +4 joints influencing a vertex -> Not supported by raylib
+                    if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_joints) // JOINTS_n (vec4: 4 bones max per vertex / u8, u16)
+                    {
+                        hasJoints = true;
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        // NOTE: JOINTS_n can only be vec4 and u8/u16
+                        // SPECS: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview
+
+                        // WARNING: raylib only supports model.meshes[].boneIds as u8 (unsigned char),
+                        // if data is provided in any other format, it is converted to supported format but
+                        // it could imply data loss (a warning message is issued in that case)
+
+                        if (attribute->type == cgltf_type_vec4)
+                        {
+                            if (attribute->component_type == cgltf_component_type_r_8u)
+                            {
+                                // Init raylib mesh boneIds to copy glTF attribute data
+                                model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+
+                                // Load attribute: vec4, u8 (unsigned char)
+                                LOAD_ATTRIBUTE(attribute, 4, unsigned char, model.meshes[meshIndex].boneIds)
+                            }
+                            else if (attribute->component_type == cgltf_component_type_r_16u)
+                            {
+                                // Init raylib mesh boneIds to copy glTF attribute data
+                                model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned short *temp = (unsigned short *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned short));
+                                LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
+
+                                // Convert data to raylib color data type (4 bytes)
+                                bool boneIdOverflowWarning = false;
+                                for (int b = 0; b < model.meshes[meshIndex].vertexCount*4; b++)
+                                {
+                                    if ((temp[b] > 255) && !boneIdOverflowWarning)
+                                    {
+                                        TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format (u16) overflow", fileName);
+                                        boneIdOverflowWarning = true;
+                                    }
+
+                                    // Despite the possible overflow, we convert data to unsigned char
+                                    model.meshes[meshIndex].boneIds[b] = (unsigned char)temp[b];
+                                }
+
+                                RL_FREE(temp);
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName);
+                        }
+                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint attribute data format not supported", fileName);
+                    }
+                    else if (mesh->primitives[p].attributes[j].type == cgltf_attribute_type_weights)  // WEIGHTS_n (vec4, u8n/u16n/f32)
+                    {
+                        cgltf_accessor *attribute = mesh->primitives[p].attributes[j].data;
+
+                        if (attribute->type == cgltf_type_vec4)
+                        {
+                            if (attribute->component_type == cgltf_component_type_r_8u)
+                            {
+                                // Init raylib mesh bone weight to copy glTF attribute data
+                                model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned char *temp = (unsigned char *)RL_MALLOC(attribute->count*4*sizeof(unsigned char));
+                                LOAD_ATTRIBUTE(attribute, 4, unsigned char, temp);
+
+                                // Convert data to raylib bone weight data type (4 bytes)
+                                for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/255.0f;
+
+                                RL_FREE(temp);
+                            }
+                            else if (attribute->component_type == cgltf_component_type_r_16u)
+                            {
+                                // Init raylib mesh bone weight to copy glTF attribute data
+                                model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
+
+                                // Load data into a temp buffer to be converted to raylib data type
+                                unsigned short *temp = (unsigned short *)RL_MALLOC(attribute->count*4*sizeof(unsigned short));
+                                LOAD_ATTRIBUTE(attribute, 4, unsigned short, temp);
+
+                                // Convert data to raylib bone weight data type
+                                for (unsigned int b = 0; b < attribute->count*4; b++) model.meshes[meshIndex].boneWeights[b] = (float)temp[b]/65535.0f;
+
+                                RL_FREE(temp);
+                            }
+                            else if (attribute->component_type == cgltf_component_type_r_32f)
+                            {
+                                // Init raylib mesh bone weight to copy glTF attribute data
+                                model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
+
+                                // Load 4 components of float data type into mesh.boneWeights
+                                // for cgltf_attribute_type_weights we have:
+
+                                //   - data.meshes[0] (256 vertices)
+                                //   - 256 values, provided as cgltf_type_vec4 of float (4 byte per joint, stride 16)
+                                LOAD_ATTRIBUTE(attribute, 4, float, model.meshes[meshIndex].boneWeights)
+                            }
+                            else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName);
+                        }
+                        else TRACELOG(LOG_WARNING, "MODEL: [%s] Joint weight attribute data format not supported, use vec4 float", fileName);
+                    }
+                }
+
+                // Check if we are animated, and the mesh was not given any bone assignments, but is the child of a bone node
+                // in this case we need to fully attach all the verts to the parent bone so it will animate with the bone
+                if (data->skins_count > 0 && !hasJoints && node->parent != NULL && node->parent->mesh == NULL)
+                {
+                    int parentBoneId = -1;
+                    for (int joint = 0; joint < model.boneCount; joint++)
+                    {
+                        if (data->skins[0].joints[joint] == node->parent)
+                        {
+                            parentBoneId = joint;
+                            break;
+                        }
+                    }
+
+                    if (parentBoneId >= 0)
+                    {
+                        model.meshes[meshIndex].boneIds = (unsigned char *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(unsigned char));
+                        model.meshes[meshIndex].boneWeights = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*4, sizeof(float));
+
+                        for (int vertexIndex = 0; vertexIndex < model.meshes[meshIndex].vertexCount*4; vertexIndex += 4)
+                        {
+                            model.meshes[meshIndex].boneIds[vertexIndex] = (unsigned char)parentBoneId;
+                            model.meshes[meshIndex].boneWeights[vertexIndex] = 1.0f;
+                        }
+                    }
+                }
+
+                // Animated vertex data
+                model.meshes[meshIndex].animVertices = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
+                memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float));
+                model.meshes[meshIndex].animNormals = (float *)RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float));
+                if (model.meshes[meshIndex].normals != NULL)
+                {
+                    memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float));
+                }
+
+                // Bone Transform Matrices
+                model.meshes[meshIndex].boneCount = model.boneCount;
+                model.meshes[meshIndex].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[meshIndex].boneCount, sizeof(Matrix));
+
+                for (int j = 0; j < model.meshes[meshIndex].boneCount; j++)
+                {
+                    model.meshes[meshIndex].boneMatrices[j] = MatrixIdentity();
+                }
+
+                meshIndex++; // Move to next mesh
+            }
+        }
+        //----------------------------------------------------------------------------------------------------
+
+        // Free all cgltf loaded data
+        cgltf_free(data);
+    }
+    else TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
+
+    // WARNING: cgltf requires the file pointer available while reading data
+    UnloadFileData(fileData);
+
+    return model;
+}
+
+// Get interpolated pose for bone sampler at a specific time. Returns true on success
+static bool GetPoseAtTimeGLTF(cgltf_interpolation_type interpolationType, cgltf_accessor *input, cgltf_accessor *output, float time, void *data)
+{
+    if (interpolationType >= cgltf_interpolation_type_max_enum) return false;
+
+    // Input and output should have the same count
+    float tstart = 0.0f;
+    float tend = 0.0f;
+    int keyframe = 0;       // Defaults to first pose
+
+    for (int i = 0; i < (int)input->count - 1; i++)
+    {
+        cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1);
+        if (!r1) return false;
+
+        cgltf_bool r2 = cgltf_accessor_read_float(input, i + 1, &tend, 1);
+        if (!r2) return false;
+
+        if ((tstart <= time) && (time < tend))
+        {
+            keyframe = i;
+            break;
+        }
+    }
+
+    // Constant animation, no need to interpolate
+    if (FloatEquals(tend, tstart)) return true;
+
+    float duration = fmaxf((tend - tstart), EPSILON);
+    float t = (time - tstart)/duration;
+    t = (t < 0.0f)? 0.0f : t;
+    t = (t > 1.0f)? 1.0f : t;
+
+    if (output->component_type != cgltf_component_type_r_32f) return false;
+
+    if (output->type == cgltf_type_vec3)
+    {
+        switch (interpolationType)
+        {
+            case cgltf_interpolation_type_step:
+            {
+                float tmp[3] = { 0.0f };
+                cgltf_accessor_read_float(output, keyframe, tmp, 3);
+                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
+                Vector3 *r = (Vector3 *)data;
+
+                *r = v1;
+            } break;
+            case cgltf_interpolation_type_linear:
+            {
+                float tmp[3] = { 0.0f };
+                cgltf_accessor_read_float(output, keyframe, tmp, 3);
+                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
+                cgltf_accessor_read_float(output, keyframe+1, tmp, 3);
+                Vector3 v2 = {tmp[0], tmp[1], tmp[2]};
+                Vector3 *r = (Vector3 *)data;
+
+                *r = Vector3Lerp(v1, v2, t);
+            } break;
+            case cgltf_interpolation_type_cubic_spline:
+            {
+                float tmp[3] = { 0.0f };
+                cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 3);
+                Vector3 v1 = {tmp[0], tmp[1], tmp[2]};
+                cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 3);
+                Vector3 tangent1 = {tmp[0], tmp[1], tmp[2]};
+                cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 3);
+                Vector3 v2 = {tmp[0], tmp[1], tmp[2]};
+                cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 3);
+                Vector3 tangent2 = {tmp[0], tmp[1], tmp[2]};
+                Vector3 *r = (Vector3 *)data;
+
+                *r = Vector3CubicHermite(v1, tangent1, v2, tangent2, t);
+            } break;
+            default: break;
+        }
+    }
+    else if (output->type == cgltf_type_vec4)
+    {
+        // Only v4 is for rotations, so we know it's a quaternion
+        switch (interpolationType)
+        {
+            case cgltf_interpolation_type_step:
+            {
+                float tmp[4] = { 0.0f };
+                cgltf_accessor_read_float(output, keyframe, tmp, 4);
+                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
+                Vector4 *r = (Vector4 *)data;
+
+                *r = v1;
+            } break;
+            case cgltf_interpolation_type_linear:
+            {
+                float tmp[4] = { 0.0f };
+                cgltf_accessor_read_float(output, keyframe, tmp, 4);
+                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
+                cgltf_accessor_read_float(output, keyframe+1, tmp, 4);
+                Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
+                Vector4 *r = (Vector4 *)data;
+
+                *r = QuaternionSlerp(v1, v2, t);
+            } break;
+            case cgltf_interpolation_type_cubic_spline:
+            {
+                float tmp[4] = { 0.0f };
+                cgltf_accessor_read_float(output, 3*keyframe+1, tmp, 4);
+                Vector4 v1 = {tmp[0], tmp[1], tmp[2], tmp[3]};
+                cgltf_accessor_read_float(output, 3*keyframe+2, tmp, 4);
+                Vector4 outTangent1 = {tmp[0], tmp[1], tmp[2], 0.0f};
+                cgltf_accessor_read_float(output, 3*(keyframe+1)+1, tmp, 4);
+                Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]};
+                cgltf_accessor_read_float(output, 3*(keyframe+1), tmp, 4);
+                Vector4 inTangent2 = {tmp[0], tmp[1], tmp[2], 0.0f};
+                Vector4 *r = (Vector4 *)data;
+
+                v1 = QuaternionNormalize(v1);
+                v2 = QuaternionNormalize(v2);
+
+                if (Vector4DotProduct(v1, v2) < 0.0f)
+                {
+                    v2 = Vector4Negate(v2);
+                }
+
+                outTangent1 = Vector4Scale(outTangent1, duration);
+                inTangent2 = Vector4Scale(inTangent2, duration);
+
+                *r = QuaternionCubicHermiteSpline(v1, outTangent1, v2, inTangent2, t);
+            } break;
+            default: break;
+        }
+    }
+
+    return true;
+}
+
+#define GLTF_FRAMERATE 60.0f    // glTF animation framerate (frames per second)
+
+static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount)
+{
+    // glTF file loading
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+    ModelAnimation *animations = NULL;
+
+    // glTF data loading
+    cgltf_options options = { 0 };
+    options.file.read = LoadFileGLTFCallback;
+    options.file.release = ReleaseFileGLTFCallback;
+    cgltf_data *data = NULL;
+    cgltf_result result = cgltf_parse(&options, fileData, dataSize, &data);
+
+    if (result != cgltf_result_success)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load glTF data", fileName);
+        *animCount = 0;
+        return NULL;
+    }
+
+    result = cgltf_load_buffers(&options, data, fileName);
+    if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load animation buffers", fileName);
+
+    if (result == cgltf_result_success)
+    {
+        if (data->skins_count > 0)
+        {
+            cgltf_skin skin = data->skins[0];
+            *animCount = (int)data->animations_count;
+            animations = (ModelAnimation *)RL_CALLOC(data->animations_count, sizeof(ModelAnimation));
+
+            Transform worldTransform = { 0 };
+            cgltf_float cgltf_worldTransform[16] = { 0 };
+            cgltf_node *node = skin.joints[0];
+            cgltf_node_transform_world(node->parent, cgltf_worldTransform);
+            Matrix worldMatrix = {
+                cgltf_worldTransform[0], cgltf_worldTransform[4], cgltf_worldTransform[8], cgltf_worldTransform[12],
+                cgltf_worldTransform[1], cgltf_worldTransform[5], cgltf_worldTransform[9], cgltf_worldTransform[13],
+                cgltf_worldTransform[2], cgltf_worldTransform[6], cgltf_worldTransform[10], cgltf_worldTransform[14],
+                cgltf_worldTransform[3], cgltf_worldTransform[7], cgltf_worldTransform[11], cgltf_worldTransform[15]
+            };
+            MatrixDecompose(worldMatrix, &worldTransform.translation, &worldTransform.rotation, &worldTransform.scale);
+
+            for (unsigned int i = 0; i < data->animations_count; i++)
+            {
+                animations[i].bones = LoadBoneInfoGLTF(skin, &animations[i].boneCount);
+
+                cgltf_animation animData = data->animations[i];
+
+                struct Channels {
+                    cgltf_animation_channel *translate;
+                    cgltf_animation_channel *rotate;
+                    cgltf_animation_channel *scale;
+                    cgltf_interpolation_type interpolationType;
+                };
+
+                struct Channels *boneChannels = (struct Channels *)RL_CALLOC(animations[i].boneCount, sizeof(struct Channels));
+                float animDuration = 0.0f;
+
+                for (unsigned int j = 0; j < animData.channels_count; j++)
+                {
+                    cgltf_animation_channel channel = animData.channels[j];
+                    int boneIndex = -1;
+
+                    for (unsigned int k = 0; k < skin.joints_count; k++)
+                    {
+                        if (animData.channels[j].target_node == skin.joints[k])
+                        {
+                            boneIndex = k;
+                            break;
+                        }
+                    }
+
+                    if (boneIndex == -1)
+                    {
+                        // Animation channel for a node not in the armature
+                        continue;
+                    }
+
+                    boneChannels[boneIndex].interpolationType = animData.channels[j].sampler->interpolation;
+
+                    if (animData.channels[j].sampler->interpolation != cgltf_interpolation_type_max_enum)
+                    {
+                        if (channel.target_path == cgltf_animation_path_type_translation)
+                        {
+                            boneChannels[boneIndex].translate = &animData.channels[j];
+                        }
+                        else if (channel.target_path == cgltf_animation_path_type_rotation)
+                        {
+                            boneChannels[boneIndex].rotate = &animData.channels[j];
+                        }
+                        else if (channel.target_path == cgltf_animation_path_type_scale)
+                        {
+                            boneChannels[boneIndex].scale = &animData.channels[j];
+                        }
+                        else
+                        {
+                            TRACELOG(LOG_WARNING, "MODEL: [%s] Unsupported target_path on channel %d's sampler for animation %d. Skipping.", fileName, j, i);
+                        }
+                    }
+                    else TRACELOG(LOG_WARNING, "MODEL: [%s] Invalid interpolation curve encountered for GLTF animation.", fileName);
+
+                    float t = 0.0f;
+                    cgltf_bool r = cgltf_accessor_read_float(channel.sampler->input, channel.sampler->input->count - 1, &t, 1);
+
+                    if (!r)
+                    {
+                        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load input time", fileName);
+                        continue;
+                    }
+
+                    animDuration = (t > animDuration)? t : animDuration;
+                }
+
+                if (animData.name != NULL) strncpy(animations[i].name, animData.name, sizeof(animations[i].name) - 1);
+
+                animations[i].frameCount = (int)(animDuration*GLTF_FRAMERATE) + 1;
+                animations[i].framePoses = (Transform **)RL_MALLOC(animations[i].frameCount*sizeof(Transform *));
+
+                for (int j = 0; j < animations[i].frameCount; j++)
+                {
+                    animations[i].framePoses[j] = (Transform *)RL_MALLOC(animations[i].boneCount*sizeof(Transform));
+                    float time = (float)j / GLTF_FRAMERATE;
+
+                    for (int k = 0; k < animations[i].boneCount; k++)
+                    {
+                        Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]};
+                        Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]};
+                        Vector3 scale = {skin.joints[k]->scale[0], skin.joints[k]->scale[1], skin.joints[k]->scale[2]};
+
+                        if (boneChannels[k].translate)
+                        {
+                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].translate->sampler->input, boneChannels[k].translate->sampler->output, time, &translation))
+                            {
+                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load translate pose data for bone %s", fileName, animations[i].bones[k].name);
+                            }
+                        }
+
+                        if (boneChannels[k].rotate)
+                        {
+                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].rotate->sampler->input, boneChannels[k].rotate->sampler->output, time, &rotation))
+                            {
+                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load rotate pose data for bone %s", fileName, animations[i].bones[k].name);
+                            }
+                        }
+
+                        if (boneChannels[k].scale)
+                        {
+                            if (!GetPoseAtTimeGLTF(boneChannels[k].interpolationType, boneChannels[k].scale->sampler->input, boneChannels[k].scale->sampler->output, time, &scale))
+                            {
+                                TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load scale pose data for bone %s", fileName, animations[i].bones[k].name);
+                            }
+                        }
+
+                        animations[i].framePoses[j][k] = (Transform){
+                            .translation = translation,
+                            .rotation = rotation,
+                            .scale = scale
+                        };
+                    }
+
+                    Transform* root = &animations[i].framePoses[j][0];
+                    root->rotation = QuaternionMultiply(worldTransform.rotation, root->rotation);
+                    root->scale = Vector3Multiply(root->scale, worldTransform.scale);
+                    root->translation = Vector3Multiply(root->translation, worldTransform.scale);
+                    root->translation = Vector3RotateByQuaternion(root->translation, worldTransform.rotation);
+                    root->translation = Vector3Add(root->translation, worldTransform.translation);
+
+                    BuildPoseFromParentJoints(animations[i].bones, animations[i].boneCount, animations[i].framePoses[j]);
+                }
+
+                TRACELOG(LOG_INFO, "MODEL: [%s] Loaded animation: %s (%d frames, %fs)", fileName, (animData.name != NULL)? animData.name : "NULL", animations[i].frameCount, animDuration);
+                RL_FREE(boneChannels);
+            }
+        }
+
+        if (data->skins_count > 1)
+        {
+            TRACELOG(LOG_WARNING, "MODEL: [%s] expected exactly one skin to load animation data from, but found %i", fileName, data->skins_count);
+        }
+
+        cgltf_free(data);
+    }
+    UnloadFileData(fileData);
+    return animations;
+}
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_VOX)
+// Load VOX (MagicaVoxel) mesh data
+static Model LoadVOX(const char *fileName)
+{
+    Model model = { 0 };
+
+    int nbvertices = 0;
+    int meshescount = 0;
+
+    // Read vox file into buffer
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+    if (fileData == 0)
+    {
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName);
+        return model;
+    }
+
+    // Read and build voxarray description
+    VoxArray3D voxarray = { 0 };
+    int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray);
+
+    if (ret != VOX_SUCCESS)
+    {
+        // Error
+        UnloadFileData(fileData);
+
+        TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX data", fileName);
+        return model;
+    }
+    else
+    {
+        // Success: Compute meshes count
+        nbvertices = voxarray.vertices.used;
+        meshescount = 1 + (nbvertices/65536);
+
+        TRACELOG(LOG_INFO, "MODEL: [%s] VOX data loaded successfully : %i vertices/%i meshes", fileName, nbvertices, meshescount);
+    }
+
+    // Build models from meshes
+    model.transform = MatrixIdentity();
+
+    model.meshCount = meshescount;
+    model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+
+    model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+
+    model.materialCount = 1;
+    model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material));
+    model.materials[0] = LoadMaterialDefault();
+
+    // Init model meshes
+    int verticesRemain = voxarray.vertices.used;
+    int verticesMax = 65532; // 5461 voxels x 12 vertices per voxel -> 65532 (must be inf 65536)
+
+    // 6*4 = 12 vertices per voxel
+    Vector3 *pvertices = (Vector3 *)voxarray.vertices.array;
+    Vector3 *pnormals = (Vector3 *)voxarray.normals.array;
+    Color *pcolors = (Color *)voxarray.colors.array;
+
+    unsigned short *pindices = voxarray.indices.array;    // 5461*6*6 = 196596 indices max per mesh
+
+    int size = 0;
+
+    for (int i = 0; i < meshescount; i++)
+    {
+        Mesh *pmesh = &model.meshes[i];
+        memset(pmesh, 0, sizeof(Mesh));
+
+        // Copy vertices
+        pmesh->vertexCount = (int)fmin(verticesMax, verticesRemain);
+
+        size = pmesh->vertexCount*sizeof(float)*3;
+        pmesh->vertices = (float *)RL_MALLOC(size);
+        memcpy(pmesh->vertices, pvertices, size);
+
+        // Copy normals
+        pmesh->normals = (float *)RL_MALLOC(size);
+        memcpy(pmesh->normals, pnormals, size);
+
+        // Copy indices
+        size = voxarray.indices.used*sizeof(unsigned short);
+        pmesh->indices = (unsigned short *)RL_MALLOC(size);
+        memcpy(pmesh->indices, pindices, size);
+
+        pmesh->triangleCount = (pmesh->vertexCount/4)*2;
+
+        // Copy colors
+        size = pmesh->vertexCount*sizeof(Color);
+        pmesh->colors = (unsigned char *)RL_MALLOC(size);
+        memcpy(pmesh->colors, pcolors, size);
+
+        // First material index
+        model.meshMaterial[i] = 0;
+
+        verticesRemain -= verticesMax;
+        pvertices += verticesMax;
+        pnormals += verticesMax;
+        pcolors += verticesMax;
+    }
+
+    // Free buffers
+    Vox_FreeArrays(&voxarray);
+    UnloadFileData(fileData);
+
+    return model;
+}
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_M3D)
+// Hook LoadFileData()/UnloadFileData() calls to M3D loaders
+unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); }
+void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); }
+
+// Load M3D mesh data
+static Model LoadM3D(const char *fileName)
+{
+    Model model = { 0 };
+
+    m3d_t *m3d = NULL;
+    m3dp_t *prop = NULL;
+    int i, j, k, l, n, mi = -2, vcolor = 0;
+
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+    if (fileData != NULL)
+    {
+        m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
+
+        if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
+        {
+            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2);
+            if (m3d) m3d_free(m3d);
+            UnloadFileData(fileData);
+            return model;
+        }
+        else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i faces/%i materials", fileName, m3d->numface, m3d->nummaterial);
+
+        // no face? this is probably just a material library
+        if (!m3d->numface)
+        {
+            m3d_free(m3d);
+            UnloadFileData(fileData);
+            return model;
+        }
+
+        if (m3d->nummaterial > 0)
+        {
+            model.meshCount = model.materialCount = m3d->nummaterial;
+            TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", model.materialCount);
+        }
+        else
+        {
+            model.meshCount = 1; model.materialCount = 0;
+            TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material");
+        }
+
+        // We always need a default material, so we add +1
+        model.materialCount++;
+
+        // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise
+        // WARNING: Sorting is not needed, valid M3D model files should already be sorted
+        // Just keeping the sorting function for reference (Check PR #3363 #3385)
+        /*
+        for (i = 1; i < m3d->numface; i++)
+        {
+            if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue;
+
+            // face[i-1] > face[i].  slide face[i] lower
+            m3df_t slider = m3d->face[i];
+            j = i-1;
+
+            do
+            {   // face[j] > slider, face[j+1] is svailable vacant gap
+                m3d->face[j+1] = m3d->face[j];
+                j = j-1;
+            }
+            while (j >= 0 && m3d->face[j].materialid > slider.materialid);
+
+            m3d->face[j+1] = slider;
+        }
+        */
+
+        model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+        model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+        model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material));
+
+        // Map no material to index 0 with default shader, everything else materialid + 1
+        model.materials[0] = LoadMaterialDefault();
+
+        for (i = l = 0, k = -1; i < (int)m3d->numface; i++, l++)
+        {
+            // Materials are grouped together
+            if (mi != m3d->face[i].materialid)
+            {
+                // There should be only one material switch per material kind,
+                // but be bulletproof for non-optimal model files
+                if (k + 1 >= model.meshCount)
+                {
+                    model.meshCount++;
+
+                    // Create a second buffer for mesh re-allocation
+                    Mesh *tempMeshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
+                    memcpy(tempMeshes, model.meshes, (model.meshCount - 1)*sizeof(Mesh));
+                    RL_FREE(model.meshes);
+                    model.meshes = tempMeshes;
+
+                    // Create a second buffer for material re-allocation
+                    int *tempMeshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int));
+                    memcpy(tempMeshMaterial, model.meshMaterial, (model.meshCount - 1)*sizeof(int));
+                    RL_FREE(model.meshMaterial);
+                    model.meshMaterial = tempMeshMaterial;
+                }
+
+                k++;
+                mi = m3d->face[i].materialid;
+
+                // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch
+                // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors
+                for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++)
+                {
+                    if (!m3d->vertex[m3d->face[j].vertex[0]].color ||
+                        !m3d->vertex[m3d->face[j].vertex[1]].color ||
+                        !m3d->vertex[m3d->face[j].vertex[2]].color) vcolor = 1;
+                }
+
+                model.meshes[k].vertexCount = l*3;
+                model.meshes[k].triangleCount = l;
+                model.meshes[k].vertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
+                model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float));
+                model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
+
+                // If no map is provided, or we have colors defined, we allocate storage for vertex colors
+                // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors
+                if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
+
+                // If no map is provided and we allocated vertex colors, set them to white
+                if ((mi == M3D_UNDEF) && (model.meshes[k].colors != NULL))
+                {
+                    for (int c = 0; c < model.meshes[k].vertexCount*4; c++) model.meshes[k].colors[c] = 255;
+                }
+
+                if (m3d->numbone && m3d->numskin)
+                {
+                    model.meshes[k].boneIds = (unsigned char *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char));
+                    model.meshes[k].boneWeights = (float *)RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(float));
+                    model.meshes[k].animVertices = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
+                    model.meshes[k].animNormals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float));
+                }
+
+                model.meshMaterial[k] = mi + 1;
+                l = 0;
+            }
+
+            // Process meshes per material, add triangles
+            model.meshes[k].vertices[l*9 + 0] = m3d->vertex[m3d->face[i].vertex[0]].x*m3d->scale;
+            model.meshes[k].vertices[l*9 + 1] = m3d->vertex[m3d->face[i].vertex[0]].y*m3d->scale;
+            model.meshes[k].vertices[l*9 + 2] = m3d->vertex[m3d->face[i].vertex[0]].z*m3d->scale;
+            model.meshes[k].vertices[l*9 + 3] = m3d->vertex[m3d->face[i].vertex[1]].x*m3d->scale;
+            model.meshes[k].vertices[l*9 + 4] = m3d->vertex[m3d->face[i].vertex[1]].y*m3d->scale;
+            model.meshes[k].vertices[l*9 + 5] = m3d->vertex[m3d->face[i].vertex[1]].z*m3d->scale;
+            model.meshes[k].vertices[l*9 + 6] = m3d->vertex[m3d->face[i].vertex[2]].x*m3d->scale;
+            model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale;
+            model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale;
+
+            // Without vertex color (full transparency), we use the default color
+            if (model.meshes[k].colors != NULL)
+            {
+                if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xff000000)
+                    memcpy(&model.meshes[k].colors[l*12 + 0], &m3d->vertex[m3d->face[i].vertex[0]].color, 4);
+                if (m3d->vertex[m3d->face[i].vertex[1]].color & 0xff000000)
+                    memcpy(&model.meshes[k].colors[l*12 + 4], &m3d->vertex[m3d->face[i].vertex[1]].color, 4);
+                if (m3d->vertex[m3d->face[i].vertex[2]].color & 0xff000000)
+                    memcpy(&model.meshes[k].colors[l*12 + 8], &m3d->vertex[m3d->face[i].vertex[2]].color, 4);
+            }
+
+            if (m3d->face[i].texcoord[0] != M3D_UNDEF)
+            {
+                model.meshes[k].texcoords[l*6 + 0] = m3d->tmap[m3d->face[i].texcoord[0]].u;
+                model.meshes[k].texcoords[l*6 + 1] = 1.0f - m3d->tmap[m3d->face[i].texcoord[0]].v;
+                model.meshes[k].texcoords[l*6 + 2] = m3d->tmap[m3d->face[i].texcoord[1]].u;
+                model.meshes[k].texcoords[l*6 + 3] = 1.0f - m3d->tmap[m3d->face[i].texcoord[1]].v;
+                model.meshes[k].texcoords[l*6 + 4] = m3d->tmap[m3d->face[i].texcoord[2]].u;
+                model.meshes[k].texcoords[l*6 + 5] = 1.0f - m3d->tmap[m3d->face[i].texcoord[2]].v;
+            }
+
+            if (m3d->face[i].normal[0] != M3D_UNDEF)
+            {
+                model.meshes[k].normals[l*9 + 0] = m3d->vertex[m3d->face[i].normal[0]].x;
+                model.meshes[k].normals[l*9 + 1] = m3d->vertex[m3d->face[i].normal[0]].y;
+                model.meshes[k].normals[l*9 + 2] = m3d->vertex[m3d->face[i].normal[0]].z;
+                model.meshes[k].normals[l*9 + 3] = m3d->vertex[m3d->face[i].normal[1]].x;
+                model.meshes[k].normals[l*9 + 4] = m3d->vertex[m3d->face[i].normal[1]].y;
+                model.meshes[k].normals[l*9 + 5] = m3d->vertex[m3d->face[i].normal[1]].z;
+                model.meshes[k].normals[l*9 + 6] = m3d->vertex[m3d->face[i].normal[2]].x;
+                model.meshes[k].normals[l*9 + 7] = m3d->vertex[m3d->face[i].normal[2]].y;
+                model.meshes[k].normals[l*9 + 8] = m3d->vertex[m3d->face[i].normal[2]].z;
+            }
+
+            // Add skin (vertex / bone weight pairs)
+            if (m3d->numbone && m3d->numskin)
+            {
+                for (n = 0; n < 3; n++)
+                {
+                    int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid;
+
+                    // Check if there is a skin for this mesh, should be, just failsafe
+                    if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin))
+                    {
+                        for (j = 0; j < 4; j++)
+                        {
+                            model.meshes[k].boneIds[l*12 + n*4 + j] = m3d->skin[skinid].boneid[j];
+                            model.meshes[k].boneWeights[l*12 + n*4 + j] = m3d->skin[skinid].weight[j];
+                        }
+                    }
+                    else
+                    {
+                        // raylib does not handle boneless meshes with skeletal animations, so
+                        // we put all vertices without a bone into a special "no bone" bone
+                        model.meshes[k].boneIds[l*12 + n*4] = m3d->numbone;
+                        model.meshes[k].boneWeights[l*12 + n*4] = 1.0f;
+                    }
+                }
+            }
+        }
+
+        // Load materials
+        for (i = 0; i < (int)m3d->nummaterial; i++)
+        {
+            model.materials[i + 1] = LoadMaterialDefault();
+
+            for (j = 0; j < m3d->material[i].numprop; j++)
+            {
+                prop = &m3d->material[i].prop[j];
+
+                switch (prop->type)
+                {
+                    case m3dp_Kd:
+                    {
+                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].color, &prop->value.color, 4);
+                        model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f;
+                    } break;
+                    case m3dp_Ks:
+                    {
+                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].color, &prop->value.color, 4);
+                    } break;
+                    case m3dp_Ns:
+                    {
+                        model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].value = prop->value.fnum;
+                    } break;
+                    case m3dp_Ke:
+                    {
+                        memcpy(&model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].color, &prop->value.color, 4);
+                        model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].value = 0.0f;
+                    } break;
+                    case m3dp_Pm:
+                    {
+                        model.materials[i + 1].maps[MATERIAL_MAP_METALNESS].value = prop->value.fnum;
+                    } break;
+                    case m3dp_Pr:
+                    {
+                        model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].value = prop->value.fnum;
+                    } break;
+                    case m3dp_Ps:
+                    {
+                        model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].color = WHITE;
+                        model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].value = prop->value.fnum;
+                    } break;
+                    default:
+                    {
+                        if (prop->type >= 128)
+                        {
+                            Image image = { 0 };
+                            image.data = m3d->texture[prop->value.textureid].d;
+                            image.width = m3d->texture[prop->value.textureid].w;
+                            image.height = m3d->texture[prop->value.textureid].h;
+                            image.mipmaps = 1;
+                            image.format = (m3d->texture[prop->value.textureid].f == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 :
+                                           ((m3d->texture[prop->value.textureid].f == 3)? PIXELFORMAT_UNCOMPRESSED_R8G8B8 :
+                                           ((m3d->texture[prop->value.textureid].f == 2)? PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA : PIXELFORMAT_UNCOMPRESSED_GRAYSCALE));
+
+                            switch (prop->type)
+                            {
+                                case m3dp_map_Kd: model.materials[i + 1].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTextureFromImage(image); break;
+                                case m3dp_map_Ks: model.materials[i + 1].maps[MATERIAL_MAP_SPECULAR].texture = LoadTextureFromImage(image); break;
+                                case m3dp_map_Ke: model.materials[i + 1].maps[MATERIAL_MAP_EMISSION].texture = LoadTextureFromImage(image); break;
+                                case m3dp_map_Km: model.materials[i + 1].maps[MATERIAL_MAP_NORMAL].texture = LoadTextureFromImage(image); break;
+                                case m3dp_map_Ka: model.materials[i + 1].maps[MATERIAL_MAP_OCCLUSION].texture = LoadTextureFromImage(image); break;
+                                case m3dp_map_Pm: model.materials[i + 1].maps[MATERIAL_MAP_ROUGHNESS].texture = LoadTextureFromImage(image); break;
+                                default: break;
+                            }
+                        }
+                    } break;
+                }
+            }
+        }
+
+        // Load bones
+        if (m3d->numbone)
+        {
+            model.boneCount = m3d->numbone + 1;
+            model.bones = (BoneInfo *)RL_CALLOC(model.boneCount, sizeof(BoneInfo));
+            model.bindPose = (Transform *)RL_CALLOC(model.boneCount, sizeof(Transform));
+
+            for (i = 0; i < (int)m3d->numbone; i++)
+            {
+                model.bones[i].parent = m3d->bone[i].parent;
+                strncpy(model.bones[i].name, m3d->bone[i].name, sizeof(model.bones[i].name) - 1);
+                model.bindPose[i].translation.x = m3d->vertex[m3d->bone[i].pos].x*m3d->scale;
+                model.bindPose[i].translation.y = m3d->vertex[m3d->bone[i].pos].y*m3d->scale;
+                model.bindPose[i].translation.z = m3d->vertex[m3d->bone[i].pos].z*m3d->scale;
+                model.bindPose[i].rotation.x = m3d->vertex[m3d->bone[i].ori].x;
+                model.bindPose[i].rotation.y = m3d->vertex[m3d->bone[i].ori].y;
+                model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z;
+                model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w;
+
+                // TODO: If the orientation quaternion is not normalized, then that's encoding scaling
+                model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation);
+                model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
+
+                // Child bones are stored in parent bone relative space, convert that into model space
+                if (model.bones[i].parent >= 0)
+                {
+                    model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation);
+                    model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation);
+                    model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation);
+                    model.bindPose[i].scale = Vector3Multiply(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale);
+                }
+            }
+
+            // Add a special "no bone" bone
+            model.bones[i].parent = -1;
+            memcpy(model.bones[i].name, "NO BONE", 7);
+            model.bindPose[i].translation.x = 0.0f;
+            model.bindPose[i].translation.y = 0.0f;
+            model.bindPose[i].translation.z = 0.0f;
+            model.bindPose[i].rotation.x = 0.0f;
+            model.bindPose[i].rotation.y = 0.0f;
+            model.bindPose[i].rotation.z = 0.0f;
+            model.bindPose[i].rotation.w = 1.0f;
+            model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f;
+        }
+
+        // Load bone-pose default mesh into animation vertices. These will be updated when UpdateModelAnimation gets
+        // called, but not before, however DrawMesh uses these if they exist (so not good if they are left empty)
+        if (m3d->numbone && m3d->numskin)
+        {
+            for (i = 0; i < model.meshCount; i++)
+            {
+                memcpy(model.meshes[i].animVertices, model.meshes[i].vertices, model.meshes[i].vertexCount*3*sizeof(float));
+                memcpy(model.meshes[i].animNormals, model.meshes[i].normals, model.meshes[i].vertexCount*3*sizeof(float));
+
+                model.meshes[i].boneCount = model.boneCount;
+                model.meshes[i].boneMatrices = (Matrix *)RL_CALLOC(model.meshes[i].boneCount, sizeof(Matrix));
+                for (j = 0; j < model.meshes[i].boneCount; j++)
+                {
+                    model.meshes[i].boneMatrices[j] = MatrixIdentity();
+                }
+            }
+        }
+
+        m3d_free(m3d);
+        UnloadFileData(fileData);
+    }
+
+    return model;
+}
+
+#define M3D_ANIMDELAY 17    // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms)
+
+// Load M3D animation data
+static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount)
+{
+    ModelAnimation *animations = NULL;
+
+    m3d_t *m3d = NULL;
+    int i = 0, j = 0;
+    *animCount = 0;
+
+    int dataSize = 0;
+    unsigned char *fileData = LoadFileData(fileName, &dataSize);
+
+    if (fileData != NULL)
+    {
+        m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL);
+
+        if (!m3d || M3D_ERR_ISFATAL(m3d->errcode))
+        {
+            TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2);
+            UnloadFileData(fileData);
+            return NULL;
+        }
+        else TRACELOG(LOG_INFO, "MODEL: [%s] M3D data loaded successfully: %i animations, %i bones, %i skins", fileName,
+            m3d->numaction, m3d->numbone, m3d->numskin);
+
+        // No animation or bones, exit out. skins are not required because some people use one animation for N models
+        if (!m3d->numaction || !m3d->numbone)
+        {
+            m3d_free(m3d);
+            UnloadFileData(fileData);
+            return NULL;
+        }
+
+        animations = (ModelAnimation *)RL_CALLOC(m3d->numaction, sizeof(ModelAnimation));
+        *animCount = m3d->numaction;
+
+        for (unsigned int a = 0; a < m3d->numaction; a++)
+        {
+            animations[a].frameCount = m3d->action[a].durationmsec/M3D_ANIMDELAY;
+            animations[a].boneCount = m3d->numbone + 1;
+            animations[a].bones = (BoneInfo *)RL_MALLOC((m3d->numbone + 1)*sizeof(BoneInfo));
+            animations[a].framePoses = (Transform **)RL_MALLOC(animations[a].frameCount*sizeof(Transform *));
+            strncpy(animations[a].name, m3d->action[a].name, sizeof(animations[a].name) - 1);
+
+            TRACELOG(LOG_INFO, "MODEL: [%s] animation #%i: %i msec, %i frames", fileName, a, m3d->action[a].durationmsec, animations[a].frameCount);
+
+            for (i = 0; i < (int)m3d->numbone; i++)
+            {
+                animations[a].bones[i].parent = m3d->bone[i].parent;
+                strncpy(animations[a].bones[i].name, m3d->bone[i].name, sizeof(animations[a].bones[i].name) - 1);
+            }
+
+            // A special, never transformed "no bone" bone, used for boneless vertices
+            animations[a].bones[i].parent = -1;
+            memcpy(animations[a].bones[i].name, "NO BONE", 7);
+
+            // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
+            // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones
+            for (i = 0; i < animations[a].frameCount; i++)
+            {
+                animations[a].framePoses[i] = (Transform *)RL_MALLOC((m3d->numbone + 1)*sizeof(Transform));
 
                 m3db_t *pose = m3d_pose(m3d, a, i*M3D_ANIMDELAY);
 
diff --git a/raylib/src/rshapes.c b/raylib/src/rshapes.c
--- a/raylib/src/rshapes.c
+++ b/raylib/src/rshapes.c
@@ -3,17 +3,17 @@
 *   rshapes - Basic functions to draw 2d shapes and check collisions
 *
 *   ADDITIONAL NOTES:
-*       Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS.
+*       Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS
 *       Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES
 *       are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define
 *
 *       Some functions define texture coordinates (rlTexCoord2f()) for the shapes and use a
 *       user-provided texture with SetShapesTexture(), the pourpouse of this implementation
-*       is allowing to reduce draw calls when combined with a texture-atlas.
+*       is allowing to reduce draw calls when combined with a texture-atlas
 *
 *       By default, raylib sets the default texture and rectangle at InitWindow()[rcore] to one
 *       white character of default font [rtext], this way, raylib text and shapes can be draw with
-*       a single draw call and it also allows users to configure it the same way with their own fonts.
+*       a single draw call and it also allows users to configure it the same way with their own fonts
 *
 *   CONFIGURATION:
 *       #define SUPPORT_MODULE_RSHAPES
@@ -25,7 +25,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -46,10 +46,7 @@
 
 #include "raylib.h"     // Declares module functions
 
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"         // Defines module configuration flags
-#endif
+#include "config.h"     // Defines module configuration flags
 
 #if defined(SUPPORT_MODULE_RSHAPES)
 
@@ -83,14 +80,13 @@
 static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f };    // Texture source rectangle used on shapes drawing
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static float EaseCubicInOut(float t, float b, float c, float d);    // Cubic easing
 
 //----------------------------------------------------------------------------------
 // Module Functions Definition
 //----------------------------------------------------------------------------------
-
 // Set texture and rectangle to be used on shapes drawing
 // NOTE: It can be useful when using basic shapes and one single font,
 // defining a font char white rectangle would allow drawing everything in a single draw call
@@ -183,6 +179,28 @@
     rlEnd();
 }
 
+// Draw a line defining thickness
+void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
+{
+    Vector2 delta = { endPos.x - startPos.x, endPos.y - startPos.y };
+    float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
+
+    if ((length > 0) && (thick > 0))
+    {
+        float scale = thick/(2*length);
+
+        Vector2 radius = { -scale*delta.y, scale*delta.x };
+        Vector2 strip[4] = {
+            { startPos.x - radius.x, startPos.y - radius.y },
+            { startPos.x + radius.x, startPos.y + radius.y },
+            { endPos.x - radius.x, endPos.y - radius.y },
+            { endPos.x + radius.x, endPos.y + radius.y }
+        };
+
+        DrawTriangleStrip(strip, 4, color);
+    }
+}
+
 // Draw a line (using gl lines)
 void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
 {
@@ -247,26 +265,50 @@
     DrawTriangleStrip(points, 2*SPLINE_SEGMENT_DIVISIONS + 2, color);
 }
 
-// Draw a line defining thickness
-void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color)
+// Draw a dashed line
+void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int spaceSize, Color color)
 {
-    Vector2 delta = { endPos.x - startPos.x, endPos.y - startPos.y };
-    float length = sqrtf(delta.x*delta.x + delta.y*delta.y);
+    // Calculate the vector and length of the line
+    float dx = endPos.x - startPos.x;
+    float dy = endPos.y - startPos.y;
+    float lineLength = sqrtf(dx*dx + dy*dy);
 
-    if ((length > 0) && (thick > 0))
+    // If the line is too short for dashing or dash size is invalid, draw a solid thick line
+    if ((lineLength < (dashSize + spaceSize)) || (dashSize <= 0))
     {
-        float scale = thick/(2*length);
+        DrawLineV(startPos, endPos, color);
+        return;
+    }
 
-        Vector2 radius = { -scale*delta.y, scale*delta.x };
-        Vector2 strip[4] = {
-            { startPos.x - radius.x, startPos.y - radius.y },
-            { startPos.x + radius.x, startPos.y + radius.y },
-            { endPos.x - radius.x, endPos.y - radius.y },
-            { endPos.x + radius.x, endPos.y + radius.y }
-        };
+    // Calculate the normalized direction vector of the line
+    float invLineLength = 1.0f/lineLength;
+    float dirX = dx*invLineLength;
+    float dirY = dy*invLineLength;
 
-        DrawTriangleStrip(strip, 4, color);
-    }
+    Vector2 currentPos = startPos;
+    float distanceTraveled = 0;
+
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
+
+        while (distanceTraveled < lineLength)
+        {
+            // Calculate the end of the current dash
+            float dashEndDist = distanceTraveled + dashSize;
+            if (dashEndDist > lineLength) dashEndDist = lineLength;
+
+            Vector2 dashEndPos = { startPos.x + dashEndDist*dirX, startPos.y + dashEndDist*dirY };
+
+            // Draw the dash segment
+            rlVertex2f(currentPos.x, currentPos.y);
+            rlVertex2f(dashEndPos.x, dashEndPos.y);
+
+            // Update the distance traveled and move the current position for the next dash
+            distanceTraveled = dashEndDist + spaceSize;
+            currentPos.x = startPos.x + distanceTraveled*dirX;
+            currentPos.y = startPos.y + distanceTraveled*dirY;
+        }
+    rlEnd();
 }
 
 // Draw a color-filled circle
@@ -285,6 +327,7 @@
 // Draw a piece of a circle
 void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
 {
+    if (startAngle == endAngle) return;
     if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero
 
     // Function expects (endAngle > startAngle)
@@ -376,6 +419,7 @@
 // Draw a piece of a circle outlines
 void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color)
 {
+    if (startAngle == endAngle) return;
     if (radius <= 0.0f) radius = 0.1f;  // Avoid div by zero issue
 
     // Function expects (endAngle > startAngle)
@@ -469,13 +513,19 @@
 // Draw ellipse
 void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color)
 {
+    DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color);
+}
+
+// Draw ellipse (Vector version)
+void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color)
+{
     rlBegin(RL_TRIANGLES);
         for (int i = 0; i < 360; i += 10)
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlVertex2f((float)centerX, (float)centerY);
-            rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV);
-            rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV);
+            rlVertex2f(center.x,  center.y);
+            rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV);
+            rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV);
         }
     rlEnd();
 }
@@ -483,12 +533,18 @@
 // Draw ellipse outline
 void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color)
 {
+    DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color);
+}
+
+// Draw ellipse outline
+void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color)
+{
     rlBegin(RL_LINES);
         for (int i = 0; i < 360; i += 10)
         {
             rlColor4ub(color.r, color.g, color.b, color.a);
-            rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV);
-            rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV);
+            rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV);
+            rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV);
         }
     rlEnd();
 }
@@ -772,7 +828,7 @@
 }
 
 // Draw a gradient-filled rectangle
-void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color topRight, Color bottomRight)
+void DrawRectangleGradientEx(Rectangle rec, Color topLeft, Color bottomLeft, Color bottomRight, Color topRight)
 {
     rlSetTexture(GetShapesTexture().id);
     Rectangle shapeRect = GetShapesTextureRectangle();
@@ -789,11 +845,11 @@
         rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
         rlVertex2f(rec.x, rec.y + rec.height);
 
-        rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
+        rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
         rlVertex2f(rec.x + rec.width, rec.y + rec.height);
 
-        rlColor4ub(bottomRight.r, bottomRight.g, bottomRight.b, bottomRight.a);
+        rlColor4ub(topRight.r, topRight.g, topRight.b, topRight.a);
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
         rlVertex2f(rec.x + rec.width, rec.y);
     rlEnd();
@@ -807,8 +863,35 @@
 // but it solves another issue: https://github.com/raysan5/raylib/issues/3884
 void DrawRectangleLines(int posX, int posY, int width, int height, Color color)
 {
+    Matrix mat = rlGetMatrixTransform();
+    float xOffset = 0.5f/mat.m0;
+    float yOffset = 0.5f/mat.m5;
+
     rlBegin(RL_LINES);
         rlColor4ub(color.r, color.g, color.b, color.a);
+        rlVertex2f((float)posX + xOffset, (float)posY + yOffset);
+        rlVertex2f((float)posX + (float)width - xOffset, (float)posY + yOffset);
+
+        rlVertex2f((float)posX + (float)width - xOffset, (float)posY + yOffset);
+        rlVertex2f((float)posX + (float)width - xOffset, (float)posY + (float)height - yOffset);
+
+        rlVertex2f((float)posX + (float)width - xOffset, (float)posY + (float)height - yOffset);
+        rlVertex2f((float)posX + xOffset, (float)posY + (float)height - yOffset);
+
+        rlVertex2f((float)posX + xOffset, (float)posY + (float)height - yOffset);
+        rlVertex2f((float)posX + xOffset, (float)posY + yOffset);
+    rlEnd();
+
+/*
+// Previous implementation, it has issues... but it does not require view matrix...
+#if defined(SUPPORT_QUADS_DRAW_MODE)
+    DrawRectangle(posX, posY, width, 1, color);
+    DrawRectangle(posX + width - 1, posY + 1, 1, height - 2, color);
+    DrawRectangle(posX, posY + height - 1, width, 1, color);
+    DrawRectangle(posX, posY + 1, 1, height - 2, color);
+#else
+    rlBegin(RL_LINES);
+        rlColor4ub(color.r, color.g, color.b, color.a);
         rlVertex2f((float)posX, (float)posY);
         rlVertex2f((float)posX + (float)width, (float)posY + 1);
 
@@ -821,6 +904,8 @@
         rlVertex2f((float)posX + 1, (float)posY + (float)height);
         rlVertex2f((float)posX + 1, (float)posY + 1);
     rlEnd();
+#endif
+*/
 }
 
 // Draw rectangle outline with extended parameters
@@ -858,7 +943,7 @@
 void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color color)
 {
     // Not a rounded rectangle
-    if ((roundness <= 0.0f) || (rec.width < 1) || (rec.height < 1 ))
+    if (roundness <= 0.0f)
     {
         DrawRectangleRec(rec, color);
         return;
@@ -1081,9 +1166,9 @@
 }
 
 // Draw rectangle with rounded edges
-// TODO: This function should be refactored to use RL_LINES, for consistency with other Draw*Lines()
 void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color)
 {
+    // NOTE: For line thicknes <=1.0f we use RL_LINES, otherwise wee use RL_QUADS/RL_TRIANGLES
     DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color);
 }
 
@@ -1134,18 +1219,29 @@
            P5 ================== P4
     */
     const Vector2 point[16] = {
-        {(float)rec.x + innerRadius, rec.y - lineThick}, {(float)(rec.x + rec.width) - innerRadius, rec.y - lineThick}, { rec.x + rec.width + lineThick, (float)rec.y + innerRadius }, // PO, P1, P2
-        {rec.x + rec.width + lineThick, (float)(rec.y + rec.height) - innerRadius}, {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height + lineThick}, // P3, P4
-        {(float)rec.x + innerRadius, rec.y + rec.height + lineThick}, { rec.x - lineThick, (float)(rec.y + rec.height) - innerRadius}, {rec.x - lineThick, (float)rec.y + innerRadius}, // P5, P6, P7
-        {(float)rec.x + innerRadius, rec.y}, {(float)(rec.x + rec.width) - innerRadius, rec.y}, // P8, P9
-        { rec.x + rec.width, (float)rec.y + innerRadius }, {rec.x + rec.width, (float)(rec.y + rec.height) - innerRadius}, // P10, P11
-        {(float)(rec.x + rec.width) - innerRadius, rec.y + rec.height}, {(float)rec.x + innerRadius, rec.y + rec.height}, // P12, P13
-        { rec.x, (float)(rec.y + rec.height) - innerRadius}, {rec.x, (float)rec.y + innerRadius} // P14, P15
+        {(float)rec.x + innerRadius + 0.5f, rec.y - lineThick + 0.5f},
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y - lineThick + 0.5f},
+        {rec.x + rec.width + lineThick - 0.5f, (float)rec.y + innerRadius + 0.5f}, // PO, P1, P2
+        {rec.x + rec.width + lineThick - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f},
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + rec.height + lineThick - 0.5f}, // P3, P4
+        {(float)rec.x + innerRadius + 0.5f, rec.y + rec.height + lineThick - 0.5f},
+        {rec.x - lineThick + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f},
+        {rec.x - lineThick + 0.5f, (float)rec.y + innerRadius + 0.5f}, // P5, P6, P7
+        {(float)rec.x + innerRadius + 0.5f, rec.y + 0.5f},
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + 0.5f}, // P8, P9
+        {rec.x + rec.width - 0.5f, (float)rec.y + innerRadius + 0.5f},
+        {rec.x + rec.width - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f}, // P10, P11
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, rec.y + rec.height - 0.5f},
+        {(float)rec.x + innerRadius + 0.5f, rec.y + rec.height - 0.5f}, // P12, P13
+        {rec.x + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f},
+        {rec.x + 0.5f, (float)rec.y + innerRadius + 0.5f} // P14, P15
     };
 
     const Vector2 centers[4] = {
-        {(float)rec.x + innerRadius, (float)rec.y + innerRadius}, {(float)(rec.x + rec.width) - innerRadius, (float)rec.y + innerRadius}, // P16, P17
-        {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19
+        {(float)rec.x + innerRadius + 0.5f, (float)rec.y + innerRadius + 0.5f},
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, (float)rec.y + innerRadius + 0.5f}, // P16, P17
+        {(float)(rec.x + rec.width) - innerRadius - 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f},
+        {(float)rec.x + innerRadius + 0.5f, (float)(rec.y + rec.height) - innerRadius - 0.5f} // P18, P19
     };
 
     const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
@@ -1296,7 +1392,6 @@
     {
         // Use LINES to draw the outline
         rlBegin(RL_LINES);
-
             // Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
             for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
             {
@@ -1319,7 +1414,6 @@
                 rlVertex2f(point[i].x, point[i].y);
                 rlVertex2f(point[i + 1].x, point[i + 1].y);
             }
-
         rlEnd();
     }
 }
@@ -1333,6 +1427,7 @@
     Rectangle shapeRect = GetShapesTextureRectangle();
 
     rlBegin(RL_QUADS);
+        rlNormal3f(0.0f, 0.0f, 1.0f);
         rlColor4ub(color.r, color.g, color.b, color.a);
 
         rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height);
@@ -1342,7 +1437,7 @@
         rlVertex2f(v2.x, v2.y);
 
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height);
-        rlVertex2f(v2.x, v2.y);
+        rlVertex2f(v3.x, v3.y);
 
         rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height);
         rlVertex2f(v3.x, v3.y);
@@ -1650,7 +1745,7 @@
         prevNormal = normal;
     }
 
-#else   // !SUPPORT_SPLINE_MITTERS
+#else   // !SUPPORT_SPLINE_MITERS
 
     Vector2 delta = { 0 };
     float length = 0.0f;
@@ -2197,7 +2292,7 @@
 // NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php
 bool CheckCollisionPointPoly(Vector2 point, const Vector2 *points, int pointCount)
 {
-    bool inside = false;
+    bool collision = false;
 
     if (pointCount > 2)
     {
@@ -2206,12 +2301,12 @@
             if ((points[i].y > point.y) != (points[j].y > point.y) &&
                 (point.x < (points[j].x - points[i].x)*(point.y - points[i].y)/(points[j].y - points[i].y) + points[i].x))
             {
-                inside = !inside;
+                collision = !collision;
             }
         }
     }
 
-    return inside;
+    return collision;
 }
 
 // Check collision between two rectangles
@@ -2316,8 +2411,8 @@
     return collision;
 }
 
-// Check if circle collides with a line created betweeen two points [p1] and [p2]
-RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2)
+// Check if circle collides with a line created between two points [p1] and [p2]
+bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2)
 {
     float dx = p1.x - p2.x;
     float dy = p1.y - p2.y;
@@ -2366,7 +2461,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 
 // Cubic easing in-out
diff --git a/raylib/src/rtext.c b/raylib/src/rtext.c
--- a/raylib/src/rtext.c
+++ b/raylib/src/rtext.c
@@ -7,8 +7,8 @@
 *           rtext module is included in the build
 *
 *       #define SUPPORT_DEFAULT_FONT
-*           Load default raylib font on initialization to be used by DrawText() and MeasureText().
-*           If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
+*           Load default raylib font on initialization to be used by DrawText() and MeasureText()
+*           If no default font loaded, DrawTextEx() and MeasureTextEx() are required
 *
 *       #define SUPPORT_FILEFORMAT_FNT
 *       #define SUPPORT_FILEFORMAT_TTF
@@ -19,7 +19,7 @@
 *       #define SUPPORT_FONT_ATLAS_WHITE_REC
 *           On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
 *           at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
-*           drawing text and shapes with a single draw call [SetShapesTexture()].
+*           drawing text and shapes with a single draw call [SetShapesTexture()]
 *
 *       #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
 *           TextSplit() function static buffer max size
@@ -34,7 +34,7 @@
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -55,19 +55,15 @@
 
 #include "raylib.h"         // Declares module functions
 
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"     // Defines module configuration flags
-#endif
+#include "config.h"         // Defines module configuration flags
 
 #if defined(SUPPORT_MODULE_RTEXT)
 
-#include "utils.h"          // Required for: LoadFile*()
 #include "rlgl.h"           // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro()
 
 #include <stdlib.h>         // Required for: malloc(), free()
 #include <stdio.h>          // Required for: vsprintf()
-#include <string.h>         // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
+#include <string.h>         // Required for: strcmp(), strstr(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
 #include <stdarg.h>         // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
 #include <ctype.h>          // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
 
@@ -93,6 +89,9 @@
         #pragma GCC diagnostic ignored "-Wunused-function"
     #endif
 
+    #define STBTT_malloc(x,u) ((void)(u),RL_MALLOC(x))
+    #define STBTT_free(x,u) ((void)(u),RL_FREE(x))
+
     #define STBTT_STATIC
     #define STB_TRUETYPE_IMPLEMENTATION
     #include "external/stb_truetype.h"      // Required for: ttf font data reading
@@ -124,12 +123,12 @@
 //----------------------------------------------------------------------------------
 // Global variables
 //----------------------------------------------------------------------------------
-extern bool isGpuReady;
 #if defined(SUPPORT_DEFAULT_FONT)
 // Default font provided by raylib
 // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
 static Font defaultFont = { 0 };
 #endif
+static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines)
 
 //----------------------------------------------------------------------------------
 // Other Modules Functions Declaration (required by text)
@@ -137,15 +136,14 @@
 //...
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 #if defined(SUPPORT_FILEFORMAT_FNT)
 static Font LoadBMFont(const char *fileName);   // Load a BMFont file (AngelCode font file)
 #endif
 #if defined(SUPPORT_FILEFORMAT_BDF)
-static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize);
+static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize);
 #endif
-static int textLineSpacing = 2;                 // Text vertical line spacing in pixels (between lines)
 
 #if defined(SUPPORT_DEFAULT_FONT)
 extern void LoadFontDefault(void);
@@ -161,10 +159,13 @@
 {
     #define BIT_CHECK(a,b) ((a) & (1u << (b)))
 
+    // Check to see if we have already allocated the font for an image, and if we don't need to upload, then just return
+    if (defaultFont.glyphs != NULL) return;
+
     // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
-    // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
+    // REF: http://www.utf8-chartable.de/unicode-utf8-table.pl
 
-    defaultFont.glyphCount = 224;   // Number of chars included in our default font
+    defaultFont.glyphCount = 224;   // Number of glyphs included in our default font
     defaultFont.glyphPadding = 0;   // Characters padding
 
     // Default font is directly defined here (data generated from a sprite font image)
@@ -247,14 +248,26 @@
                 // we must consider data as little-endian order (alpha + gray)
                 ((unsigned short *)imFont.data)[i + j] = 0xffff;
             }
-            else ((unsigned short *)imFont.data)[i + j] = 0x00ff;
+            else
+            {
+                ((unsigned char *)imFont.data)[(i + j)*sizeof(short)] = 0xff;
+                ((unsigned char *)imFont.data)[(i + j)*sizeof(short) + 1] = 0x00;
+            }
         }
 
         counter++;
     }
 
-    if (isGpuReady) defaultFont.texture = LoadTextureFromImage(imFont);
+    defaultFont.texture = LoadTextureFromImage(imFont);
 
+    // we have already loaded the font glyph data an image, and the GPU is ready, we are done
+    // if we don't do this, we will leak memory by reallocating the glyphs and rects
+    if (defaultFont.glyphs != NULL)
+    {
+        UnloadImage(imFont);
+        return;
+    }
+
     // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount
     //------------------------------------------------------------------------------
 
@@ -278,7 +291,7 @@
 
         testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor);
 
-        if (testPosX >= defaultFont.texture.width)
+        if (testPosX >= imFont.width)
         {
             currentLine++;
             currentPosX = 2*charsDivisor + charsWidth[i];
@@ -309,9 +322,12 @@
 extern void UnloadFontDefault(void)
 {
     for (int i = 0; i < defaultFont.glyphCount; i++) UnloadImage(defaultFont.glyphs[i].image);
-    if (isGpuReady) UnloadTexture(defaultFont.texture);
+    UnloadTexture(defaultFont.texture);
     RL_FREE(defaultFont.glyphs);
     RL_FREE(defaultFont.recs);
+    defaultFont.glyphCount = 0;
+    defaultFont.glyphs = NULL;
+    defaultFont.recs = NULL;
 }
 #endif      // SUPPORT_DEFAULT_FONT
 
@@ -340,7 +356,7 @@
     #define FONT_TTF_DEFAULT_FIRST_CHAR     32      // TTF font generation default first char for image sprite font (32-Space)
 #endif
 #ifndef FONT_TTF_DEFAULT_CHARS_PADDING
-    #define FONT_TTF_DEFAULT_CHARS_PADDING   4      // TTF font generation default chars padding
+    #define FONT_TTF_DEFAULT_CHARS_PADDING   4      // TTF font generation default glyphs padding
 #endif
 
     Font font = { 0 };
@@ -360,17 +376,15 @@
     {
         Image image = LoadImage(fileName);
         if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
+        else font = GetFontDefault();
         UnloadImage(image);
     }
 
-    if (isGpuReady)
+    if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
+    else
     {
-        if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
-        else
-        {
-            SetTextureFilter(font.texture, TEXTURE_FILTER_POINT);    // By default, we set point filter (the best performance)
-            TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
-        }
+        SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance)
+        TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
     }
 
     return font;
@@ -379,7 +393,7 @@
 // Load Font from TTF or BDF font file with generation parameters
 // NOTE: You can pass an array with desired characters, those characters should be available in the font
 // if array is NULL, default char set is selected 32..126
-Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount)
+Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount)
 {
     Font font = { 0 };
 
@@ -415,8 +429,8 @@
     int x = 0;
     int y = 0;
 
-    // We allocate a temporal arrays for chars data measures,
-    // once we get the actual number of chars, we copy data to a sized arrays
+    // We allocate a temporal arrays for glyphs data measures,
+    // once we get the actual number of glyphs, we copy data to a sized arrays
     int tempCharValues[MAX_GLYPHS_FROM_IMAGE] = { 0 };
     Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE] = { 0 };
 
@@ -491,11 +505,11 @@
     };
 
     // Set font with all data parsed from image
-    if (isGpuReady) font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
+    font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
     font.glyphCount = index;
     font.glyphPadding = 0;
 
-    // We got tempCharValues and tempCharsRecs populated with chars data
+    // We got tempCharValues and tempCharsRecs populated with glyphs data
     // Now we move temp data to sized charValues and charRecs arrays
     font.glyphs = (GlyphInfo *)RL_MALLOC(font.glyphCount*sizeof(GlyphInfo));
     font.recs = (Rectangle *)RL_MALLOC(font.glyphCount*sizeof(Rectangle));
@@ -524,7 +538,7 @@
 }
 
 // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
-Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount)
+Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount)
 {
     Font font = { 0 };
 
@@ -532,21 +546,21 @@
     strncpy(fileExtLower, TextToLower(fileType), 16 - 1);
 
     font.baseSize = fontSize;
-    font.glyphCount = (codepointCount > 0)? codepointCount : 95;
     font.glyphPadding = 0;
 
 #if defined(SUPPORT_FILEFORMAT_TTF)
     if (TextIsEqual(fileExtLower, ".ttf") ||
         TextIsEqual(fileExtLower, ".otf"))
     {
-        font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT);
+        font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, (codepointCount > 0)? codepointCount : 95, FONT_DEFAULT, &font.glyphCount);
     }
     else
 #endif
 #if defined(SUPPORT_FILEFORMAT_BDF)
     if (TextIsEqual(fileExtLower, ".bdf"))
     {
-        font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize);
+        font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, (codepointCount > 0)? codepointCount : 95, &font.baseSize);
+        font.glyphCount = (codepointCount > 0)? codepointCount : 95;
     }
     else
 #endif
@@ -560,7 +574,7 @@
         font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
 
         Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0);
-        if (isGpuReady) font.texture = LoadTextureFromImage(atlas);
+        font.texture = LoadTextureFromImage(atlas);
 
         // Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
         for (int i = 0; i < font.glyphCount; i++)
@@ -595,7 +609,7 @@
 
 // Load font data for further use
 // NOTE: Requires TTF font memory data and can generate SDF data
-GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type)
+GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount, int type, int *glyphCount)
 {
     // NOTE: Using some SDF generation default values,
     // trades off precision with ability to handle *smaller* sizes
@@ -612,7 +626,8 @@
     #define FONT_BITMAP_ALPHA_THRESHOLD     80      // Bitmap (B&W) font generation alpha threshold
 #endif
 
-    GlyphInfo *chars = NULL;
+    GlyphInfo *glyphs = NULL;
+    int glyphCounter = 0;
 
 #if defined(SUPPORT_FILEFORMAT_TTF)
     // Load font data (including pixel data) from TTF memory file
@@ -621,6 +636,7 @@
     {
         bool genFontChars = false;
         stbtt_fontinfo fontInfo = { 0 };
+        int *requiredCodepoints = (int *)codepoints; // TODO: Should we create a shallow copy to avoid "dealing" with a const user array?
 
         if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0))     // Initialize font for data reading
         {
@@ -629,7 +645,9 @@
 
             // Calculate font basic metrics
             // NOTE: ascent is equivalent to font baseline
-            int ascent, descent, lineGap;
+            int ascent = 0;
+            int descent = 0;
+            int lineGap = 0;
             stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
 
             // In case no chars count provided, default to 95
@@ -637,21 +655,29 @@
 
             // Fill fontChars in case not provided externally
             // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
-            if (codepoints == NULL)
+            if (requiredCodepoints == NULL)
             {
-                codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
-                for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
+                requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
+                for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32;
                 genFontChars = true;
             }
 
-            chars = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
+            // Check available glyphs on provided font before loading them
+            for (int i = 0, index; i < codepointCount; i++)
+            {
+                index = stbtt_FindGlyphIndex(&fontInfo, requiredCodepoints[i]);
+                if (index > 0) glyphCounter++;
+            }
 
-            // NOTE: Using simple packaging, one char after another
+            // WARNING: Allocating space for maximum number of codepoints
+            glyphs = (GlyphInfo *)RL_CALLOC(glyphCounter, sizeof(GlyphInfo));
+            glyphCounter = 0; // Reset to reuse
+
+            int k = 0;
             for (int i = 0; i < codepointCount; i++)
             {
-                int chw = 0, chh = 0;   // Character width and height (on generation)
-                int ch = codepoints[i];  // Character value to get info for
-                chars[i].value = ch;
+                int cpWidth = 0, cpHeight = 0;   // Codepoint width and height (on generation)
+                int cp = requiredCodepoints[i];  // Codepoint value to get info for
 
                 //  Render a unicode codepoint to a bitmap
                 //      stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap
@@ -660,76 +686,105 @@
 
                 // Check if a glyph is available in the font
                 // WARNING: if (index == 0), glyph not found, it could fallback to default .notdef glyph (if defined in font)
-                int index = stbtt_FindGlyphIndex(&fontInfo, ch);
+                int index = stbtt_FindGlyphIndex(&fontInfo, cp);
 
                 if (index > 0)
                 {
+                    // NOTE: Only storing glyphs for codepoints found in the font
+                    glyphs[k].value = cp;
+
                     switch (type)
                     {
                         case FONT_DEFAULT:
-                        case FONT_BITMAP: chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break;
-                        case FONT_SDF: if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break;
+                        case FONT_BITMAP:
+                        {
+                            glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp,
+                                &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY);
+                        } break;
+                        case FONT_SDF:
+                        {
+                            if (cp != 32)
+                            {
+                                glyphs[k].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, cp,
+                                    FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE,
+                                    &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY);
+                            }
+                        } break;
+                        //case FONT_MSDF:
                         default: break;
                     }
 
-                    if (chars[i].image.data != NULL)    // Glyph data has been found in the font
+                    if (glyphs[k].image.data != NULL)    // Glyph data has been found in the font
                     {
-                        stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
-                        chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
+                        stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL);
+                        glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor);
 
-                        if (chh > fontSize) TRACELOG(LOG_WARNING, "FONT: Character [0x%08x] size is bigger than expected font size", ch);
+                        // WARNING: If requested SDF font, sdf-glyph height is definitely bigger than fontSize due to FONT_SDF_CHAR_PADDING
+                        if ((type != FONT_SDF) && (cpHeight > fontSize)) TRACELOG(LOG_WARNING, "FONT: [0x%04x] Glyph height is bigger than requested font size: %i > %i", cp, cpHeight, (int)fontSize);
 
-                        // Load characters images
-                        chars[i].image.width = chw;
-                        chars[i].image.height = chh;
-                        chars[i].image.mipmaps = 1;
-                        chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+                        // Load glyph image
+                        glyphs[k].image.width = cpWidth;
+                        glyphs[k].image.height = cpHeight;
+                        glyphs[k].image.mipmaps = 1;
+                        glyphs[k].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
 
-                        chars[i].offsetY += (int)((float)ascent*scaleFactor);
+                        glyphs[k].offsetY += (int)((float)ascent*scaleFactor);
                     }
+                    //else TRACELOG(LOG_WARNING, "FONT: Glyph [0x%08x] has no image data available", cp); // Only reported for 0x20 and 0x3000
 
-                    // NOTE: We create an empty image for space character,
-                    // it could be further required for atlas packing
-                    if (ch == 32)
+                    // We create an empty image for Space character (0x20), useful for sprite font generation
+                    // NOTE: Another space to consider: 0x3000 (CJK - Ideographic Space)
+                    if ((cp == 0x20) || (cp == 0x3000))
                     {
-                        stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
-                        chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
-
+                        stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL);
+                        glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor);
+                        
                         Image imSpace = {
-                            .data = RL_CALLOC(chars[i].advanceX*fontSize, 2),
-                            .width = chars[i].advanceX,
+                            .data = NULL,
+                            .width = glyphs[k].advanceX,
                             .height = fontSize,
                             .mipmaps = 1,
                             .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE
                         };
 
-                        chars[i].image = imSpace;
+                        // Only allocate space image if required
+                        if (glyphs[k].advanceX > 0) imSpace.data = RL_CALLOC(glyphs[k].advanceX*fontSize, 1);
+                        else glyphs[k].advanceX = 0;
+
+                        glyphs[k].image = imSpace;
                     }
 
                     if (type == FONT_BITMAP)
                     {
                         // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
                         // NOTE: For optimum results, bitmap font should be generated at base pixel size
-                        for (int p = 0; p < chw*chh; p++)
+                        for (int p = 0; p < cpWidth*cpHeight; p++)
                         {
-                            if (((unsigned char *)chars[i].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0;
-                            else ((unsigned char *)chars[i].image.data)[p] = 255;
+                            if (((unsigned char *)glyphs[k].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD)
+                                ((unsigned char *)glyphs[k].image.data)[p] = 0;
+                            else ((unsigned char *)glyphs[k].image.data)[p] = 255;
                         }
                     }
+
+                    k++;
+                    glyphCounter++;
                 }
                 else
                 {
-                    // TODO: Use some fallback glyph for codepoints not found in the font
+                    // WARNING: Glyph not found on font, optionally use a fallback glyph
                 }
             }
+
+            if (glyphCounter < codepointCount) TRACELOG(LOG_WARNING, "FONT: Requested codepoints glyphs found: [%i/%i]", k, codepointCount);
         }
         else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
 
-        if (genFontChars) RL_FREE(codepoints);
+        if (genFontChars) RL_FREE(requiredCodepoints);
     }
 #endif
 
-    return chars;
+    *glyphCount = glyphCounter;
+    return glyphs;
 }
 
 // Generate image font atlas using chars info
@@ -779,7 +834,9 @@
     atlas.height = imageSize;  // Atlas bitmap height
 #else
     int paddedFontSize = fontSize + 2*padding;
+
     // No need for a so-conservative atlas generation
+    // NOTE: Multiplying total expected are by 1.2f scale factor
     float totalArea = totalWidth*paddedFontSize*1.2f;
     float imageMinSize = sqrtf(totalArea);
     int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2)));
@@ -796,7 +853,8 @@
     }
 #endif
 
-    atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height);   // Create a bitmap to store characters (8 bpp)
+    int atlasDataSize = atlas.width*atlas.height; // Save total size for bounds checking
+    atlas.data = (unsigned char *)RL_CALLOC(atlasDataSize, 1); // Create a bitmap to store characters (8 bpp)
     atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
     atlas.mipmaps = 1;
 
@@ -825,14 +883,14 @@
                 {
                     for (int j = i + 1; j < glyphCount; j++)
                     {
-                        TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", j);
+                        TRACELOG(LOG_WARNING, "FONT: Failed to package character (0x%02x)", glyphs[j].value);
                         // Make sure remaining recs contain valid data
                         recs[j].x = 0;
                         recs[j].y = 0;
                         recs[j].width = 0;
                         recs[j].height = 0;
                     }
-                    break;
+                    break; // Break for() loop, stop processing glyphs
                 }
             }
 
@@ -841,7 +899,15 @@
             {
                 for (int x = 0; x < glyphs[i].image.width; x++)
                 {
-                    ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
+                    int destX = offsetX + x;
+                    int destY = offsetY + y;
+
+                    // Security: check both lower and upper bounds
+                    if ((destX >= 0) && (destX < atlas.width) && (destY >= 0) && (destY < atlas.height))
+                    {
+                        ((unsigned char *)atlas.data)[destY*atlas.width + destX] =
+                            ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
+                    }
                 }
             }
 
@@ -889,11 +955,19 @@
                 {
                     for (int x = 0; x < glyphs[i].image.width; x++)
                     {
-                        ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
+                        int destX = rects[i].x + padding + x;
+                        int destY = rects[i].y + padding + y;
+
+                        // Security fix: check both lower and upper bounds
+                        if (destX >= 0 && destX < atlas.width && destY >= 0 && destY < atlas.height)
+                        {
+                            ((unsigned char *)atlas.data)[destY * atlas.width + destX] =
+                                ((unsigned char *)glyphs[i].image.data)[y * glyphs[i].image.width + x];
+                        }
                     }
                 }
             }
-            else TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", i);
+            else TRACELOG(LOG_WARNING, "FONT: Failed to package character (0x%02x)", glyphs[i].value);
         }
 
         RL_FREE(rects);
@@ -903,14 +977,17 @@
 
 #if defined(SUPPORT_FONT_ATLAS_WHITE_REC)
     // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas,
-    // useful to use as the white texture to draw shapes with raylib, using this rectangle
-    // shapes and text can be backed into a single draw call: SetShapesTexture()
-    for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++)
+    // useful to use as the white texture to draw shapes with raylib
+    // Security: ensure the atlas is large enough to hold a 3x3 rectangle
+    if ((atlas.width >= 3) && (atlas.height >= 3))
     {
-        ((unsigned char *)atlas.data)[k - 0] = 255;
-        ((unsigned char *)atlas.data)[k - 1] = 255;
-        ((unsigned char *)atlas.data)[k - 2] = 255;
-        k -= atlas.width;
+        for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++)
+        {
+            ((unsigned char *)atlas.data)[k - 0] = 255;
+            ((unsigned char *)atlas.data)[k - 1] = 255;
+            ((unsigned char *)atlas.data)[k - 2] = 255;
+            k -= atlas.width;
+        }
     }
 #endif
 
@@ -951,10 +1028,10 @@
     if (font.texture.id != GetFontDefault().texture.id)
     {
         UnloadFontData(font.glyphs, font.glyphCount);
-        if (isGpuReady) UnloadTexture(font.texture);
+        UnloadTexture(font.texture);
         RL_FREE(font.recs);
 
-        TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
+        TRACELOG(LOG_DEBUG, "FONT: Unloaded font data from RAM and VRAM");
     }
 }
 
@@ -967,15 +1044,26 @@
     #define TEXT_BYTES_PER_LINE     20
 #endif
 
-    #define MAX_FONT_DATA_SIZE      1024*1024       // 1 MB
-
     // Get file name from path
     char fileNamePascal[256] = { 0 };
     strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1);
+    
+    // Get font atlas image and size, required to estimate code file size
+    // NOTE: This mechanism is highly coupled to raylib
+    Image image = LoadImageFromTexture(font.texture);
+    if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!");
+    int imageDataSize = GetPixelDataSize(image.width, image.height, image.format);
 
-    // NOTE: Text data buffer size is estimated considering image data size in bytes
-    // and requiring 6 char bytes for every byte: "0x00, "
-    char *txtData = (char *)RL_CALLOC(MAX_FONT_DATA_SIZE, sizeof(char));
+    // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
+    //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
+    
+    // Estimate text code size
+    //  - Image data is stored as "0x%02x", so it requires at least 4 char per byte, let's use 6
+    //  - font.recs[] data is stored as "{ %1.0f, %1.0f, %1.0f , %1.0f }", let's reserve 64 per rec
+    //  - font.glyphs[] data is stored as "{ %i, %i, %i, %i, { 0 }},\n", let's reserve 64 per glyph
+    //  - Comments and additional code, let's reserve 32KB
+    int txtDataSize = imageDataSize*6 + font.glyphCount*64 + font.glyphCount*64 + 32768; 
+    char *txtData = (char *)RL_CALLOC(txtDataSize, sizeof(char));
 
     int byteCount = 0;
     byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
@@ -985,7 +1073,7 @@
     byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
     byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)                                //\n");
+    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5)                                //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
     byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
@@ -997,15 +1085,6 @@
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
     byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
 
-    // Support font export and initialization
-    // NOTE: This mechanism is highly coupled to raylib
-    Image image = LoadImageFromTexture(font.texture);
-    if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!");
-    int imageDataSize = GetPixelDataSize(image.width, image.height, image.format);
-
-    // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
-    //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
-
 #define SUPPORT_COMPRESSED_FONT_ATLAS
 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
     // WARNING: Data is compressed using raylib CompressData() DEFLATE,
@@ -1043,8 +1122,7 @@
     byteCount += sprintf(txtData + byteCount, "};\n\n");
 
     // Save font glyphs data
-    // NOTE: Glyphs image data not saved (grayscale pixels),
-    // it could be generated from image and recs
+    // NOTE: Glyphs image data not saved (grayscale pixels), it could be generated from image and recs
     byteCount += sprintf(txtData + byteCount, "// Font glyphs info data\n");
     byteCount += sprintf(txtData + byteCount, "// NOTE: No glyphs.image data provided\n");
     byteCount += sprintf(txtData + byteCount, "static GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
@@ -1071,12 +1149,12 @@
     byteCount += sprintf(txtData + byteCount, "    Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format);
 #endif
     byteCount += sprintf(txtData + byteCount, "    // Load texture from image\n");
-    byteCount += sprintf(txtData + byteCount, "    if (isGpuReady) font.texture = LoadTextureFromImage(imFont);\n");
+    byteCount += sprintf(txtData + byteCount, "    font.texture = LoadTextureFromImage(imFont);\n");
 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
     byteCount += sprintf(txtData + byteCount, "    UnloadImage(imFont);  // Uncompressed data can be unloaded from memory\n\n");
 #endif
-    // We have two possible mechanisms to assign font.recs and font.glyphs data,
-    // that data is already available as global arrays, we two options to assign that data:
+    // There are two possible mechanisms to assign font.recs and font.glyphs data,
+    // that data is already available as global arrays, two options to assign that data:
     //  - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code
     //  - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment
 //#define SUPPORT_FONT_DATA_COPY
@@ -1214,7 +1292,7 @@
                       (font.recs[index].height + 2.0f*font.glyphPadding)*scaleFactor };
 
     // Character source rectangle from font texture atlas
-    // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
+    // NOTE: We consider glyphs padding when drawing, it could be required for outline/glow shader effects
     Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding,
                          font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding };
 
@@ -1267,7 +1345,7 @@
     // Check if default font has been loaded
     if (GetFontDefault().texture.id != 0)
     {
-        int defaultFontSize = 10;   // Default Font chars height in pixel
+        int defaultFontSize = 10;   // Default Font glyphs height in pixel
         if (fontSize < defaultFontSize) fontSize = defaultFontSize;
         int spacing = fontSize/defaultFontSize;
 
@@ -1282,7 +1360,7 @@
 {
     Vector2 textSize = { 0 };
 
-    if ((isGpuReady && (font.texture.id == 0)) || (text == NULL)) return textSize; // Security check
+    if ((font.texture.id == 0) || (text == NULL) || (text[0] == '\0')) return textSize; // Security check
 
     int size = TextLength(text);    // Get size in bytes of text
     int tempByteCounter = 0;        // Used to count longer text line num chars
@@ -1338,6 +1416,7 @@
 int GetGlyphIndex(Font font, int codepoint)
 {
     int index = 0;
+    if (!IsFontValid(font)) return index;
 
 #define SUPPORT_UNORDERED_CHARSET
 #if defined(SUPPORT_UNORDERED_CHARSET)
@@ -1388,6 +1467,49 @@
 //----------------------------------------------------------------------------------
 // Text strings management functions
 //----------------------------------------------------------------------------------
+// Load text as separate lines ('\n')
+// NOTE: Returned lines end with null terminator '\0'
+char **LoadTextLines(const char *text, int *count)
+{
+    char **lines = NULL;
+    int lineCount = 0;
+
+    if (text != NULL)
+    {
+        int textLength = TextLength(text);
+        lineCount = 1;
+
+        // First text scan pass to get required line count
+        for (int i = 0; i < textLength; i++)
+        {
+            if (text[i] == '\n') lineCount++;
+        }
+
+        lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
+        for (int i = 0, l = 0, lineLen = 0; i <= textLength; i++)
+        {
+            if ((text[i] == '\n') || (text[i] == '\0'))
+            {
+                lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
+                strncpy(lines[l], &text[i - lineLen], lineLen);
+                lineLen = 0;
+                l++;
+            }
+            else lineLen++;
+        }
+    }
+
+    *count = lineCount;
+    return lines;
+}
+
+// Unload text lines
+void UnloadTextLines(char **lines, int lineCount)
+{
+    for (int i = 0; i < lineCount; i++) RL_FREE(lines[i]);
+    RL_FREE(lines);
+}
+
 // Get text length in bytes, check for \0 character
 unsigned int TextLength(const char *text)
 {
@@ -1416,24 +1538,27 @@
     static int index = 0;
 
     char *currentBuffer = buffers[index];
-    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
-
-    va_list args;
-    va_start(args, text);
-    int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
-    va_end(args);
+    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
 
-    // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
-    if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+    if (text != NULL)
     {
-        // Inserting "..." at the end of the string to mark as truncated
-        char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
-        sprintf(truncBuffer, "...");
-    }
+        va_list args;
+        va_start(args, text);
+        int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+        va_end(args);
 
-    index += 1;     // Move to next buffer for next function call
-    if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+        // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+        if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+        {
+            // Inserting "..." at the end of the string to mark as truncated
+            char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+            snprintf(truncBuffer, 4, "...");
+        }
 
+        index += 1;     // Move to next buffer for next function call
+        if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
+    }
+
     return currentBuffer;
 }
 
@@ -1444,13 +1569,16 @@
     int value = 0;
     int sign = 1;
 
-    if ((text[0] == '+') || (text[0] == '-'))
+    if (text != NULL)
     {
-        if (text[0] == '-') sign = -1;
-        text++;
-    }
+        if ((text[0] == '+') || (text[0] == '-'))
+        {
+            if (text[0] == '-') sign = -1;
+            text++;
+        }
 
-    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+        for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+    }
 
     return value*sign;
 }
@@ -1463,22 +1591,25 @@
     float value = 0.0f;
     float sign = 1.0f;
 
-    if ((text[0] == '+') || (text[0] == '-'))
+    if (text != NULL)
     {
-        if (text[0] == '-') sign = -1.0f;
-        text++;
-    }
+        if ((text[0] == '+') || (text[0] == '-'))
+        {
+            if (text[0] == '-') sign = -1.0f;
+            text++;
+        }
 
-    int i = 0;
-    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+        int i = 0;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
 
-    if (text[i++] == '.')
-    {
-        float divisor = 10.0f;
-        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        if (text[i++] == '.')
         {
-            value += ((float)(text[i] - '0'))/divisor;
-            divisor = divisor*10.0f;
+            float divisor = 10.0f;
+            for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+            {
+                value += ((float)(text[i] - '0'))/divisor;
+                divisor = divisor*10.0f;
+            }
         }
     }
 
@@ -1487,14 +1618,13 @@
 
 #if defined(SUPPORT_TEXT_MANIPULATION)
 // Copy one string to another, returns bytes copied
+// NOTE: Alternative implementation to strcpy(dst, src) from C standard library
 int TextCopy(char *dst, const char *src)
 {
     int bytes = 0;
 
     if ((src != NULL) && (dst != NULL))
     {
-        // NOTE: Alternative: use strcpy(dst, src)
-
         while (*src != '\0')
         {
             *dst = *src;
@@ -1530,76 +1660,168 @@
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
-    int textLength = TextLength(text);
-
-    if (position >= textLength)
+    if (text != NULL)
     {
-        position = textLength - 1;
-        length = 0;
+        int textLength = TextLength(text);
+
+        if (position >= textLength) return buffer; // First char is already '\0' by memset
+
+        int maxLength = textLength - position;
+        if (length > maxLength) length = maxLength;
+        if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
+
+        // NOTE: Alternative: memcpy(buffer, text + position, length)
+
+        for (int c = 0; c < length; c++) buffer[c] = text[position + c];
+
+        buffer[length] = '\0';
     }
 
-    if (length >= textLength) length = textLength;
+    return buffer;
+}
 
-    // NOTE: Alternative: memcpy(buffer, text + position, length)
+// Remove text spaces, concat words
+const char *TextRemoveSpaces(const char *text)
+{
+    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
+    memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
-    for (int c = 0 ; c < length ; c++)
+    if (text != NULL)
     {
-        *(buffer + c) = *(text + position);
-        text++;
+        // Avoid copying the ' ' characters
+        for (int i = 0, j = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++)
+        {
+            if (text[i] != ' ') { buffer[j] = text[i]; j++; }
+        }
     }
 
-    *(buffer + length) = '\0';
+    return buffer;
+}
 
+// Get text between two strings
+char *GetTextBetween(const char *text, const char *begin, const char *end)
+{
+    #define MAX_TEXT_BETWEEN_SIZE   1024
+
+    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
+    memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
+
+    int beginIndex = TextFindIndex(text, begin);
+
+    if (beginIndex > -1)
+    {
+        int beginLen = TextLength(begin);
+        int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
+
+        if (endIndex > -1)
+        {
+            endIndex += (beginIndex + beginLen);
+            int len = (endIndex - beginIndex - beginLen);
+            if (len < (MAX_TEXT_BETWEEN_SIZE - 1)) strncpy(buffer, text + beginIndex + beginLen, len);
+            else strncpy(buffer, text + beginIndex + beginLen, MAX_TEXT_BETWEEN_SIZE - 1);
+        }
+    }
+
     return buffer;
 }
 
 // Replace text string
-// REQUIRES: strlen(), strstr(), strncpy(), strcpy()
+// REQUIRES: strstr(), strncpy()
+// TODO: If (replacement == "") remove "search" text
 // WARNING: Allocated memory must be manually freed
-char *TextReplace(const char *text, const char *replace, const char *by)
+char *TextReplace(const char *text, const char *search, const char *replacement)
 {
-    // Sanity checks and initialization
-    if (!text || !replace || !by) return NULL;
-
     char *result = NULL;
 
-    char *insertPoint = NULL;   // Next insert point
-    char *temp = NULL;          // Temp pointer
-    int replaceLen = 0;         // Replace string length of (the string to remove)
-    int byLen = 0;              // Replacement length (the string to replace by)
-    int lastReplacePos = 0;     // Distance between replace and end of last replace
-    int count = 0;              // Number of replacements
+    if ((text != NULL) && (search != NULL))
+    {
+        char *insertPoint = NULL;   // Next insert point
+        char *temp = NULL;          // Temp pointer
+        int textLen = 0;            // Text string length
+        int searchLen = 0;          // Search string length of (the string to remove)
+        int replaceLen = 0;         // Replacement length (the string to replace by)
+        int lastReplacePos = 0;     // Distance between next search and end of last replace
+        int count = 0;              // Number of replacements
 
-    replaceLen = TextLength(replace);
-    if (replaceLen == 0) return NULL;  // Empty replace causes infinite loop during count
+        textLen = TextLength(text);
+        searchLen = TextLength(search);
+        if (searchLen == 0) return NULL;  // Empty search causes infinite loop during count
 
-    byLen = TextLength(by);
+        replaceLen = TextLength(replacement);
 
-    // Count the number of replacements needed
-    insertPoint = (char *)text;
-    for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
+        // Count the number of replacements needed
+        insertPoint = (char *)text;
+        for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
 
-    // Allocate returning string and point temp to it
-    temp = result = (char *)RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1);
+        // Allocate returning string and point temp to it
+        int tempLen = textLen + (replaceLen - searchLen)*count + 1;
+        temp = result = (char *)RL_MALLOC(tempLen);
 
-    if (!result) return NULL;   // Memory could not be allocated
+        if (!result) return NULL;   // Memory could not be allocated
 
-    // First time through the loop, all the variable are set correctly from here on,
-    //  - 'temp' points to the end of the result string
-    //  - 'insertPoint' points to the next occurrence of replace in text
-    //  - 'text' points to the remainder of text after "end of replace"
-    while (count--)
-    {
-        insertPoint = strstr(text, replace);
-        lastReplacePos = (int)(insertPoint - text);
-        temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
-        temp = strcpy(temp, by) + byLen;
-        text += lastReplacePos + replaceLen; // Move to next "end of replace"
+        // First time through the loop, all the variable are set correctly from here on,
+        //  - 'temp' points to the end of the result string
+        //  - 'insertPoint' points to the next occurrence of replace in text
+        //  - 'text' points to the remainder of text after "end of replace"
+        while (count--)
+        {
+            insertPoint = (char *)strstr(text, search);
+            lastReplacePos = (int)(insertPoint - text);
+
+            // TODO: Review logic to avoid strcpy()
+            // OK - Those lines work
+            temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
+            temp = strcpy(temp, replacement) + replaceLen;
+            // WRONG - But not those ones
+            //temp = strncpy(temp, text, tempLen - 1) + lastReplacePos;
+            //tempLen -= lastReplacePos;
+            //temp = strncpy(temp, replacement, tempLen - 1) + replaceLen;
+            //tempLen -= replaceLen;
+
+            text += lastReplacePos + searchLen; // Move to next "end of replace"
+        }
+
+        // Copy remaind text part after replacement to result (pointed by moving temp)
+        strcpy(temp, text); // OK
+        //strncpy(temp, text, tempLen - 1); // WRONG
     }
 
-    // Copy remaind text part after replacement to result (pointed by moving temp)
-    strcpy(temp, text);
+    return result;
+}
 
+// Replace text between two specific strings
+// REQUIRES: strncpy()
+// NOTE: If (replacement == NULL) remove "begin"[ ]"end" text
+// WARNING: Returned string must be freed by user
+char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement)
+{
+    char *result = NULL;
+
+    if ((text != NULL) && (begin != NULL) && (end != NULL))
+    {
+        int beginIndex = TextFindIndex(text, begin);
+
+        if (beginIndex > -1)
+        {
+            int beginLen = TextLength(begin);
+            int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
+
+            if (endIndex > -1)
+            {
+                endIndex += (beginIndex + beginLen);
+
+                int textLen = TextLength(text);
+                int replaceLen = (replacement == NULL)? 0 : TextLength(replacement);
+                int toreplaceLen = endIndex - beginIndex - beginLen;
+                result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
+
+                strncpy(result, text, beginIndex + beginLen); // Copy first text part
+                if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
+                strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
+            }
+        }
+    }
+
     return result;
 }
 
@@ -1607,23 +1829,28 @@
 // WARNING: Allocated memory must be manually freed
 char *TextInsert(const char *text, const char *insert, int position)
 {
-    int textLen = TextLength(text);
-    int insertLen = TextLength(insert);
+    char *result = NULL;
 
-    char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
+    if ((text != NULL) && (insert != NULL))
+    {
+        int textLen = TextLength(text);
+        int insertLen = TextLength(insert);
 
-    for (int i = 0; i < position; i++) result[i] = text[i];
-    for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
-    for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
+        result = (char *)RL_MALLOC(textLen + insertLen + 1);
 
-    result[textLen + insertLen] = '\0';     // Make sure text string is valid!
+        for (int i = 0; i < position; i++) result[i] = text[i];
+        for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
+        for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
 
+        result[textLen + insertLen] = '\0'; // Add EOL
+    }
+
     return result;
 }
 
 // Join text strings with delimiter
 // REQUIRES: memset(), memcpy()
-const char *TextJoin(const char **textList, int count, const char *delimiter)
+char *TextJoin(char **textList, int count, const char *delimiter)
 {
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@@ -1657,7 +1884,7 @@
 
 // Split string into multiple strings
 // REQUIRES: memset()
-const char **TextSplit(const char *text, char delimiter, int *count)
+char **TextSplit(const char *text, char delimiter, int *count)
 {
     // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
     // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
@@ -1665,11 +1892,11 @@
     //      1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT
     //      2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
 
-    static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL };
-    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
+    static char *buffers[MAX_TEXTSPLIT_COUNT] = { NULL }; // Pointers to buffer[] text data
+    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; // Text data with '\0' separators
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
-    result[0] = buffer;
+    buffers[0] = buffer;
     int counter = 0;
 
     if (text != NULL)
@@ -1684,7 +1911,7 @@
             else if (buffer[i] == delimiter)
             {
                 buffer[i] = '\0';   // Set an end of string at this point
-                result[counter] = buffer + i + 1;
+                buffers[counter] = buffer + i + 1;
                 counter++;
 
                 if (counter == MAX_TEXTSPLIT_COUNT) break;
@@ -1693,27 +1920,32 @@
     }
 
     *count = counter;
-    return result;
+    return buffers;
 }
 
 // Append text at specific position and move cursor
 // WARNING: It's up to the user to make sure appended text does not overflow the buffer!
-// REQUIRES: strcpy()
 void TextAppend(char *text, const char *append, int *position)
 {
-    strcpy(text + *position, append);
-    *position += TextLength(append);
+    if ((text != NULL) && (append != NULL))
+    {
+        TextCopy(text + *position, append);
+        *position += TextLength(append);
+    }
 }
 
 // Find first text occurrence within a string
 // REQUIRES: strstr()
-int TextFindIndex(const char *text, const char *find)
+int TextFindIndex(const char *text, const char *search)
 {
     int position = -1;
 
-    char *ptr = strstr(text, find);
+    if (text != NULL)
+    {
+        char *ptr = (char *)strstr(text, search);
 
-    if (ptr != NULL) position = (int)(ptr - text);
+        if (ptr != NULL) position = (int)(ptr - text);
+    }
 
     return position;
 }
@@ -1721,7 +1953,7 @@
 // Get upper case version of provided string
 // WARNING: Limited functionality, only basic characters set
 // TODO: Support UTF-8 diacritics to upper-case, check codepoints
-const char *TextToUpper(const char *text)
+char *TextToUpper(const char *text)
 {
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@@ -1740,7 +1972,7 @@
 
 // Get lower case version of provided string
 // WARNING: Limited functionality, only basic characters set
-const char *TextToLower(const char *text)
+char *TextToLower(const char *text)
 {
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@@ -1759,7 +1991,7 @@
 
 // Get Pascal case notation version of provided string
 // WARNING: Limited functionality, only basic characters set
-const char *TextToPascal(const char *text)
+char *TextToPascal(const char *text)
 {
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
@@ -1778,6 +2010,7 @@
             {
                 j++;
                 if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32;
+                else if ((text[j] >= '0') && (text[j] <= '9')) buffer[i] = text[j];
             }
         }
     }
@@ -1787,9 +2020,9 @@
 
 // Get snake case notation version of provided string
 // WARNING: Limited functionality, only basic characters set
-const char *TextToSnake(const char *text)
+char *TextToSnake(const char *text)
 {
-    static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0};
+    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
     if (text != NULL)
@@ -1815,9 +2048,9 @@
 
 // Get Camel case notation version of provided string
 // WARNING: Limited functionality, only basic characters set
-const char *TextToCamel(const char *text)
+char *TextToCamel(const char *text)
 {
-    static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0};
+    static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
     if (text != NULL)
@@ -1846,23 +2079,29 @@
 // WARNING: Allocated memory must be manually freed
 char *LoadUTF8(const int *codepoints, int length)
 {
-    // We allocate enough memory to fit all possible codepoints
-    // NOTE: 5 bytes for every codepoint should be enough
-    char *text = (char *)RL_CALLOC(length*5, 1);
-    const char *utf8 = NULL;
-    int size = 0;
+    char *text = NULL;
 
-    for (int i = 0, bytes = 0; i < length; i++)
+    if ((codepoints != NULL) && (length > 0))
     {
-        utf8 = CodepointToUTF8(codepoints[i], &bytes);
-        memcpy(text + size, utf8, bytes);
-        size += bytes;
-    }
+        // We allocate enough memory to fit all possible codepoints
+        // NOTE: 5 bytes for every codepoint should be enough
+        text = (char *)RL_CALLOC(length*5, 1);
+        const char *utf8 = NULL;
+        int size = 0;
 
-    // Resize memory to text length + string NULL terminator
-    void *ptr = RL_REALLOC(text, size + 1);
+        for (int i = 0, bytes = 0; i < length; i++)
+        {
+            utf8 = CodepointToUTF8(codepoints[i], &bytes);
+            memcpy(text + size, utf8, bytes);
+            size += bytes;
+        }
 
-    if (ptr != NULL) text = (char *)ptr;
+        // Create second buffer and copy data manually to it
+        char *temp = (char *)RL_CALLOC(size + 1, 1);
+        memcpy(temp, text, size);
+        RL_FREE(text);
+        text = temp;
+    }
 
     return text;
 }
@@ -1876,25 +2115,31 @@
 // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
 int *LoadCodepoints(const char *text, int *count)
 {
-    int textLength = TextLength(text);
-
-    int codepointSize = 0;
+    int *codepoints = NULL;
     int codepointCount = 0;
 
-    // Allocate a big enough buffer to store as many codepoints as text bytes
-    int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
-
-    for (int i = 0; i < textLength; codepointCount++)
+    if (text != NULL)
     {
-        codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
-        i += codepointSize;
-    }
+        int textLength = TextLength(text);
 
-    // Re-allocate buffer to the actual number of codepoints loaded
-    codepoints = (int *)RL_REALLOC(codepoints, codepointCount*sizeof(int));
+        // Allocate a big enough buffer to store as many codepoints as text bytes
+        codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
 
-    *count = codepointCount;
+        int codepointSize = 0;
+        for (int i = 0; i < textLength; codepointCount++)
+        {
+            codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
+            i += codepointSize;
+        }
 
+        // Create second buffer and copy data manually to it
+        int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
+        for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
+        RL_FREE(codepoints);
+        codepoints = temp;
+    }
+
+    *count = codepointCount;
     return codepoints;
 }
 
@@ -1909,16 +2154,17 @@
 int GetCodepointCount(const char *text)
 {
     unsigned int length = 0;
-    char *ptr = (char *)&text[0];
+    const char *ptr = text;
 
-    while (*ptr != '\0')
+    if (ptr != NULL)
     {
-        int next = 0;
-        GetCodepointNext(ptr, &next);
-
-        ptr += next;
-
-        length++;
+        while (*ptr != '\0')
+        {
+            int next = 0;
+            GetCodepointNext(ptr, &next);
+            ptr += next;
+            length++;
+        }
     }
 
     return length;
@@ -1983,12 +2229,15 @@
     0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
     0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 */
-    // NOTE: on decode errors we return as soon as possible
 
+
     int codepoint = 0x3f;   // Codepoint (defaults to '?')
-    int octet = (unsigned char)(text[0]); // The first UTF8 octet
     *codepointSize = 1;
+    if (text == NULL) return codepoint;
 
+    // NOTE: on decode errors we return as soon as possible
+    int octet = (unsigned char)(text[0]); // The first UTF8 octet
+
     if (octet <= 0x7f)
     {
         // Only one octet (ASCII range x00-7F)
@@ -2079,6 +2328,7 @@
     const char *ptr = text;
     int codepoint = 0x3f;       // Codepoint (defaults to '?')
     *codepointSize = 1;
+    if (text == NULL) return codepoint;
 
     // Get current codepoint and bytes processed
     if (0xf0 == (0xf8 & ptr[0]))
@@ -2117,22 +2367,22 @@
 {
     const char *ptr = text;
     int codepoint = 0x3f;       // Codepoint (defaults to '?')
-    int cpSize = 0;
-    *codepointSize = 0;
+    *codepointSize = 1;
+    if (text == NULL) return codepoint;
 
     // Move to previous codepoint
     do ptr--;
     while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) ==  0x80));
 
+    int cpSize = 0;
     codepoint = GetCodepointNext(ptr, &cpSize);
-
     if (codepoint != 0) *codepointSize = cpSize;
 
     return codepoint;
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 #if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF)
 // Read a line from memory
@@ -2263,7 +2513,7 @@
 
     RL_FREE(imFonts);
 
-    if (isGpuReady) font.texture = LoadTextureFromImage(fullFont);
+    font.texture = LoadTextureFromImage(fullFont);
 
     // Fill font characters info data
     font.baseSize = fontSize;
@@ -2272,7 +2522,15 @@
     font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
     font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
 
-    int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID;
+    int charId = 0;
+    int charX = 0;
+    int charY = 0;
+    int charWidth = 0;
+    int charHeight = 0;
+    int charOffsetX = 0;
+    int charOffsetY = 0;
+    int charAdvanceX = 0;
+    int pageID = 0;
 
     for (int i = 0; i < glyphCount; i++)
     {
@@ -2305,7 +2563,7 @@
     UnloadImage(fullFont);
     UnloadFileText(fileText);
 
-    if (isGpuReady && (font.texture.id == 0))
+    if (font.texture.id == 0)
     {
         UnloadFont(font);
         font = GetFontDefault();
@@ -2315,30 +2573,28 @@
 
     return font;
 }
-
 #endif
 
 #if defined(SUPPORT_FILEFORMAT_BDF)
-
 // Convert hexadecimal to decimal (single digit)
 static unsigned char HexToInt(char hex)
 {
-    if (hex >= '0' && hex <= '9') return hex - '0';
-    else if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10;
-    else if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10;
+    if ((hex >= '0') && (hex <= '9')) return hex - '0';
+    else if ((hex >= 'a') && (hex <= 'f')) return hex - 'a' + 10;
+    else if ((hex >= 'A') && (hex <= 'F')) return hex - 'A' + 10;
     else return 0;
 }
 
 // Load font data for further use
 // NOTE: Requires BDF font memory data
-static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize)
+static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize)
 {
     #define MAX_BUFFER_SIZE 256
 
     char buffer[MAX_BUFFER_SIZE] = { 0 };
 
     GlyphInfo *glyphs = NULL;
-    bool genFontChars = false;
+    bool internalCodepoints = false;
 
     int totalReadBytes = 0;         // Data bytes read (total)
     int readBytes = 0;              // Data bytes read (line)
@@ -2365,21 +2621,25 @@
     int charBByoff0 = 0;            // Character bounding box Y0 offset
     int charDWidthX = 0;            // Character advance X
     int charDWidthY = 0;            // Character advance Y (unused)
-    GlyphInfo *charGlyphInfo = NULL; // Pointer to output glyph info (NULL if not set)
 
+    int *requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
+
     if (fileData == NULL) return glyphs;
 
     // In case no chars count provided, default to 95
     codepointCount = (codepointCount > 0)? codepointCount : 95;
 
-    // Fill fontChars in case not provided externally
-    // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
     if (codepoints == NULL)
     {
-        codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
-        for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
-        genFontChars = true;
+        // Fill internal codepoints array in case not provided externally
+        // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
+        for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32;
+        internalCodepoints = true;
     }
+    else
+    {
+        for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = codepoints[i];
+    }
 
     glyphs = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
 
@@ -2403,11 +2663,11 @@
 
             if (charBitmapStarted)
             {
-                if (charGlyphInfo != NULL)
+                if (glyphs != NULL)
                 {
                     int pixelY = charBitmapNextRow++;
 
-                    if (pixelY >= charGlyphInfo->image.height) break;
+                    if (pixelY >= glyphs->image.height) break;
 
                     for (int x = 0; x < readBytes; x++)
                     {
@@ -2417,9 +2677,9 @@
                         {
                             int pixelX = ((x*4) + bitX);
 
-                            if (pixelX >= charGlyphInfo->image.width) break;
+                            if (pixelX >= glyphs->image.width) break;
 
-                            if ((byte & (8 >> bitX)) > 0) ((unsigned char *)charGlyphInfo->image.data)[(pixelY*charGlyphInfo->image.width) + pixelX] = 255;
+                            if ((byte & (8 >> bitX)) > 0) ((unsigned char *)glyphs->image.data)[(pixelY*glyphs->image.width) + pixelX] = 255;
                         }
                     }
                 }
@@ -2451,30 +2711,30 @@
             if (strstr(buffer, "BITMAP") != NULL)
             {
                 // Search for glyph index in codepoints
-                charGlyphInfo = NULL;
+                glyphs = NULL;
 
-                for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++)
+                for (int index = 0; index < codepointCount; index++)
                 {
-                    if (codepoints[codepointIndex] == charEncoding)
+                    if (requiredCodepoints[index] == charEncoding)
                     {
-                        charGlyphInfo = &glyphs[codepointIndex];
+                        glyphs = &glyphs[index];
                         break;
                     }
                 }
 
                 // Init glyph info
-                if (charGlyphInfo != NULL)
+                if (glyphs != NULL)
                 {
-                    charGlyphInfo->value = charEncoding;
-                    charGlyphInfo->offsetX = charBBxoff0 + fontBByoff0;
-                    charGlyphInfo->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
-                    charGlyphInfo->advanceX = charDWidthX;
+                    glyphs->value = charEncoding;
+                    glyphs->offsetX = charBBxoff0 + fontBByoff0;
+                    glyphs->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
+                    glyphs->advanceX = charDWidthX;
 
-                    charGlyphInfo->image.data = RL_CALLOC(charBBw*charBBh, 1);
-                    charGlyphInfo->image.width = charBBw;
-                    charGlyphInfo->image.height = charBBh;
-                    charGlyphInfo->image.mipmaps = 1;
-                    charGlyphInfo->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+                    glyphs->image.data = RL_CALLOC(charBBw*charBBh, 1);
+                    glyphs->image.width = charBBw;
+                    glyphs->image.height = charBBh;
+                    glyphs->image.mipmaps = 1;
+                    glyphs->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
                 }
 
                 charBitmapStarted = true;
@@ -2525,14 +2785,14 @@
             {
                 charStarted = true;
                 charEncoding = -1;
-                charGlyphInfo = NULL;
+                glyphs = NULL;
                 charBBw = 0;
                 charBBh = 0;
                 charBBxoff0 = 0;
                 charBByoff0 = 0;
                 charDWidthX = 0;
                 charDWidthY = 0;
-                charGlyphInfo = NULL;
+                glyphs = NULL;
                 charBitmapStarted = false;
                 charBitmapNextRow = 0;
                 continue;
@@ -2557,7 +2817,7 @@
         }
     }
 
-    if (genFontChars) RL_FREE(codepoints);
+    RL_FREE(requiredCodepoints);
 
     if (fontMalformed)
     {
diff --git a/raylib/src/rtextures.c b/raylib/src/rtextures.c
--- a/raylib/src/rtextures.c
+++ b/raylib/src/rtextures.c
@@ -36,13 +36,13 @@
 *
 *   DEPENDENCIES:
 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
-*                          NOTE: stb_image has been slightly modified to support Android platform.
+*                          NOTE: stb_image has been slightly modified to support Android platform
 *       stb_image_resize - Multiple image resize algorithms
 *
 *
 *   LICENSE: zlib/libpng
 *
-*   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
+*   Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
 *
 *   This software is provided "as-is", without any express or implied warranty. In no event
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -63,14 +63,10 @@
 
 #include "raylib.h"             // Declares module functions
 
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"         // Defines module configuration flags
-#endif
+#include "config.h"             // Defines module configuration flags
 
 #if defined(SUPPORT_MODULE_RTEXTURES)
 
-#include "utils.h"              // Required for: TRACELOG()
 #include "rlgl.h"               // OpenGL abstraction layer to multiple versions
 
 #include <stdlib.h>             // Required for: malloc(), calloc(), free()
@@ -124,10 +120,6 @@
 #endif
 
 // Image fileformats not supported by default
-#if defined(__TINYC__)
-    #define STBI_NO_SIMD
-#endif
-
 #if (defined(SUPPORT_FILEFORMAT_BMP) || \
      defined(SUPPORT_FILEFORMAT_PNG) || \
      defined(SUPPORT_FILEFORMAT_TGA) || \
@@ -149,6 +141,10 @@
 
     #define STBI_NO_THREAD_LOCALS
 
+    #if defined(__TINYC__)
+        #define STBI_NO_SIMD
+    #endif
+
     #define STB_IMAGE_IMPLEMENTATION
     #include "external/stb_image.h"         // Required for: stbi_load_from_file()
                                             // NOTE: Used to read image data (multiple formats support)
@@ -169,10 +165,13 @@
         #pragma GCC diagnostic ignored "-Wunused-function"
     #endif
 
+    #define RL_GPUTEX_MALLOC RL_MALLOC
+    #define RL_GPUTEX_FREE RL_FREE
+    #define RL_GPUTEX_LOG(...) TRACELOG(LOG_WARNING, "IMAGE: " __VA_ARGS__)
+    #define RL_GPUTEX_SHOW_LOG_INFO
     #define RL_GPUTEX_IMPLEMENTATION
     #include "external/rl_gputex.h"         // Required for: rl_load_xxx_from_memory()
                                             // NOTE: Used to read compressed textures data (multiple formats support)
-
     #if defined(__GNUC__) // GCC and Clang
         #pragma GCC diagnostic pop
     #endif
@@ -218,6 +217,9 @@
     #pragma GCC diagnostic ignored "-Wunused-function"
 #endif
 
+#if defined(__TINYC__)
+    #define STBIR_NO_SIMD
+#endif
 #define STB_IMAGE_RESIZE_IMPLEMENTATION
 #include "external/stb_image_resize2.h"     // Required for: stbir_resize_uint8_linear() [ImageResize()]
 
@@ -252,7 +254,7 @@
 extern void LoadFontDefault(void);          // [Module: text] Loads default font, required by ImageDrawText()
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Declaration
+// Module Internal Functions Declaration
 //----------------------------------------------------------------------------------
 static float HalfToFloat(unsigned short x);
 static unsigned short FloatToHalf(float x);
@@ -261,7 +263,6 @@
 //----------------------------------------------------------------------------------
 // Module Functions Definition
 //----------------------------------------------------------------------------------
-
 // Load image from file into CPU memory (RAM)
 Image LoadImage(const char *fileName)
 {
@@ -416,7 +417,7 @@
 {
     Image image = { 0 };
 
-    // Security check for input data
+    // Security checks for input data
     if ((fileData == NULL) || (dataSize == 0))
     {
         TRACELOG(LOG_WARNING, "IMAGE: Invalid file data");
@@ -509,7 +510,7 @@
             image.data = qoi_decode(fileData, dataSize, &desc, (int) fileData[12]);
             image.width = desc.width;
             image.height = desc.height;
-            image.format = desc.channels == 4 ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
+            image.format = (desc.channels == 4)? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
             image.mipmaps = 1;
         }
     }
@@ -587,11 +588,10 @@
 // Load image from screen buffer and (screenshot)
 Image LoadImageFromScreen(void)
 {
-    Vector2 scale = GetWindowScaleDPI();
     Image image = { 0 };
 
-    image.width = (int)(GetScreenWidth()*scale.x);
-    image.height = (int)(GetScreenHeight()*scale.y);
+    image.width = (int)(GetRenderWidth());
+    image.height = (int)(GetRenderHeight());
     image.mipmaps = 1;
     image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
     image.data = rlReadScreenPixels(image.width, image.height);
@@ -644,16 +644,15 @@
         allocatedData = true;
     }
 
+    if (false) { } // Required to attach following 'else' cases
 #if defined(SUPPORT_FILEFORMAT_PNG)
-    if (IsFileExtension(fileName, ".png"))
+    else if (IsFileExtension(fileName, ".png"))
     {
         int dataSize = 0;
         unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize);
         result = SaveFileData(fileName, fileData, dataSize);
         RL_FREE(fileData);
     }
-#else
-    if (false) { }
 #endif
 #if defined(SUPPORT_FILEFORMAT_BMP)
     else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
@@ -697,6 +696,7 @@
         // NOTE: It's up to the user to track image parameters
         result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
     }
+    else TRACELOG(LOG_WARNING, "IMAGE: Export image format requested not supported");
 
     if (allocatedData) RL_FREE(imgData);
 #endif      // SUPPORT_IMAGE_EXPORT
@@ -761,13 +761,13 @@
     byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
     byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)                                //\n");
+    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2026 Ramon Santamaria (@raysan5)                                //\n");
     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
     byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
 
     // Get file name from path and convert variable name to uppercase
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
     for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
 
     // Add image information
@@ -807,8 +807,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -827,16 +827,25 @@
     float cosDir = cosf(radianDirection);
     float sinDir = sinf(radianDirection);
 
+    // Calculate how far the top-left pixel is along the gradient direction from the center of said gradient
+    float startingPos = 0.5f - (cosDir*width/2) - (sinDir*height/2);
+
+    // With directions that lie in the first or third quadrant (i.e. from top-left to
+    // bottom-right or vice-versa), pixel (0, 0) is the farthest point on the gradient
+    // (i.e. the pixel which should become one of the gradient's ends color); while for
+    // directions that lie in the second or fourth quadrant, that point is pixel (width, 0)
+    float maxPosValue = ((signbit(sinDir) != 0) == (signbit(cosDir) != 0))? fabsf(startingPos) : fabsf(startingPos + width*cosDir);
     for (int i = 0; i < width; i++)
     {
         for (int j = 0; j < height; j++)
         {
             // Calculate the relative position of the pixel along the gradient direction
-            float pos = (i*cosDir + j*sinDir)/(width*cosDir + height*sinDir);
+            float pos = (startingPos + (i*cosDir + j*sinDir))/maxPosValue;
 
             float factor = pos;
-            factor = (factor > 1.0f)? 1.0f : factor;  // Clamp to [0,1]
-            factor = (factor < 0.0f)? 0.0f : factor;  // Clamp to [0,1]
+            factor = (factor > 1.0f)? 1.0f : factor;  // Clamp to [-1,1]
+            factor = (factor < -1.0f)? -1.0f : factor;  // Clamp to [-1,1]
+            factor = factor/2.0f + 0.5f;
 
             // Generate the color for this pixel
             pixels[j*width + i].r = (int)((float)end.r*factor + (float)start.r*(1.0f - factor));
@@ -850,8 +859,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -887,8 +896,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -936,8 +945,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -961,8 +970,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -984,8 +993,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -996,7 +1005,8 @@
 {
     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
 
-    float aspectRatio = (float)width / (float)height;
+    float aspectRatio = (float)width/(float)height;
+
     for (int y = 0; y < height; y++)
     {
         for (int x = 0; x < width; x++)
@@ -1025,7 +1035,7 @@
             // We need to normalize the data from [-1..1] to [0..1]
             float np = (p + 1.0f)/2.0f;
 
-            int intensity = (int)(np*255.0f);
+            unsigned char intensity = (unsigned char)(np*255.0f);
             pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
         }
     }
@@ -1034,8 +1044,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -1085,11 +1095,12 @@
                 }
             }
 
-            // I made this up, but it seems to give good results at all tile sizes
+            // This approach seems to give good results at all tile sizes
             int intensity = (int)(minDistance*256.0f/tileSize);
             if (intensity > 255) intensity = 255;
 
-            pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
+            unsigned char intensityUC = (unsigned char)intensity;
+            pixels[y*width + x] = (Color){ intensityUC, intensityUC, intensityUC, 255 };
         }
     }
 
@@ -1099,8 +1110,8 @@
         .data = pixels,
         .width = width,
         .height = height,
-        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
-        .mipmaps = 1
+        .mipmaps = 1,
+        .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8
     };
 
     return image;
@@ -1111,21 +1122,18 @@
 {
     Image image = { 0 };
 
-#if defined(SUPPORT_MODULE_RTEXT)
-    int textLength = TextLength(text);
-    int imageViewSize = width*height;
-
+    int imageSize = width*height;
     image.width = width;
     image.height = height;
     image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
-    image.data = RL_CALLOC(imageViewSize, 1);
+    image.data = RL_CALLOC(imageSize, 1);
     image.mipmaps = 1;
 
-    memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
-#else
-    TRACELOG(LOG_WARNING, "IMAGE: GenImageText() requires module: rtext");
-    image = GenImageColor(width, height, BLACK);     // Generating placeholder black image rectangle
-#endif
+    if (text != NULL)
+    {
+        int textLength = (int)strlen(text);
+        memcpy(image.data, text, (textLength > imageSize)? imageSize : textLength);
+    }
 
     return image;
 }
@@ -1474,8 +1482,9 @@
 {
     Image imText = { 0 };
 #if defined(SUPPORT_MODULE_RTEXT)
-    int size = (int)strlen(text);   // Get size in bytes of text
+    if (text == NULL) return imText;
 
+    int textLength = (int)strlen(text); // Get length of text in bytes
     int textOffsetX = 0;            // Image drawing position X
     int textOffsetY = 0;            // Offset between lines (on linebreak '\n')
 
@@ -1486,7 +1495,7 @@
     // Create image to store text
     imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
 
-    for (int i = 0; i < size;)
+    for (int i = 0; i < textLength;)
     {
         // Get next codepoint from byte string and glyph index in font
         int codepointByteCount = 0;
@@ -1702,7 +1711,7 @@
 }
 
 // Resize and image to new size using Nearest-Neighbor scaling algorithm
-void ImageResizeNN(Image *image,int newWidth,int newHeight)
+void ImageResizeNN(Image *image, int newWidth, int newHeight)
 {
     // Security check to avoid program crash
     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
@@ -1714,7 +1723,8 @@
     int xRatio = (int)((image->width << 16)/newWidth) + 1;
     int yRatio = (int)((image->height << 16)/newHeight) + 1;
 
-    int x2, y2;
+    int x2 = 0;
+    int y2 = 0;
     for (int y = 0; y < newHeight; y++)
     {
         for (int x = 0; x < newWidth; x++)
@@ -2094,8 +2104,8 @@
     Color *pixels = LoadImageColors(*image);
 
     // Loop switches between pixelsCopy1 and pixelsCopy2
-    Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
-    Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
+    Vector4 *pixelsCopy1 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
+    Vector4 *pixelsCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
 
     for (int i = 0; i < (image->height*image->width); i++)
     {
@@ -2209,9 +2219,9 @@
         else if (pixelsCopy1[i].w <= 255.0f)
         {
             float alpha = (float)pixelsCopy1[i].w/255.0f;
-            pixels[i].r = (unsigned char)((float)pixelsCopy1[i].x/alpha);
-            pixels[i].g = (unsigned char)((float)pixelsCopy1[i].y/alpha);
-            pixels[i].b = (unsigned char)((float)pixelsCopy1[i].z/alpha);
+            pixels[i].r = (unsigned char)fminf((float)pixelsCopy1[i].x/alpha, 255.0);
+            pixels[i].g = (unsigned char)fminf((float)pixelsCopy1[i].y/alpha, 255.0);
+            pixels[i].b = (unsigned char)fminf((float)pixelsCopy1[i].z/alpha, 255.0);
             pixels[i].a = (unsigned char) pixelsCopy1[i].w;
         }
     }
@@ -2243,8 +2253,8 @@
 
     Color *pixels = LoadImageColors(*image);
 
-    Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
-    Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
+    Vector4 *imageCopy2 = (Vector4 *)RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
+    Vector4 *temp = (Vector4 *)RL_MALLOC(kernelSize*sizeof(Vector4));
 
     for (int i = 0; i < kernelSize; i++)
     {
@@ -2381,7 +2391,7 @@
         if (mipWidth < 1) mipWidth = 1;
         if (mipHeight < 1) mipHeight = 1;
 
-        TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
+        TRACELOG(LOG_DEBUG, "IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
 
         mipCount++;
         mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format);       // Add mipmap size (in bytes)
@@ -2389,28 +2399,23 @@
 
     if (image->mipmaps < mipCount)
     {
-        void *temp = RL_REALLOC(image->data, mipSize);
-
-        if (temp != NULL) image->data = temp;      // Assign new pointer (new size) to store mipmaps data
-        else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
+        // Create second buffer and copy data manually to it
+        void *temp = RL_CALLOC(mipSize, 1);
+        memcpy(temp, image->data, GetPixelDataSize(image->width, image->height, image->format));
+        RL_FREE(image->data);
+        image->data = temp;
 
         // Pointer to allocated memory point where store next mipmap level data
-        unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
+        unsigned char *nextmip = (unsigned char *)image->data;
 
-        mipWidth = image->width/2;
-        mipHeight = image->height/2;
+        mipWidth = image->width;
+        mipHeight = image->height;
         mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
         Image imCopy = ImageCopy(*image);
 
         for (int i = 1; i < mipCount; i++)
         {
-            TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
-
-            ImageResize(&imCopy, mipWidth, mipHeight);  // Uses internally Mitchell cubic downscale filter
-
-            memcpy(nextmip, imCopy.data, mipSize);
             nextmip += mipSize;
-            image->mipmaps++;
 
             mipWidth /= 2;
             mipHeight /= 2;
@@ -2420,9 +2425,17 @@
             if (mipHeight < 1) mipHeight = 1;
 
             mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
+
+            if (i < image->mipmaps) continue;
+
+            TRACELOG(LOG_DEBUG, "IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
+            ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
+            memcpy(nextmip, imCopy.data, mipSize);
         }
 
         UnloadImage(imCopy);
+
+        image->mipmaps = mipCount;
     }
     else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
 }
@@ -2472,8 +2485,13 @@
         Color oldPixel = WHITE;
         Color newPixel = WHITE;
 
-        int rError, gError, bError;
-        unsigned short rPixel, gPixel, bPixel, aPixel;   // Used for 16bit pixel composition
+        int rError = 0;
+        int gError = 0;
+        int bError = 0;
+        unsigned short rPixel = 0;   // Used for 16bit pixel composition
+        unsigned short gPixel = 0;
+        unsigned short bPixel = 0;
+        unsigned short aPixel = 0;
 
         #define MIN(a,b) (((a)<(b))?(a):(b))
 
@@ -2915,7 +2933,16 @@
     image->data = pixels;
     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
 
-    ImageFormat(image, format);
+    // Only convert back to original format if it supported alpha
+    if ((format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
+        (format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) ||
+        (format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
+        (format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
+        (format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) ||
+        (format == PIXELFORMAT_COMPRESSED_DXT1_RGB) ||
+        (format == PIXELFORMAT_COMPRESSED_ETC1_RGB) ||
+        (format == PIXELFORMAT_COMPRESSED_ETC2_RGB) ||
+        (format == PIXELFORMAT_COMPRESSED_PVRT_RGB)) ImageFormat(image, format);
 }
 #endif      // SUPPORT_IMAGE_MANIPULATION
 
@@ -3319,11 +3346,14 @@
 
     unsigned char *pSrcPixel = (unsigned char *)dst->data;
     int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
+    int totalPixels = dst->width*dst->height;
 
-    // Repeat the first pixel data throughout the image
-    for (int i = 1; i < dst->width*dst->height; i++)
+    // Repeat the first pixel data throughout the image,
+    // doubling the pixels copied on each iteration
+    for (int i = 1; i < totalPixels; i *= 2)
     {
-        memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, bytesPerPixel);
+        int pixelsToCopy = MIN(i, totalPixels - i);
+        memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel);
     }
 }
 
@@ -3553,35 +3583,44 @@
     int dx = x2 - x1;
     int dy = y2 - y1;
 
-    // Draw the main line between (x1, y1) and (x2, y2)
-    ImageDrawLine(dst, x1, y1, x2, y2, color);
-
     // Determine if the line is more horizontal or vertical
-    if (dx != 0 && abs(dy/dx) < 1)
+    if ((dx != 0) && (abs(dy/dx) < 1))
     {
         // Line is more horizontal
-        // Calculate half the width of the line
-        int wy = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dx));
 
-        // Draw additional lines above and below the main line
-        for (int i = 1; i <= wy; i++)
+        // How many additional lines to draw
+        int wy = thick - 1;
+
+        // Draw the main line and lower half
+        for (int i = 0; i <= ((wy+1)/2); i++)
         {
-            ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line
-            ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line
+            ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color);
         }
+
+        // Draw the upper half
+        for (int i = 1; i <= (wy/2); i++)
+        {
+            ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color);
+        }
     }
     else if (dy != 0)
     {
         // Line is more vertical or perfectly horizontal
-        // Calculate half the width of the line
-        int wx = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dy));
 
-        // Draw additional lines to the left and right of the main line
-        for (int i = 1; i <= wx; i++)
+        // How many additional lines to draw
+        int wx = thick - 1;
+
+        //Draw the main line and right half
+        for (int i = 0; i <= ((wx+1)/2); i++)
         {
-            ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line
-            ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line
+            ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color);
         }
+
+        // Draw the left half
+        for (int i = 1; i <= (wx/2); i++)
+        {
+            ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color);
+        }
     }
 }
 
@@ -3693,9 +3732,10 @@
     unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
 
     // Repeat the first pixel data throughout the row
-    for (int x = 1; x < (int)rec.width; x++)
+    for (int x = 1; x < (int)rec.width; x *= 2)
     {
-        memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel);
+        int pixelsToCopy = MIN(x, (int)rec.width - x);
+        memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel);
     }
 
     // Repeat the first row data for all other rows
@@ -3824,7 +3864,7 @@
 
     // Calculate the inverse of the sum of the barycentric coordinates for normalization
     // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional
-    //         calculations in the loop. This is acceptable because we are only interpolating colors.
+    //         calculations in the loop. This is acceptable because we are only interpolating colors
     // NOTE 2: This sum remains constant throughout the triangle
     float wInvSum = 255.0f/(w1Row + w2Row + w3Row);
 
@@ -3879,7 +3919,7 @@
 }
 
 // Draw a triangle fan defined by points within an image (first vertex is the center)
-void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color)
+void ImageDrawTriangleFan(Image *dst, const Vector2 *points, int pointCount, Color color)
 {
     if (pointCount >= 3)
     {
@@ -3891,7 +3931,7 @@
 }
 
 // Draw a triangle strip defined by points within an image
-void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color)
+void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, Color color)
 {
     if (pointCount >= 3)
     {
@@ -3911,7 +3951,6 @@
     if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
         (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
 
-    if (dst->mipmaps > 1) TRACELOG(LOG_WARNING, "Image drawing only applied to base mipmap level");
     if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
     else
     {
@@ -3967,11 +4006,11 @@
         //    [x] Consider fast path: no alpha blending required cases (src has no alpha)
         //    [x] Consider fast path: same src/dst format with no alpha -> direct line copy
         //    [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
-        //    [ ] Support f32bit channels drawing
-
-        // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 and PIXELFORMAT_UNCOMPRESSED_R1616B16A16
+        //    [ ] TODO: Support 16bit and 32bit (float) channels drawing
 
-        Color colSrc, colDst, blend;
+        Color colSrc = { 0 };
+        Color colDst = { 0 };
+        Color blend = { 0 };
         bool blendRequired = true;
 
         // Fast path: Avoid blend if source has no alpha to blend
@@ -4024,6 +4063,35 @@
         }
 
         if (useSrcMod) UnloadImage(srcMod);     // Unload source modified image
+
+        if ((dst->mipmaps > 1) && (src.mipmaps > 1))
+        {
+            Image mipmapDst = *dst;
+            mipmapDst.data = (char *)mipmapDst.data + GetPixelDataSize(mipmapDst.width, mipmapDst.height, mipmapDst.format);
+            mipmapDst.width /= 2;
+            mipmapDst.height /= 2;
+            mipmapDst.mipmaps--;
+
+            Image mipmapSrc = src;
+            mipmapSrc.data = (char *)mipmapSrc.data + GetPixelDataSize(mipmapSrc.width, mipmapSrc.height, mipmapSrc.format);
+            mipmapSrc.width /= 2;
+            mipmapSrc.height /= 2;
+            mipmapSrc.mipmaps--;
+
+            Rectangle mipmapSrcRec = srcRec;
+            mipmapSrcRec.width /= 2;
+            mipmapSrcRec.height /= 2;
+            mipmapSrcRec.x /= 2;
+            mipmapSrcRec.y /= 2;
+
+            Rectangle mipmapDstRec = dstRec;
+            mipmapDstRec.width /= 2;
+            mipmapDstRec.height /= 2;
+            mipmapDstRec.x /= 2;
+            mipmapDstRec.y /= 2;
+
+            ImageDraw(&mipmapDst, mipmapSrc, mipmapSrcRec, mipmapDstRec, tint);
+        }
     }
 }
 
@@ -4105,7 +4173,6 @@
         {
             if ((image.width/6) == image.height) { layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL; cubemap.width = image.width/6; }
             else if ((image.width/4) == (image.height/3)) { layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
-            else if (image.width >= (int)((float)image.height*1.85f)) { layout = CUBEMAP_LAYOUT_PANORAMA; cubemap.width = image.width/4; }
         }
         else if (image.height > image.width)
         {
@@ -4119,7 +4186,6 @@
         if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) cubemap.width = image.width/6;
         if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR) cubemap.width = image.width/3;
         if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE) cubemap.width = image.width/4;
-        if (layout == CUBEMAP_LAYOUT_PANORAMA) cubemap.width = image.width/4;
     }
 
     cubemap.height = cubemap.width;
@@ -4138,11 +4204,11 @@
         {
             faces = ImageCopy(image);       // Image data already follows expected convention
         }
-        else if (layout == CUBEMAP_LAYOUT_PANORAMA)
+        /*else if (layout == CUBEMAP_LAYOUT_PANORAMA)
         {
-            // TODO: Convert panorama image to square faces...
-            // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
-        }
+            // TODO: Implement panorama by converting image to square faces...
+            // REF: https://github.com/denivip/panorama/blob/master/panorama.cpp
+        } */
         else
         {
             if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = (float)size*i;
@@ -4166,22 +4232,32 @@
             }
 
             // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
+            // NOTE: Image formatting does not work with compressed textures
             faces = GenImageColor(size, size*6, MAGENTA);
             ImageFormat(&faces, image.format);
 
-            // NOTE: Image formatting does not work with compressed textures
+            Image mipmapped = ImageCopy(image);
+        #if defined(SUPPORT_IMAGE_MANIPULATION)
+            if (image.mipmaps > 1)
+            {
+                ImageMipmaps(&mipmapped);
+                ImageMipmaps(&faces);
+            }
+        #endif
 
-            for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
+            for (int i = 0; i < 6; i++) ImageDraw(&faces, mipmapped, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
+
+            UnloadImage(mipmapped);
         }
 
         // NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
         // one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
-        cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
+        cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format, faces.mipmaps);
 
         if (cubemap.id != 0)
         {
             cubemap.format = faces.format;
-            cubemap.mipmaps = 1;
+            cubemap.mipmaps = faces.mipmaps;
         }
         else TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
 
@@ -4237,13 +4313,11 @@
 {
     bool result = false;
 
-    // TODO: Validate maximum texture size supported by GPU
-
     if ((texture.id > 0) &&         // Validate OpenGL id (texture uplaoded to GPU)
         (texture.width > 0) &&      // Validate texture width
         (texture.height > 0) &&     // Validate texture height
         (texture.format > 0) &&     // Validate texture pixel format
-        (texture.mipmaps > 0)) result = true;     // Validate texture mipmaps (at least 1 for basic mipmap level)
+        (texture.mipmaps > 0)) result = true; // Validate texture mipmaps (at least 1 for basic mipmap level)
 
     return result;
 }
@@ -4289,14 +4363,17 @@
 }
 
 // Update GPU texture with new data
-// NOTE: pixels data must match texture.format
+// NOTE 1: pixels data must match texture.format
+// NOTE 2: pixels data must contain at least as many pixels as texture
 void UpdateTexture(Texture2D texture, const void *pixels)
 {
     rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
 }
 
 // Update GPU texture rectangle with new data
-// NOTE: pixels data must match texture.format
+// NOTE 1: pixels data must match texture.format
+// NOTE 2: pixels data must contain as many pixels as rec contains
+// NOTE 3: rec must fit completely within texture's width and height
 void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
 {
     rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
@@ -4530,7 +4607,7 @@
         // NOTE: Vertex position can be transformed using matrices
         // but the process is way more costly than just calculating
         // the vertex positions manually, like done above
-        // I leave here the old implementation for educational purposes,
+        // Old implementation is left here for educational purposes,
         // just in case someone wants to do some performance test
         /*
         rlSetTexture(texture.id);
@@ -4608,17 +4685,23 @@
             bottomBorder = patchHeight - topBorder;
         }
 
-        Vector2 vertA, vertB, vertC, vertD;
-        vertA.x = 0.0f;                             // outer left
-        vertA.y = 0.0f;                             // outer top
-        vertB.x = leftBorder;                       // inner left
-        vertB.y = topBorder;                        // inner top
-        vertC.x = patchWidth  - rightBorder;        // inner right
-        vertC.y = patchHeight - bottomBorder;       // inner bottom
-        vertD.x = patchWidth;                       // outer right
-        vertD.y = patchHeight;                      // outer bottom
+        Vector2 vertA = { 0 };
+        Vector2 vertB = { 0 };
+        Vector2 vertC = { 0 };
+        Vector2 vertD = { 0 };
+        vertA.x = 0.0f;                             // Outer left
+        vertA.y = 0.0f;                             // Outer top
+        vertB.x = leftBorder;                       // Inner left
+        vertB.y = topBorder;                        // Inner top
+        vertC.x = patchWidth  - rightBorder;        // Inner right
+        vertC.y = patchHeight - bottomBorder;       // Inner bottom
+        vertD.x = patchWidth;                       // Outer right
+        vertD.y = patchHeight;                      // Outer bottom
 
-        Vector2 coordA, coordB, coordC, coordD;
+        Vector2 coordA = { 0 };
+        Vector2 coordB = { 0 };
+        Vector2 coordC = { 0 };
+        Vector2 coordD = { 0 };
         coordA.x = nPatchInfo.source.x/width;
         coordA.y = nPatchInfo.source.y/height;
         coordB.x = (nPatchInfo.source.x + leftBorder)/width;
@@ -4834,7 +4917,9 @@
 {
     Vector3 hsv = { 0 };
     Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
-    float min, max, delta;
+    float min = 0.0f;
+    float max = 0.0f;
+    float delta = 0.0f;
 
     min = rgb.x < rgb.y? rgb.x : rgb.y;
     min = min  < rgb.z? min  : rgb.z;
@@ -5099,10 +5184,10 @@
 {
     Color color;
 
-    color.r = (unsigned char)(hexValue >> 24) & 0xFF;
-    color.g = (unsigned char)(hexValue >> 16) & 0xFF;
-    color.b = (unsigned char)(hexValue >> 8) & 0xFF;
-    color.a = (unsigned char)hexValue & 0xFF;
+    color.r = (unsigned char)(hexValue >> 24) & 0xff;
+    color.g = (unsigned char)(hexValue >> 16) & 0xff;
+    color.b = (unsigned char)(hexValue >> 8) & 0xff;
+    color.a = (unsigned char)hexValue & 0xff;
 
     return color;
 }
@@ -5334,7 +5419,7 @@
 }
 
 //----------------------------------------------------------------------------------
-// Module specific Functions Definition
+// Module Internal Functions Definition
 //----------------------------------------------------------------------------------
 // Convert half-float (stored as unsigned short) to float
 // REF: https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
@@ -5342,14 +5427,19 @@
 {
     float result = 0.0f;
 
-    const unsigned int e = (x & 0x7C00) >> 10; // Exponent
-    const unsigned int m = (x & 0x03FF) << 13; // Mantissa
-    const float fm = (float)m;
-    const unsigned int v = (*(unsigned int*)&fm) >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
-    const unsigned int r = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized
+    union {
+        float fm;
+        unsigned int ui;
+    } uni;
 
-    result = *(float *)&r;
+    const unsigned int e = (x & 0x7c00) >> 10; // Exponent
+    const unsigned int m = (x & 0x03ff) << 13; // Mantissa
+    uni.fm = (float)m;
+    const unsigned int v = uni.ui >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
+    uni.ui = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007fe000)); // sign : normalized : denormalized
 
+    result = uni.fm;
+
     return result;
 }
 
@@ -5358,12 +5448,18 @@
 {
     unsigned short result = 0;
 
-    const unsigned int b = (*(unsigned int*) & x) + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
-    const unsigned int e = (b & 0x7F800000) >> 23; // Exponent
-    const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
+    union {
+        float fm;
+        unsigned int ui;
+    } uni;
+    uni.fm = x;
 
-    result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate
+    const unsigned int b = uni.ui + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
+    const unsigned int e = (b & 0x7f800000) >> 23; // Exponent
+    const unsigned int m = b & 0x007fffff; // Mantissa; in line below: 0x007ff000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
 
+    result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7c00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007ff000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7fff; // sign : normalized : denormalized : saturate
+
     return result;
 }
 
@@ -5451,6 +5547,7 @@
                     pixels[i].z = 0.0f;
                     pixels[i].w = 1.0f;
 
+                    k += 1;
                 } break;
                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
                 {
@@ -5476,6 +5573,8 @@
                     pixels[i].y = 0.0f;
                     pixels[i].z = 0.0f;
                     pixels[i].w = 1.0f;
+
+                    k += 1;
                 } break;
                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
                 {
diff --git a/raylib/src/utils.c b/raylib/src/utils.c
deleted file mode 100644
--- a/raylib/src/utils.c
+++ /dev/null
@@ -1,510 +0,0 @@
-/**********************************************************************************************
-*
-*   raylib.utils - Some common utility functions
-*
-*   CONFIGURATION:
-*       #define SUPPORT_TRACELOG
-*           Show TraceLog() output messages
-*           NOTE: By default LOG_DEBUG traces not shown
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#include "raylib.h"                     // WARNING: Required for: LogType enum
-
-// Check if config flags have been externally provided on compilation line
-#if !defined(EXTERNAL_CONFIG_FLAGS)
-    #include "config.h"                 // Defines module configuration flags
-#endif
-
-#include "utils.h"
-
-#if defined(PLATFORM_ANDROID)
-    #include <errno.h>                  // Required for: Android error types
-    #include <android/log.h>            // Required for: Android log system: __android_log_vprint()
-    #include <android/asset_manager.h>  // Required for: Android assets manager: AAsset, AAssetManager_open()...
-#endif
-
-#include <stdlib.h>                     // Required for: exit()
-#include <stdio.h>                      // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose()
-#include <stdarg.h>                     // Required for: va_list, va_start(), va_end()
-#include <string.h>                     // Required for: strcpy(), strcat()
-
-//----------------------------------------------------------------------------------
-// Defines and Macros
-//----------------------------------------------------------------------------------
-#ifndef MAX_TRACELOG_MSG_LENGTH
-    #define MAX_TRACELOG_MSG_LENGTH     256         // Max length of one trace-log message
-#endif
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-static int logTypeLevel = LOG_INFO;                 // Minimum log type level
-
-static TraceLogCallback traceLog = NULL;            // TraceLog callback function pointer
-static LoadFileDataCallback loadFileData = NULL;    // LoadFileData callback function pointer
-static SaveFileDataCallback saveFileData = NULL;    // SaveFileText callback function pointer
-static LoadFileTextCallback loadFileText = NULL;    // LoadFileText callback function pointer
-static SaveFileTextCallback saveFileText = NULL;    // SaveFileText callback function pointer
-
-//----------------------------------------------------------------------------------
-// Functions to set internal callbacks
-//----------------------------------------------------------------------------------
-void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; }              // Set custom trace log
-void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; }  // Set custom file data loader
-void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; }  // Set custom file data saver
-void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; }  // Set custom file text loader
-void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; }  // Set custom file text saver
-
-#if defined(PLATFORM_ANDROID)
-static AAssetManager *assetManager = NULL;          // Android assets manager pointer
-static const char *internalDataPath = NULL;         // Android internal data path
-#endif
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Declaration
-//----------------------------------------------------------------------------------
-#if defined(PLATFORM_ANDROID)
-FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
-              fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
-
-static int android_read(void *cookie, char *buf, int size);
-static int android_write(void *cookie, const char *buf, int size);
-static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
-static int android_close(void *cookie);
-#endif
-
-//----------------------------------------------------------------------------------
-// Module Functions Definition - Utilities
-//----------------------------------------------------------------------------------
-
-// Set the current threshold (minimum) log level
-void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
-
-// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
-void TraceLog(int logType, const char *text, ...)
-{
-#if defined(SUPPORT_TRACELOG)
-    // Message has level below current threshold, don't emit
-    if (logType < logTypeLevel) return;
-
-    va_list args;
-    va_start(args, text);
-
-    if (traceLog)
-    {
-        traceLog(logType, text, args);
-        va_end(args);
-        return;
-    }
-
-#if defined(PLATFORM_ANDROID)
-    switch (logType)
-    {
-        case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break;
-        case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break;
-        case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break;
-        case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break;
-        case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break;
-        case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break;
-        default: break;
-    }
-#else
-    char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 };
-
-    switch (logType)
-    {
-        case LOG_TRACE: strcpy(buffer, "TRACE: "); break;
-        case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break;
-        case LOG_INFO: strcpy(buffer, "INFO: "); break;
-        case LOG_WARNING: strcpy(buffer, "WARNING: "); break;
-        case LOG_ERROR: strcpy(buffer, "ERROR: "); break;
-        case LOG_FATAL: strcpy(buffer, "FATAL: "); break;
-        default: break;
-    }
-
-    unsigned int textSize = (unsigned int)strlen(text);
-    memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12));
-    strcat(buffer, "\n");
-    vprintf(buffer, args);
-    fflush(stdout);
-#endif
-
-    va_end(args);
-
-    if (logType == LOG_FATAL) exit(EXIT_FAILURE);  // If fatal logging, exit program
-
-#endif  // SUPPORT_TRACELOG
-}
-
-// Internal memory allocator
-// NOTE: Initializes to zero by default
-void *MemAlloc(unsigned int size)
-{
-    void *ptr = RL_CALLOC(size, 1);
-    return ptr;
-}
-
-// Internal memory reallocator
-void *MemRealloc(void *ptr, unsigned int size)
-{
-    void *ret = RL_REALLOC(ptr, size);
-    return ret;
-}
-
-// Internal memory free
-void MemFree(void *ptr)
-{
-    RL_FREE(ptr);
-}
-
-// Load data from file into a buffer
-unsigned char *LoadFileData(const char *fileName, int *dataSize)
-{
-    unsigned char *data = NULL;
-    *dataSize = 0;
-
-    if (fileName != NULL)
-    {
-        if (loadFileData)
-        {
-            data = loadFileData(fileName, dataSize);
-            return data;
-        }
-#if defined(SUPPORT_STANDARD_FILEIO)
-        FILE *file = fopen(fileName, "rb");
-
-        if (file != NULL)
-        {
-            // WARNING: On binary streams SEEK_END could not be found,
-            // using fseek() and ftell() could not work in some (rare) cases
-            fseek(file, 0, SEEK_END);
-            int size = ftell(file);     // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes)
-            fseek(file, 0, SEEK_SET);
-
-            if (size > 0)
-            {
-                data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
-
-                if (data != NULL)
-                {
-                    // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
-                    size_t count = fread(data, sizeof(unsigned char), size, file);
-
-                    // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
-                    // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation
-                    if (count > 2147483647)
-                    {
-                        TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
-
-                        RL_FREE(data);
-                        data = NULL;
-                    }
-                    else
-                    {
-                        *dataSize = (int)count;
-
-                        if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count);
-                        else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
-                    }
-                }
-                else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
-            }
-            else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
-
-            fclose(file);
-        }
-        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
-#else
-    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
-
-    return data;
-}
-
-// Unload file data allocated by LoadFileData()
-void UnloadFileData(unsigned char *data)
-{
-    RL_FREE(data);
-}
-
-// Save data to file from buffer
-bool SaveFileData(const char *fileName, void *data, int dataSize)
-{
-    bool success = false;
-
-    if (fileName != NULL)
-    {
-        if (saveFileData)
-        {
-            return saveFileData(fileName, data, dataSize);
-        }
-#if defined(SUPPORT_STANDARD_FILEIO)
-        FILE *file = fopen(fileName, "wb");
-
-        if (file != NULL)
-        {
-            // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
-            // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem
-            int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file);
-
-            if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
-            else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
-            else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
-
-            int result = fclose(file);
-            if (result == 0) success = true;
-        }
-        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
-#else
-    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
-
-    return success;
-}
-
-// Export data to code (.h), returns true on success
-bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
-{
-    bool success = false;
-
-#ifndef TEXT_BYTES_PER_LINE
-    #define TEXT_BYTES_PER_LINE     20
-#endif
-
-    // NOTE: Text data buffer size is estimated considering raw data size in bytes
-    // and requiring 6 char bytes for every byte: "0x00, "
-    char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
-
-    int byteCount = 0;
-    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes                  //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
-    byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2024 Ramon Santamaria (@raysan5)                                //\n");
-    byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
-    byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
-
-    // Get file name from path
-    char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
-    for (int i = 0; varFileName[i] != '\0'; i++)
-    {
-        // Convert variable name to uppercase
-        if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
-        // Replace non valid character for C identifier with '_'
-        else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; }
-    }
-
-    byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE     %i\n\n", varFileName, dataSize);
-
-    byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName);
-    for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]);
-    byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]);
-
-    // NOTE: Text data size exported is determined by '\0' (NULL) character
-    success = SaveFileText(fileName, txtData);
-
-    RL_FREE(txtData);
-
-    if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
-    else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
-
-    return success;
-}
-
-// Load text data from file, returns a '\0' terminated string
-// NOTE: text chars array should be freed manually
-char *LoadFileText(const char *fileName)
-{
-    char *text = NULL;
-
-    if (fileName != NULL)
-    {
-        if (loadFileText)
-        {
-            text = loadFileText(fileName);
-            return text;
-        }
-#if defined(SUPPORT_STANDARD_FILEIO)
-        FILE *file = fopen(fileName, "rt");
-
-        if (file != NULL)
-        {
-            // WARNING: When reading a file as 'text' file,
-            // text mode causes carriage return-linefeed translation...
-            // ...but using fseek() should return correct byte-offset
-            fseek(file, 0, SEEK_END);
-            unsigned int size = (unsigned int)ftell(file);
-            fseek(file, 0, SEEK_SET);
-
-            if (size > 0)
-            {
-                text = (char *)RL_MALLOC((size + 1)*sizeof(char));
-
-                if (text != NULL)
-                {
-                    unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
-
-                    // WARNING: \r\n is converted to \n on reading, so,
-                    // read bytes count gets reduced by the number of lines
-                    if (count < size) text = RL_REALLOC(text, count + 1);
-
-                    // Zero-terminate the string
-                    text[count] = '\0';
-
-                    TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
-                }
-                else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
-            }
-            else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
-
-            fclose(file);
-        }
-        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
-#else
-    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
-
-    return text;
-}
-
-// Unload file text data allocated by LoadFileText()
-void UnloadFileText(char *text)
-{
-    RL_FREE(text);
-}
-
-// Save text data to file (write), string must be '\0' terminated
-bool SaveFileText(const char *fileName, char *text)
-{
-    bool success = false;
-
-    if (fileName != NULL)
-    {
-        if (saveFileText)
-        {
-            return saveFileText(fileName, text);
-        }
-#if defined(SUPPORT_STANDARD_FILEIO)
-        FILE *file = fopen(fileName, "wt");
-
-        if (file != NULL)
-        {
-            int count = fprintf(file, "%s", text);
-
-            if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
-            else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
-
-            int result = fclose(file);
-            if (result == 0) success = true;
-        }
-        else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
-#else
-    TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
-#endif
-    }
-    else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
-
-    return success;
-}
-
-#if defined(PLATFORM_ANDROID)
-// Initialize asset manager from android app
-void InitAssetManager(AAssetManager *manager, const char *dataPath)
-{
-    assetManager = manager;
-    internalDataPath = dataPath;
-}
-
-// Replacement for fopen()
-// Ref: https://developer.android.com/ndk/reference/group/asset
-FILE *android_fopen(const char *fileName, const char *mode)
-{
-    if (mode[0] == 'w')
-    {
-        // fopen() is mapped to android_fopen() that only grants read access to
-        // assets directory through AAssetManager but we want to also be able to
-        // write data when required using the standard stdio FILE access functions
-        // Ref: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only
-        #undef fopen
-        return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
-        #define fopen(name, mode) android_fopen(name, mode)
-    }
-    else
-    {
-        // NOTE: AAsset provides access to read-only asset
-        AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN);
-
-        if (asset != NULL)
-        {
-            // Get pointer to file in the assets
-            return funopen(asset, android_read, android_write, android_seek, android_close);
-        }
-        else
-        {
-            #undef fopen
-            // Just do a regular open if file is not found in the assets
-            return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
-            #define fopen(name, mode) android_fopen(name, mode)
-        }
-    }
-}
-#endif  // PLATFORM_ANDROID
-
-//----------------------------------------------------------------------------------
-// Module specific Functions Definition
-//----------------------------------------------------------------------------------
-#if defined(PLATFORM_ANDROID)
-static int android_read(void *cookie, char *data, int dataSize)
-{
-    return AAsset_read((AAsset *)cookie, data, dataSize);
-}
-
-static int android_write(void *cookie, const char *data, int dataSize)
-{
-    TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");
-
-    return EACCES;
-}
-
-static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
-{
-    return AAsset_seek((AAsset *)cookie, offset, whence);
-}
-
-static int android_close(void *cookie)
-{
-    AAsset_close((AAsset *)cookie);
-    return 0;
-}
-#endif  // PLATFORM_ANDROID
diff --git a/raylib/src/utils.h b/raylib/src/utils.h
deleted file mode 100644
--- a/raylib/src/utils.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**********************************************************************************************
-*
-*   raylib.utils - Some common utility functions
-*
-*
-*   LICENSE: zlib/libpng
-*
-*   Copyright (c) 2014-2024 Ramon Santamaria (@raysan5)
-*
-*   This software is provided "as-is", without any express or implied warranty. In no event
-*   will the authors be held liable for any damages arising from the use of this software.
-*
-*   Permission is granted to anyone to use this software for any purpose, including commercial
-*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
-*
-*     1. The origin of this software must not be misrepresented; you must not claim that you
-*     wrote the original software. If you use this software in a product, an acknowledgment
-*     in the product documentation would be appreciated but is not required.
-*
-*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
-*     as being the original software.
-*
-*     3. This notice may not be removed or altered from any source distribution.
-*
-**********************************************************************************************/
-
-#ifndef UTILS_H
-#define UTILS_H
-
-#if defined(PLATFORM_ANDROID)
-    #include <stdio.h>                      // Required for: FILE
-    #include <android/asset_manager.h>      // Required for: AAssetManager
-#endif
-
-#if defined(SUPPORT_TRACELOG)
-    #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__)
-
-    #if defined(SUPPORT_TRACELOG_DEBUG)
-        #define TRACELOGD(...) TraceLog(LOG_DEBUG, __VA_ARGS__)
-    #else
-        #define TRACELOGD(...) (void)0
-    #endif
-#else
-    #define TRACELOG(level, ...) (void)0
-    #define TRACELOGD(...) (void)0
-#endif
-
-//----------------------------------------------------------------------------------
-// Some basic Defines
-//----------------------------------------------------------------------------------
-#if defined(PLATFORM_ANDROID)
-    #define fopen(name, mode) android_fopen(name, mode)
-#endif
-
-//----------------------------------------------------------------------------------
-// Types and Structures Definition
-//----------------------------------------------------------------------------------
-//...
-
-//----------------------------------------------------------------------------------
-// Global Variables Definition
-//----------------------------------------------------------------------------------
-// Nop...
-
-//----------------------------------------------------------------------------------
-// Module Functions Declaration
-//----------------------------------------------------------------------------------
-#if defined(__cplusplus)
-extern "C" {            // Prevents name mangling of functions
-#endif
-
-#if defined(PLATFORM_ANDROID)
-void InitAssetManager(AAssetManager *manager, const char *dataPath);   // Initialize asset manager from android app
-FILE *android_fopen(const char *fileName, const char *mode);           // Replacement for fopen() -> Read-only!
-#endif
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif // UTILS_H
diff --git a/raylib/tools/rexm/rexm.c b/raylib/tools/rexm/rexm.c
new file mode 100644
--- /dev/null
+++ b/raylib/tools/rexm/rexm.c
@@ -0,0 +1,2964 @@
+/*******************************************************************************************
+*
+*   rexm [raylib examples manager] - A simple and easy-to-use raylib examples collection manager
+*
+*   Supported processes:
+*    - create <new_example_name>
+*    - add <example_name>
+*    - rename <old_examples_name> <new_example_name>
+*    - remove <example_name>
+*    - build <example_name>
+*    - test <example_name>
+*    - validate                 // All examples
+*    - update                   // All examples
+*
+*   Files involved in the processes:
+*    - raylib/examples/<category>/<category>_example_name.c
+*    - raylib/examples/<category>/<category>_example_name.png
+*    - raylib/examples/<category>/resources/..
+*    - raylib/examples/Makefile
+*    - raylib/examples/Makefile.Web
+*    - raylib/examples/README.md
+*    - raylib/projects/VS2022/examples/<category>_example_name.vcxproj
+*    - raylib/projects/VS2022/raylib.sln
+*    - raylib.com/common/examples.js
+*    - raylib.com/examples/<category>/<category>_example_name.html
+*    - raylib.com/examples/<category>/<category>_example_name.data
+*    - raylib.com/examples/<category>/<category>_example_name.wasm
+*    - raylib.com/examples/<category>/<category>_example_name.js
+*    - ...
+*
+*   LICENSE: zlib/libpng
+*
+*   Copyright (c) 2025-2026 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#include "raylib.h"
+
+#include <stdio.h>      // Required for: rename(), remove()
+#include <string.h>     // Required for: strcmp(), strcpy()
+#include <stdlib.h>     // Required for: NULL, calloc(), free()
+
+#define SUPPORT_LOG_INFO
+#if defined(SUPPORT_LOG_INFO) //&& defined(_DEBUG)
+    #define LOG(...) printf("REXM: "__VA_ARGS__)
+#else
+    #define LOG(...)
+#endif
+
+#define REXM_MAX_EXAMPLES               512
+#define REXM_MAX_EXAMPLE_CATEGORIES     8
+
+#define REXM_MAX_BUFFER_SIZE            (2*1024*1024)      // 2MB
+
+#define REXM_MAX_RESOURCE_PATHS         256
+
+// Create local commit with changes on example renaming
+//#define RENAME_AUTO_COMMIT_CREATION
+
+#if defined(__GNUC__) // GCC and Clang
+    #pragma GCC diagnostic push
+    // Avoid GCC/Clang complaining about sprintf() second parameter not being a string literal (being TextFormat())
+    #pragma GCC diagnostic ignored "-Wformat-security"
+#endif
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+// raylib example info struct
+typedef struct {
+    char category[16];      // Example category: core, shapes, textures, text, models, shaders, audio, [others]
+    char name[128];         // Example name: <category>_name_part
+    int stars;              // Example stars count: ★☆☆☆
+    char verCreated[12];    // Example raylib creation version
+    char verUpdated[12];    // Example raylib last update version
+    int yearCreated;        // Example year created
+    int yearReviewed;       // Example year reviewed
+    char author[64];        // Example author
+    char authorGitHub[64];  // Example author, GitHub user name
+
+    int status;             // Example validation status flags
+    int resCount;           // Example resources counter
+    char **resPaths;        // Example resources paths (MAX: 256)
+} rlExampleInfo;
+
+// Automated testing data
+typedef struct {
+    int buildwarns;         // Example building warnings count (by GCC compiler)
+    int warnings;           // Example run output log warnings count
+    int status;             // Example run testing status flags (>0 = FAILS)
+} rlExampleTesting;
+
+// Validation status for a single example
+typedef enum {
+    VALID_OK                    = 0,        // All required files and entries are present
+    VALID_MISSING_C             = 1 << 0,   // Missing .c source file
+    VALID_MISSING_PNG           = 1 << 1,   // Missing screenshot .png
+    VALID_INVALID_PNG           = 1 << 2,   // Invalid screenshot .png (using template one)
+    VALID_MISSING_RESOURCES     = 1 << 3,   // Missing resources listed in the code
+    VALID_MISSING_VCXPROJ       = 1 << 4,   // Missing Visual Studio .vcxproj file
+    VALID_NOT_IN_VCXSOL         = 1 << 5,   // Project not included in solution file
+    VALID_NOT_IN_MAKEFILE       = 1 << 6,   // Not listed in Makefile
+    VALID_NOT_IN_MAKEFILE_WEB   = 1 << 7,   // Not listed in Makefile.Web
+    VALID_NOT_IN_README         = 1 << 8,   // Not listed in README.md
+    VALID_NOT_IN_JS             = 1 << 9,   // Not listed in examples.js
+    VALID_INCONSISTENT_INFO     = 1 << 10,  // Inconsistent info between collection and example header (stars, author...)
+    VALID_MISSING_WEB_OUTPUT    = 1 << 11,  // Missing .html/.data/.wasm/.js
+    VALID_MISSING_WEB_METADATA  = 1 << 12,  // Missing .html example metadata
+    VALID_INVALID_CATEGORY      = 1 << 13,  // Not a recognized category
+    VALID_UNKNOWN_ERROR         = 1 << 14   // Unknown failure case (fallback)
+} rlExampleValidationStatus;
+
+typedef enum {
+    TESTING_OK                  = 0,        // All automated testing ok
+    TESTING_FAIL_INIT           = 1 << 0,   // Initialization (InitWindow())    -> "INFO: DISPLAY: Device initialized successfully"
+    TESTING_FAIL_CLOSE          = 1 << 1,   // Closing (CloseWindow())          -> "INFO: Window closed successfully"
+    TESTING_FAIL_ASSETS         = 1 << 2,   // Assets loading (WARNING: FILE:)  -> "WARNING: FILEIO:"
+    TESTING_FAIL_RLGL           = 1 << 3,   // OpenGL-wrapped initialization    -> "INFO: RLGL: Default OpenGL state initialized successfully"
+    TESTING_FAIL_PLATFORM       = 1 << 4,   // Platform initialization          -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+    TESTING_FAIL_FONT           = 1 << 5,   // Font deefault initialization     -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+    TESTING_FAIL_TIMER          = 1 << 6,   // Timer initialization             -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+    TESTING_FAIL_OTHER          = 1 << 7,   // Other types of warnings (WARNING:)
+} rlExampleTestingStatus;
+
+// Example management operations
+typedef enum {
+    OP_NONE     = 0,        // No process to do
+    OP_CREATE   = 1,        // Create new example, using default template
+    OP_ADD      = 2,        // Add existing examples (hopefully following template)
+    OP_RENAME   = 3,        // Rename existing example
+    OP_REMOVE   = 4,        // Remove existing example
+    OP_VALIDATE = 5,        // Validate examples, using [examples_list.txt] as main source by default
+    OP_UPDATE   = 6,        // Validate and update required examples (as far as possible): ALL
+    OP_BUILD    = 7,        // Build example(s) for desktop and web, copy web output - Multiple examples supported
+    OP_TEST     = 8,        // Test example(s), checking output log "WARNING" - Multiple examples supported
+    OP_TESTLOG  = 9,        // Process available examples logs to generate report
+} rlExampleOperation;
+
+static const char *exCategories[REXM_MAX_EXAMPLE_CATEGORIES] = { "core", "shapes", "textures", "text", "models", "shaders", "audio", "others" };
+
+// Paths required for examples management
+// NOTE: Paths can be provided with environment variables
+static const char *exBasePath = NULL;           // Env: REXM_EXAMPLES_BASE_PATH
+static const char *exWebPath = NULL;            // Env: REXM_EXAMPLES_WEB_PATH
+static const char *exTemplateFilePath = NULL;   // Env: REXM_EXAMPLES_TEMPLATE_FILE_PATH
+static const char *exTemplateScreenshot = NULL; // Env: REXM_EXAMPLES_TEMPLATE_SCREENSHOT_PATH
+static const char *exCollectionFilePath = NULL; // Env: REXM_EXAMPLES_COLLECTION_FILE_PATH
+static const char *exVSProjectSolutionFile = NULL; // Env REXM_EXAMPLES_VS2022_SLN_FILE
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Declaration
+//----------------------------------------------------------------------------------
+// Update required files from examples collection
+// UPDATES: Makefile, Makefile.Web, README.md, examples.js
+static int UpdateRequiredFiles(void);
+
+// Load examples collection information
+// NOTE 1: Load by category: "ALL", "core", "shapes", "textures", "text", "models", "shaders", others"
+// NOTE 2: Sort examples list on request flag
+static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCount);
+static void UnloadExampleData(rlExampleInfo *exInfo);
+
+// Load example info from file header
+static rlExampleInfo *LoadExampleInfo(const char *exFileName);
+static void UnloadExampleInfo(rlExampleInfo *exInfo);
+
+// raylib example line info parser
+// Parses following line format: core/core_basic_window;★☆☆☆;1.0;1.0;"Ray"/@raysan5
+static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry);
+
+// Sort array of strings by name
+// WARNING: items[] pointers are reorganized
+static void SortExampleByName(rlExampleInfo *items, int count);
+
+// Scan resource paths in example file
+static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount);
+
+// Clear resource paths scanned
+static void UnloadExampleResourcePaths(char **resPaths);
+
+// Add/remove VS project (.vcxproj) tofrom existing VS solution (.sln)
+static int AddVSProjectToSolution(const char *slnFile, const char *projFile, const char *category);
+static int RemoveVSProjectFromSolution(const char *slnFile, const char *exName);
+
+// Generate unique UUID v4 string
+// Output format: {9A2F48CC-0DA8-47C0-884E-02E37F9BE6C1}
+// TODO: It seems generated UUID is not valid for VS2022
+static const char *GenerateUUIDv4(void);
+
+// Update source code header and comments metadata
+static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *info);
+// Update generated Web example .html file metadata
+static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath);
+
+// Check if text string is in a list of strings and get index, -1 if not found
+static int GetTextListIndex(const char *text, const char **list, int listCount);
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(int argc, char *argv[])
+{
+    SetTraceLogLevel(LOG_NONE);
+
+    // Path values can be configured with environment variables
+    exBasePath = getenv("REXM_EXAMPLES_BASE_PATH");
+    exWebPath = getenv("REXM_EXAMPLES_WEB_PATH");
+    exTemplateFilePath = getenv("REXM_EXAMPLES_TEMPLATE_FILE_PATH");
+    exTemplateScreenshot = getenv("REXM_EXAMPLES_TEMPLATE_SCREENSHOT_PATH");
+    exCollectionFilePath = getenv("REXM_EXAMPLES_COLLECTION_FILE_PATH");
+    exVSProjectSolutionFile = getenv("REXM_EXAMPLES_VS2022_SLN_FILE");
+
+#if defined(_WIN32)
+    if (!exBasePath) exBasePath = "C:/GitHub/raylib/examples";
+    if (!exWebPath) exWebPath = "C:/GitHub/raylib.com/examples";
+    if (!exTemplateFilePath) exTemplateFilePath = "C:/GitHub/raylib/examples/examples_template.c";
+    if (!exTemplateScreenshot) exTemplateScreenshot = "C:/GitHub/raylib/examples/examples_template.png";
+    if (!exCollectionFilePath) exCollectionFilePath = "C:/GitHub/raylib/examples/examples_list.txt";
+    if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "C:/GitHub/raylib/projects/VS2022/raylib.sln";
+#else
+    // Cross-platform relative fallbacks (run from tools/rexm directory)
+    if (!exBasePath) exBasePath = "../../examples";
+    if (!exWebPath) exWebPath = "../../raylib.com/examples";
+    if (!exTemplateFilePath) exTemplateFilePath = "../../examples/examples_template.c";
+    if (!exTemplateScreenshot) exTemplateScreenshot = "../../examples/examples_template.png";
+    if (!exCollectionFilePath) exCollectionFilePath = "../../examples/examples_list.txt";
+    if (!exVSProjectSolutionFile) exVSProjectSolutionFile = "../../projects/VS2022/raylib.sln";
+#endif
+
+    char inFileName[1024] = { 0 };  // Example input filename (to be added)
+
+    char exName[64] = { 0 };        // Example name, without extension: core_basic_window
+    char exCategory[32] = { 0 };    // Example category: core
+    char exRecategory[32] = { 0 };  // Example re-name category: shapes
+    char exRename[64] = { 0 };      // Example re-name, without extension
+
+    char *exBuildList[256] = { 0 }; // Example build list for: ALL, <category>, single-example
+    int exBuildListCount = 0;       // Example build list file count
+
+    int opCode = OP_NONE;           // Operation code: 0-None(Help), 1-Create, 2-Add, 3-Rename, 4-Remove
+    bool showUsage = false;         // Flag to show usage help
+    bool verbose = false;           // Flag for verbose log info
+
+    // Command-line usage mode: command args processing
+    //--------------------------------------------------------------------------------------
+    if (argc > 1)
+    {
+        // Supported commands:
+        //    create <new_example_name>     : Creates an empty example, from internal template
+        //    add <example_name>            : Add existing example, category extracted from name
+        //    rename <old_examples_name> <new_example_name> : Rename an existing example
+        //    remove <example_name>         : Remove an existing example
+        //    build <example_name>          : Build example for Desktop and Web platforms
+        //    validate                      : Validate examples collection, generates report
+        //    update                        : Validate and update examples collection, generates report
+
+        if (strcmp(argv[1], "create") == 0)
+        {
+            // Check for valid upcoming argument
+            if (argc == 2) LOG("WARNING: No filename provided to create\n");
+            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                // Security checks for file name to verify category is included
+                int catIndex = TextFindIndex(argv[2], "_");
+                if (catIndex > 3)
+                {
+                    char cat[12] = { 0 };
+                    strncpy(cat, argv[2], catIndex);
+                    bool catFound = false;
+                    for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
+                    {
+                        if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; }
+                    }
+
+                    if (catFound)
+                    {
+                        strcpy(exName, argv[2]); // Register filename for new example creation
+                        strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+                        opCode = OP_CREATE;
+                    }
+                    else LOG("WARNING: Example category is not valid\n");
+                }
+                else LOG("WARNING: Example name does not include category\n");
+            }
+        }
+        else if (strcmp(argv[1], "add") == 0)
+        {
+            // Check for valid upcoming argument
+            if (argc == 2) LOG("WARNING: No filename provided to create\n");
+            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                if (IsFileExtension(argv[2], ".c")) // Check for valid file extension: input
+                {
+                    if (FileExists(argv[2]))
+                    {
+                        // Security checks for file name to verify category is included
+                        int catIndex = TextFindIndex(GetFileName(argv[2]), "_");
+                        if (catIndex > 3)
+                        {
+                            char cat[12] = { 0 };
+                            strncpy(cat, GetFileName(argv[2]), catIndex);
+                            bool catFound = false;
+                            for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
+                            {
+                                if (TextIsEqual(cat, exCategories[i])) { catFound = true; break; }
+                            }
+
+                            if (catFound)
+                            {
+                                strcpy(inFileName, argv[2]); // Register filename for addition
+                                strcpy(exName, GetFileNameWithoutExt(inFileName)); // Register example name
+                                strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+                                opCode = OP_ADD;
+                            }
+                            else LOG("WARNING: Example category is not valid\n");
+                        }
+                        else LOG("WARNING: Example name does not include category\n");
+                    }
+                    else LOG("WARNING: Input file not found, include path\n");
+                }
+                else LOG("WARNING: Input file extension not recognized (.c)\n");
+            }
+        }
+        else if (strcmp(argv[1], "rename") == 0)
+        {
+            if (argc == 2) LOG("WARNING: No filename provided to be renamed\n");
+            else if (argc == 3) LOG("WARNING: No new filename provided to be renamed\n");
+            else if (argc > 4) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                // Verify example exists in collection to be removed
+                char *exColInfo = LoadFileText(exCollectionFilePath);
+                if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
+                {
+                    // Security checks for new file name to verify category is included
+                    int newCatIndex = TextFindIndex(argv[3], "_");
+                    if (newCatIndex > 3)
+                    {
+                        char cat[12] = { 0 };
+                        strncpy(cat, argv[3], newCatIndex);
+                        bool newCatFound = false;
+                        for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
+                        {
+                            if (TextIsEqual(cat, exCategories[i])) { newCatFound = true; break; }
+                        }
+
+                        if (newCatFound)
+                        {
+                            strcpy(exName, argv[2]);    // Register example name
+                            strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+                            strcpy(exRename, argv[3]);
+                            strncpy(exRecategory, exRename, TextFindIndex(exRename, "_"));
+                            opCode = OP_RENAME;
+                        }
+                        else LOG("WARNING: Example new category is not valid\n");
+                    }
+                    else LOG("WARNING: Example new name does not include category\n");
+                }
+                else LOG("WARNING: RENAME: Example not available in the collection\n");
+                UnloadFileText(exColInfo);
+            }
+        }
+        else if (strcmp(argv[1], "remove") == 0)
+        {
+            // Check for valid upcoming argument
+            if (argc == 2) LOG("WARNING: No filename provided to create\n");
+            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                // Verify example exists in collection to be removed
+                char *exColInfo = LoadFileText(exCollectionFilePath);
+                if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection
+                {
+                    strcpy(exName, argv[2]); // Register filename for removal
+                    strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+                    opCode = OP_REMOVE;
+                }
+                else LOG("WARNING: REMOVE: Example not available in the collection\n");
+                UnloadFileText(exColInfo);
+            }
+        }
+        else if (strcmp(argv[1], "validate") == 0)
+        {
+            // Validate examples in collection (report results)
+            // All examples in collection match all files requirements
+
+            opCode = OP_VALIDATE;
+        }
+        else if (strcmp(argv[1], "update") == 0)
+        {
+            // Validate and update examples in collection
+            // All examples in collection match all files requirements
+
+            opCode = OP_UPDATE;
+        }
+        else if ((strcmp(argv[1], "build") == 0) || (strcmp(argv[1], "test") == 0) || (strcmp(argv[1], "testlog") == 0))
+        {
+            // Build/Test example(s) for PLATFORM_DESKTOP and PLATFORM_WEB
+            // NOTE: Build outputs to default directory, usually where the .c file is located,
+            // to avoid issues with copying resources (at least on Desktop)
+            // Web build files (.html, .wasm, .js, .data) are copied to raylib.com/examples repo
+            // Check for valid upcoming argument
+            if (argc == 2) LOG("WARNING: No example name/category provided\n");
+            else if (argc > 3) LOG("WARNING: Too many arguments provided\n");
+            else
+            {
+                // Support building/testing not only individual examples but multiple: ALL/<category>
+                int exBuildListInfoCount = 0;
+                rlExampleInfo *exBuildListInfo = LoadExampleData(argv[2], false, &exBuildListInfoCount);
+
+                for (int i = 0; i < exBuildListInfoCount; i++)
+                {
+                    if (!TextIsEqual(exBuildListInfo[i].category, "others"))
+                    {
+                        exBuildList[exBuildListCount] = (char *)RL_CALLOC(256, sizeof(char));
+                        strcpy(exBuildList[exBuildListCount], exBuildListInfo[i].name);
+                        exBuildListCount++;
+                    }
+                }
+
+                UnloadExampleData(exBuildListInfo);
+
+                if (exBuildListCount == 0) LOG("WARNING: BUILD: Example requested not available in the collection\n");
+                else
+                {
+                    if (strcmp(argv[1], "build") == 0) opCode = OP_BUILD;
+                    else if (strcmp(argv[1], "test") == 0) opCode = OP_TEST;
+                    else if (strcmp(argv[1], "testlog") == 0) opCode = OP_TESTLOG;
+                }
+            }
+        }
+
+        // Process command line options arguments
+        for (int i = 1; i < argc; i++)
+        {
+            if ((strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "--help") == 0)) showUsage = true;
+            else if ((strcmp(argv[i], "-v") == 0) || (strcmp(argv[i], "--verbose") == 0)) verbose = true;
+        }
+    }
+
+    // Command-line usage mode: command execution
+    switch (opCode)
+    {
+        case OP_CREATE:     // Create: New example from template
+        {
+            LOG("INFO: Command requested: CREATE\n");
+            LOG("INFO: Example to create: %s\n", exName);
+
+            // Create: raylib/examples/<category>/<category>_example_name.c
+            char *exText = LoadFileText(exTemplateFilePath);
+            char *exTextUpdated[6] = { 0 };
+            int exIndex = TextFindIndex(exText, "/****************");
+
+            // Update required info with some defaults
+            exTextUpdated[0] = TextReplace(exText + exIndex, "<module>", exCategory);
+            exTextUpdated[1] = TextReplace(exTextUpdated[0], "<name>", exName + strlen(exCategory) + 1);
+            //TextReplace(newExample, "<user_name>", "Ray");
+            //TextReplace(newExample, "@<user_github>", "@raysan5");
+            //TextReplace(newExample, "<year_created>", 2025);
+            //TextReplace(newExample, "<year_updated>", 2025);
+
+            SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), exTextUpdated[1]);
+            for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; }
+            UnloadFileText(exText);
+        }
+        case OP_ADD:     // Add: Example from command-line input filename
+        {
+            if (opCode != OP_CREATE) LOG("INFO: Command requested: ADD\n");
+            LOG("INFO: Example file to be added: %s\n", inFileName);
+
+            // Add: raylib/examples/<category>/<category>_example_name.c
+            if (opCode != OP_CREATE) FileCopy(inFileName, TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+            // Create: raylib/examples/<category>/<category>_example_name.png
+            if (FileExists(TextFormat("%s/%s.png", GetDirectoryPath(inFileName), exName)))
+            {
+                LOG("INFO: [%s] Copying file screenshot...\n", GetFileName(inFileName));
+                FileCopy(TextFormat("%s/%s.png", GetDirectoryPath(inFileName), exName),
+                    TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
+            }
+            else // No screenshot available next to source file
+            {
+                // Copy screenshot template
+                LOG("WARNING: [%s] No screenshot found, using placeholder screenshot\n", GetFileName(inFileName));
+                FileCopy(exTemplateScreenshot, TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
+            }
+
+            // Copy: raylib/examples/<category>/resources/...
+            // -----------------------------------------------------------------------------------------
+            // Scan resources used in example to copy
+            // NOTE: resources path will be relative to example source file directory
+            int resPathCount = 0;
+            LOG("INFO: [%s] Scanning file for resources...\n", GetFileName(inFileName));
+            char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s.c", GetDirectoryPath(inFileName), exName), &resPathCount);
+
+            if (resPathCount > 0)
+            {
+                LOG("INFO: [%s] Required resources found: %i\n", GetFileName(inFileName), resPathCount);
+
+                for (int r = 0; r < resPathCount; r++)
+                {
+                    // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
+                    // In this case, multiple resources are required: glsl100, glsl120, glsl330
+                    if (TextFindIndex(resPaths[r], "glsl%i") > -1)
+                    {
+                        int glslVer[3] = { 100, 120, 330 };
+
+                        for (int v = 0; v < 3; v++)
+                        {
+                            char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
+
+                            LOG("INFO: [%s] Resource required [%i/%i]: %s\n", GetFileName(inFileName), r, resPathCount, resPathUpdated);
+
+                            if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated)))
+                            {
+                                // Verify the resources are placed in "resources" directory
+                                if (TextFindIndex(resPathUpdated, "resources/") > 0)
+                                {
+                                    // NOTE: Look for resources in the path of the provided .c to be added
+                                    // To be copied to <category>/resources directory, extra dirs are automatically created if required
+                                    FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated),
+                                        TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
+                                }
+                                else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n");
+                            }
+                            else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPathUpdated));
+
+                            RL_FREE(resPathUpdated);
+                        }
+                    }
+                    else
+                    {
+                        LOG("INFO: [%s] Resource required [%i/%i]: %s\n", GetFileName(inFileName), r, resPathCount, resPaths[r]);
+
+                        if (FileExists(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r])))
+                        {
+                            // Verify the resources are placed in "resources" directory
+                            if (TextFindIndex(resPaths[r], "resources/") > 0)
+                            {
+                                // NOTE: Look for resources in the path of the provided .c to be added
+                                // To be copied to <category>/resources directory, extra dirs are automatically created if required
+                                FileCopy(TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]),
+                                    TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
+                            }
+                            else LOG("WARNING: Example resource must be placed in 'resources' directory next to .c file\n");
+                        }
+                        else LOG("WARNING: Example resource can not be found in: %s\n", TextFormat("%s/%s", GetDirectoryPath(inFileName), resPaths[r]));
+                    }
+                }
+            }
+
+            UnloadExampleResourcePaths(resPaths);
+            // -----------------------------------------------------------------------------------------
+
+            // Add example to the collection list, if not already there
+            // NOTE: Required format: shapes;shapes_basic_shapes;★☆☆☆;1.0;4.2;2014;2026;"Ray";@raysan5
+            //------------------------------------------------------------------------------------------------
+            char *exCollectionList = LoadFileText(exCollectionFilePath);
+            if (TextFindIndex(exCollectionList, exName) == -1) // Example not found
+            {
+                LOG("INFO: [%s] Adding example to collection list (%s)\n", GetFileName(inFileName), exCategory);
+
+                char *exCollectionListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated list copy, 2MB
+
+                // Add example to the main list, by category
+                // by default add it last in the category list
+                // NOTE: When populating to other files, lists are sorted by name
+                int nextCategoryIndex = 0;
+                if (TextIsEqual(exCategory, "core")) nextCategoryIndex = 1;
+                else if (TextIsEqual(exCategory, "shapes")) nextCategoryIndex = 2;
+                else if (TextIsEqual(exCategory, "textures")) nextCategoryIndex = 3;
+                else if (TextIsEqual(exCategory, "text")) nextCategoryIndex = 4;
+                else if (TextIsEqual(exCategory, "models")) nextCategoryIndex = 5;
+                else if (TextIsEqual(exCategory, "shaders")) nextCategoryIndex = 6;
+                else if (TextIsEqual(exCategory, "audio")) nextCategoryIndex = 7;
+                else if (TextIsEqual(exCategory, "others")) nextCategoryIndex = -1; // Add to EOF
+
+                // Get required example info from example file header (if provided)
+
+                // NOTE: Load example info from provided example header
+                rlExampleInfo *exInfo = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+                LOG("INFO: [%s] Example info: \n", GetFileName(inFileName));
+                LOG("      > Author: %s (@%s)\n", exInfo->author, exInfo->authorGitHub);
+                LOG("      > Stars: %i\n", exInfo->stars);
+                LOG("      > Version-Update: %s-%s\n", exInfo->verCreated, exInfo->verUpdated);
+                LOG("      > Created-Reviewed: %i-%i\n", exInfo->yearCreated, exInfo->yearReviewed);
+
+                // Get example difficulty stars text
+                char starsText[16] = { 0 };
+                for (int i = 0; i < 4; i++)
+                {
+                    // NOTE: Every UTF-8 star are 3 bytes
+                    if (i < exInfo->stars) strcpy(starsText + 3*i, "★");
+                    else strcpy(starsText + 3*i, "☆");
+                }
+
+                if (nextCategoryIndex == -1)
+                {
+                    // Add example to collection at the EOF
+                    int endIndex = (int)strlen(exCollectionList);
+                    memcpy(exCollectionListUpdated, exCollectionList, endIndex);
+                    sprintf(exCollectionListUpdated + endIndex, TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n",
+                        exInfo->category, exInfo->name, starsText, exInfo->verCreated, exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed, exInfo->author, exInfo->authorGitHub));
+                }
+                else
+                {
+                    // Add example to collection, at the end of the category list
+                    int categoryIndex = TextFindIndex(exCollectionList, exCategories[nextCategoryIndex]);
+                    memcpy(exCollectionListUpdated, exCollectionList, categoryIndex);
+                    int textWritenSize = sprintf(exCollectionListUpdated + categoryIndex, TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n",
+                        exInfo->category, exInfo->name, starsText, exInfo->verCreated, exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed, exInfo->author, exInfo->authorGitHub));
+                    memcpy(exCollectionListUpdated + categoryIndex + textWritenSize, exCollectionList + categoryIndex, strlen(exCollectionList) - categoryIndex);
+                }
+
+                UnloadExampleInfo(exInfo);
+
+                SaveFileText(exCollectionFilePath, exCollectionListUpdated);
+                RL_FREE(exCollectionListUpdated);
+            }
+            else LOG("WARNING: ADD: Example is already on the collection\n");
+            UnloadFileText(exCollectionList);
+            //------------------------------------------------------------------------------------------------
+
+            // Update: Metadata, Makefile, Makefile.Web, README.md, examples.js
+            //------------------------------------------------------------------------------------------------
+            UpdateRequiredFiles();
+            //------------------------------------------------------------------------------------------------
+
+            // Create: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
+            //------------------------------------------------------------------------------------------------
+            LOG("INFO: [%s] Creating example project\n", TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName));
+            // WARNING: When adding new project a unique UUID should be assigned!
+            FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath),
+                TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName));
+            FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName),
+                "core_basic_window", exName);
+            FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName),
+                "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exCategory));
+
+            // Edit: raylib/projects/VS2022/raylib.sln --> Add new example project
+            // WARNING: This function uses TextFormat() extensively inside,
+            // we must store provided file paths because pointers will be overwriten
+            // TODO: It seems projects are added to solution BUT not to required solution folder,
+            // that process still requires to be done manually
+            LOG("INFO: [%s] Adding project to raylib solution (.sln)\n", 
+                TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName));
+            AddVSProjectToSolution(exVSProjectSolutionFile,
+                TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), exCategory);
+            //------------------------------------------------------------------------------------------------
+
+            // Recompile example (on raylib side)
+            // NOTE: Tools requirements: emscripten, w64devkit
+            // Compile to: raylib.com/examples/<category>/<category>_example_name.html
+            // Compile to: raylib.com/examples/<category>/<category>_example_name.data
+            // Compile to: raylib.com/examples/<category>/<category>_example_name.wasm
+            // Compile to: raylib.com/examples/<category>/<category>_example_name.js
+            //------------------------------------------------------------------------------------------------
+            // WARNING 1: EMSDK_PATH must be set to proper location when calling from GitHub Actions
+            // WARNING 2: raylib.a and raylib.web.a must be available when compiling locally
+#if defined(_WIN32)
+            LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", GetFileNameWithoutExt(inFileName));
+            //putenv("RAYLIB_DIR=C:\\GitHub\\raylib");
+            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+            system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+#else
+            LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", GetFileNameWithoutExt(inFileName));
+            system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+#endif
+            // Update generated .html metadata
+            LOG("INFO: [%s] Updating HTML Metadata...\n", TextFormat("%s.html", exName));
+            UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+                TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+            // Copy results to web side
+            LOG("INFO: [%s] Copy example build to raylib.com\n", exName);
+            FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+                TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName));
+            FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName),
+                TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName));
+            FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName),
+                TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName));
+            FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName),
+                TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
+            //------------------------------------------------------------------------------------------------
+
+        } break;
+        case OP_RENAME:     // Rename
+        {
+            LOG("INFO: Command requested: RENAME\n");
+            LOG("INFO: Example to be renamed: %s --> %s\n", exName, exRename);
+
+            // NOTE: At this point provided values have been validated:
+            // exName, exCategory, exRename, exRecategory
+            if (strcmp(exCategory, exRecategory) == 0)
+            {
+                // Rename example on collection
+                FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName),
+                    TextFormat("%s;%s", exRecategory, exRename));
+
+                // Edit: Rename example code and screenshot files .c and .png
+                FileRename(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename));
+                FileRename(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename));
+
+                // Edit: Update example source code metadata
+                int exListCount = 0;
+                rlExampleInfo *exList = LoadExampleData(exCategory, false, &exListCount);
+                for (int i = 0; i < exListCount; i++)
+                {
+                    if (strcmp(exList[i].name, exRename) == 0)
+                        UpdateSourceMetadata(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename), &exList[i]);
+                }
+                UnloadExampleData(exList);
+
+                // NOTE: Example resource files do not need to be changed...
+                // unless the example is moved from one caegory to another
+
+                // Edit: Rename example on required files
+                FileTextReplace(TextFormat("%s/Makefile", exBasePath), exName, exRename);
+                FileTextReplace(TextFormat("%s/Makefile.Web", exBasePath), exName, exRename);
+                FileTextReplace(TextFormat("%s/README.md", exBasePath), exName, exRename);
+                FileTextReplace(TextFormat("%s/../common/examples.js", exWebPath),
+                    exName + strlen(exCategory) + 1, exRename + strlen(exRecategory) + 1); // Skip category
+
+                // Edit: Rename example project and solution
+                FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName), exName, exRename);
+                FileRename(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName),
+                    TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exRename));
+                FileTextReplace(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName, exRename);
+            }
+            else
+            {
+                // WARNING: Rename with change of category
+                // TODO: Reorder collection to place renamed example at the end of category
+                FileTextReplace(exCollectionFilePath, TextFormat("%s;%s", exCategory, exName),
+                    TextFormat("%s;%s", exRecategory, exRename));
+
+                // TODO: Move example resources from <exCategory>/resources to <exRecategory>/resources
+                // WARNING: Resources can be shared with other examples in the category
+
+                // Edit: Rename example code file (copy and remove)
+                FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename));
+                FileRemove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+                // Edit: Rename example screenshot file (copy and remove)
+                FileCopy(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.png", exBasePath, exCategory, exRename));
+                FileRemove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
+
+                // Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js
+                UpdateRequiredFiles();
+            }
+
+            // Remove old web compilation
+            FileRemove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
+
+            // Recompile example (on raylib side)
+            // WARNING: EMSDK_PATH must be set to proper location when calling from GitHub Actions
+#if defined(_WIN32)
+            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+            system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exRecategory, exRename));
+#else
+            system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exRecategory, exRename));
+#endif
+            // Update generated .html metadata
+            UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exRename),
+                TextFormat("%s/%s/%s.c", exBasePath, exCategory, exRename));
+
+            // Copy results to web side
+            FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exRecategory, exRename),
+                TextFormat("%s/%s/%s.html", exWebPath, exRecategory, exRename));
+            FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exRecategory, exRename),
+                TextFormat("%s/%s/%s.data", exWebPath, exRecategory, exRename));
+            FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exRecategory, exRename),
+                TextFormat("%s/%s/%s.wasm", exWebPath, exRecategory, exRename));
+            FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exRecategory, exRename),
+                TextFormat("%s/%s/%s.js", exWebPath, exRecategory, exRename));
+
+#if defined(RENAME_AUTO_COMMIT_CREATION)
+            // Create GitHub commit with changes (local)
+            putenv("PATH=%PATH%;C:\\Program Files\\Git\\bin");
+            ChangeDirectory("C:\\GitHub\\raylib");
+            system("git --version");
+            system("git status");
+            system("git add -A");
+            int result = system(TextFormat("git commit -m \"REXM: RENAME: example: `%s` --> `%s`\"", exName, exRename)); // Commit changes (only tracked files)
+            if (result != 0) LOG("WARNING: Error committing changes\n");
+            ChangeDirectory("C:/GitHub/raylib.com");
+            system("git add -A");
+            result = system(TextFormat("git commit -m \"REXM: RENAME: example: `%s` --> `%s`\"", exName, exRename)); // Commit changes (only tracked files)
+            if (result != 0) LOG("WARNING: Error committing changes\n");
+            //result = system("git push"); // Push to the remote (origin, current branch)
+            //if (result != 0) LOG("WARNING: Error pushing changes\n");
+#endif
+
+        } break;
+        case OP_REMOVE:     // Remove
+        {
+            LOG("INFO: Command requested: REMOVE\n");
+            LOG("INFO: Example to be removed: %s\n", exName);
+
+            // Remove example from collection for files update
+            //------------------------------------------------------------------------------------------------
+            LOG("INFO: [%s] Removing example from collection\n", exName);
+            char *exCollectionList = LoadFileText(exCollectionFilePath);
+            int exIndex = TextFindIndex(exCollectionList, TextFormat("%s;%s", exCategory, exName));
+            if (exIndex > 0) // Example found
+            {
+                char *exCollectionListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated list copy, 2MB
+
+                memcpy(exCollectionListUpdated, exCollectionList, exIndex);
+                int lineLen = 0;
+                for (int i = exIndex; (exCollectionList[i] != '\n') && (exCollectionList[i] != '\0'); i++) lineLen++;
+                // Remove line and copy the rest next
+                memcpy(exCollectionListUpdated + exIndex, exCollectionList + exIndex + lineLen + 1, strlen(exCollectionList) - exIndex - lineLen);
+
+                SaveFileText(exCollectionFilePath, exCollectionListUpdated);
+                RL_FREE(exCollectionListUpdated);
+            }
+            else LOG("WARNING: REMOVE: Example not found in the collection\n");
+            UnloadFileText(exCollectionList);
+            //------------------------------------------------------------------------------------------------
+
+            // Remove: raylib/examples/<category>/resources/..
+            // WARNING: Some of those resources could be used by other examples,
+            // just leave this process to manual update for now!
+            // -----------------------------------------------------------------------------------------
+            /*
+            // Scan resources used in example to be removed
+            int resPathCount = 0;
+            char **resPaths = ScanExampleResources(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), &resPathCount);
+
+            if (resPathCount > 0)
+            {
+                for (int r = 0; r < resPathCount; r++)
+                {
+                    // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
+                    // In this case, multiple resources are required: glsl100, glsl120, glsl330
+                    if (TextFindIndex(resPaths[r], "glsl%i") > -1)
+                    {
+                        int glslVer[3] = { 100, 120, 330 };
+
+                        for (int v = 0; v < 3; v++)
+                        {
+                            char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
+                            FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPathUpdated));
+                            RL_FREE(resPathUpdated);
+                        }
+                    }
+                    else FileRemove(TextFormat("%s/%s/%s", exBasePath, exCategory, resPaths[r]));
+                }
+            }
+
+            ClearExampleResources(resPaths);
+            */
+            // -----------------------------------------------------------------------------------------
+
+            // Remove: raylib/examples/<category>/<category>_example_name.c
+            // Remove: raylib/examples/<category>/<category>_example_name.png
+            LOG("INFO: [%s] Removing example code file\n", TextFormat("%s.c", exName));
+            FileRemove(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+            LOG("INFO: [%s] Removing example screenshot file\n", TextFormat("%s.png", exName));
+            FileRemove(TextFormat("%s/%s/%s.png", exBasePath, exCategory, exName));
+
+            // Edit: Update required files: Makefile, Makefile.Web, README.md, examples.js
+            UpdateRequiredFiles();
+
+            // Remove: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
+            LOG("INFO: [%s] Removing example project file\n", TextFormat("%s.vcxproj", exName));
+            FileRemove(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exName));
+
+            // Edit: raylib/projects/VS2022/raylib.sln --> Remove example project
+            LOG("INFO: [%s] Removing example from raylib solution (.sln)\n", exName);
+            RemoveVSProjectFromSolution(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exName);
+
+            // Remove: Delete example build from local copy (if exists)
+            FileRemove(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName));
+
+            // Remove: raylib.com/examples/<category>/<category>_example_name.html
+            // Remove: raylib.com/examples/<category>/<category>_example_name.data
+            // Remove: raylib.com/examples/<category>/<category>_example_name.wasm
+            // Remove: raylib.com/examples/<category>/<category>_example_name.js
+            LOG("INFO: [%s] Deleting example from raylib.com\n", exName);
+            FileRemove(TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName));
+            FileRemove(TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
+
+        } break;
+        case OP_BUILD:
+        {
+            LOG("INFO: Command requested: BUILD\n");
+            LOG("INFO: Example(s) to be built: %i [%s]\n", exBuildListCount, (exBuildListCount == 1)? exBuildList[0] : argv[2]);
+
+#if defined(_WIN32)
+            // Set required environment variables
+            //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+            //putenv("MAKE=mingw32-make");
+            //ChangeDirectory(exBasePath);
+#endif
+            for (int i = 0; i < exBuildListCount; i++)
+            {
+                // Get example name and category
+                memset(exName, 0, 64);
+                strcpy(exName, exBuildList[i]);
+                memset(exCategory, 0, 32);
+                strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+
+                LOG("INFO: [%i/%i] Building example: [%s]\n", i + 1, exBuildListCount, exName);
+
+                // Build example for PLATFORM_DESKTOP
+#if defined(_WIN32)
+                LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
+                system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#elif defined(PLATFORM_DRM)
+                LOG("INFO: [%s] Building example for PLATFORM_DRM (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DRM -B > %s/%s/logs/%s.build.log 2>&1",
+                    exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+#else
+                LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B", exBasePath, exCategory, exName));
+#endif
+
+#if !defined(PLATFORM_DRM)
+                // Build example for PLATFORM_WEB
+                // Build: raylib.com/examples/<category>/<category>_example_name.html
+                // Build: raylib.com/examples/<category>/<category>_example_name.data
+                // Build: raylib.com/examples/<category>/<category>_example_name.wasm
+                // Build: raylib.com/examples/<category>/<category>_example_name.js
+    #if defined(_WIN32)
+                LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exName);
+                system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+    #else
+                LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+    #endif
+                // Update generated .html metadata
+                LOG("INFO: [%s] Updating HTML Metadata...\n", TextFormat("%s.html", exName));
+                UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+                // Copy results to web side
+                LOG("INFO: [%s] Copy example build to raylib.com\n", exName);
+                FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.html", exWebPath, exCategory, exName));
+                FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.data", exWebPath, exCategory, exName));
+                FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.wasm", exWebPath, exCategory, exName));
+                FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.js", exWebPath, exCategory, exName));
+#endif // !PLATFORM_DRM
+
+                // Once example processed, free memory from list
+                RL_FREE(exBuildList[i]);
+            }
+
+        } break;
+        case OP_VALIDATE:     // Validate: report and actions
+        case OP_UPDATE:
+        {
+            LOG("INFO: Command requested: %s\n", (opCode == OP_VALIDATE)? "VALIDATE" : "UPDATE");
+            LOG("INFO: Example collection is being %s\n", (opCode == OP_VALIDATE)? "validated" : "validated and updated");
+            /*
+            // Validation flags available:
+            VALID_MISSING_C
+            VALID_MISSING_PNG
+            VALID_INVALID_PNG
+            VALID_MISSING_RESOURCES
+            VALID_MISSING_VCXPROJ
+            VALID_NOT_IN_VCXSOL
+            VALID_NOT_IN_MAKEFILE
+            VALID_NOT_IN_MAKEFILE_WEB
+            VALID_NOT_IN_README
+            VALID_NOT_IN_JS
+            VALID_INCONSISTENT_INFO
+            VALID_MISSING_WEB_OUTPUT
+            VALID_INVALID_CATEGORY
+            */
+
+            // Validate and update examples collection list
+            // NOTE: New .c examples found are added at the end of its category
+            //---------------------------------------------------------------------------------------------------
+            // Scan available example .c files and add to collection missing ones
+            // NOTE: Source of truth is what we have in the examples directories (on validation/update)
+            LOG("INFO: Scanning available example (.c) files to be added to collection...\n");
+            FilePathList clist = LoadDirectoryFilesEx(exBasePath, ".c", true);
+
+            // Load examples collection list file (raylib/examples/examples_list.txt)
+            char *exList = LoadFileText(exCollectionFilePath);
+            int exListLen = (int)strlen(exList);
+
+            char *exListUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+            bool listUpdated = false;
+
+            // Add new examples to the collection list if not found
+            // WARNING: Added to the end of category, order defines place on raylib webpage
+            for (unsigned int i = 0; i < clist.count; i++)
+            {
+                // NOTE: Skipping "examples_template" from checks
+                if (!TextIsEqual(GetFileNameWithoutExt(clist.paths[i]), "examples_template") &&
+                    (TextFindIndex(exList, GetFileNameWithoutExt(clist.paths[i])) == -1))
+                {
+                    // Get new example data
+                    rlExampleInfo *exInfo = LoadExampleInfo(clist.paths[i]);
+
+                    // Get example category, -1 if not found in list
+                    int catIndex = GetTextListIndex(exInfo->category, exCategories, REXM_MAX_EXAMPLE_CATEGORIES);
+
+                    if (catIndex > -1)
+                    {
+                        int nextCatIndex = catIndex + 1;
+                        if (nextCatIndex > (REXM_MAX_EXAMPLE_CATEGORIES - 1)) nextCatIndex = -1; // EOF
+
+                        // Find position to add new example on list, just before the following category
+                        // Category order: core, shapes, textures, text, models, shaders, audio, [others]
+                        int exListNextCatIndex = -1;
+                        if (nextCatIndex != -1) exListNextCatIndex = TextFindIndex(exList, exCategories[nextCatIndex]);
+                        else exListNextCatIndex = exListLen; // EOF
+
+                        strncpy(exListUpdated, exList, exListNextCatIndex);
+
+                        // Get example difficulty stars
+                        char starsText[16] = { 0 };
+                        for (int s = 0; s < 4; s++)
+                        {
+                            // NOTE: Every UTF-8 star are 3 bytes
+                            if (s < exInfo->stars) strcpy(starsText + 3*s, "★");
+                            else strcpy(starsText + 3*s, "☆");
+                        }
+
+                        // Add new example to the list
+                        int exListNewExLen = sprintf(exListUpdated + exListNextCatIndex,
+                            TextFormat("%s;%s;%s;%s;%s;%i;%i;\"%s\";@%s\n",
+                                exInfo->category, exInfo->name, starsText, exInfo->verCreated,
+                                exInfo->verUpdated, exInfo->yearCreated, exInfo->yearReviewed,
+                                exInfo->author, exInfo->authorGitHub));
+
+                        // Add the following examples to the end of collection list
+                        strncpy(exListUpdated + exListNextCatIndex + exListNewExLen, exList + exListNextCatIndex, exListLen - exListNextCatIndex);
+
+                        listUpdated = true;
+                    }
+
+                    UnloadExampleInfo(exInfo);
+                }
+            }
+
+            /*
+            // Check and remove duplicate example entries
+            int lineCount = 0;
+            char **exListLines = LoadTextLines(exList, &lineCount);
+            int exListUpdatedOffset = 0;
+            exListUpdatedOffset = sprintf(exListUpdated, "%s\n", exListLines[0]);
+
+            for (int i = 1; i < lineCount; i++)
+            {
+                if ((TextFindIndex(exListUpdated, exListLines[i]) == -1) || (exListLines[i][0] == '#'))
+                    exListUpdatedOffset += sprintf(exListUpdated + exListUpdatedOffset, "%s\n", exListLines[i]);
+                else listUpdated = true;
+            }
+
+            UnloadTextLines(exListLines, lineCount);
+            */
+
+            if (listUpdated) SaveFileText(exCollectionFilePath, exListUpdated);
+
+            UnloadFileText(exList);
+            RL_FREE(exListUpdated);
+
+            UnloadDirectoryFiles(clist);
+            //---------------------------------------------------------------------------------------------------
+
+            // Check all examples in collection [examples_list.txt] -> Source of truth!
+            LOG("INFO: Validating examples in collection...\n");
+            int exCollectionCount = 0;
+            rlExampleInfo *exCollection = LoadExampleData("ALL", false, &exCollectionCount);
+
+            // Set status information for all examples, using "status" field in the struct
+            for (int i = 0; i < exCollectionCount; i++)
+            {
+                rlExampleInfo *exInfo = &exCollection[i];
+                exInfo->status = 0;
+
+                LOG("INFO: [%s] Validating example...\n", exInfo->name);
+
+                // Validate: raylib/examples/<category>/<category>_example_name.c       -> File exists?
+                if (!FileExists(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name))) exInfo->status |= VALID_MISSING_C;
+
+                // Validate: raylib/examples/<category>/<category>_example_name.png     -> File exists?
+                if (!FileExists(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name))) exInfo->status |= VALID_MISSING_PNG;
+
+                // Validate: example screenshot is not the template default one
+                Image imScreenshot = LoadImage(TextFormat("%s/%s/%s.png", exBasePath, exInfo->category, exInfo->name));
+                Image imTemplate = LoadImage(TextFormat("%s/examples_template.png", exBasePath));
+                if (memcmp(imScreenshot.data, imTemplate.data, GetPixelDataSize(imScreenshot.width, imScreenshot.height, imScreenshot.format)) == 0)
+                    exInfo->status |= VALID_INVALID_PNG;
+                UnloadImage(imTemplate);
+                UnloadImage(imScreenshot);
+
+                // Validate: raylib/examples/Makefile                                   -> Example listed?
+                if (FileTextFindIndex(TextFormat("%s/Makefile", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE;
+
+                // Validate: raylib/examples/Makefile.Web                               -> Example listed?
+                if (FileTextFindIndex(TextFormat("%s/Makefile.Web", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_MAKEFILE_WEB;
+
+                // Validate: raylib/examples/README.md                                  -> Example listed?
+                if (FileTextFindIndex(TextFormat("%s/README.md", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_README;
+
+                // Validate: raylib.com/common/examples.js                              -> Example listed?
+                if (FileTextFindIndex(TextFormat("%s/../common/examples.js", exWebPath), exInfo->name + TextFindIndex(exInfo->name, "_") + 1) == -1) exInfo->status |= VALID_NOT_IN_JS;
+
+                // Validate: raylib/projects/VS2022/examples/<category>_example_name.vcxproj -> File exists?
+                if (!FileExists(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name))) exInfo->status |= VALID_MISSING_VCXPROJ;
+
+                // Validate: raylib/projects/VS2022/raylib.sln                          -> Example listed?
+                if (FileTextFindIndex(TextFormat("%s/../projects/VS2022/raylib.sln", exBasePath), exInfo->name) == -1) exInfo->status |= VALID_NOT_IN_VCXSOL;
+
+                // Validate: raylib/examples/<category>/resources/..                    -> Example resources available?
+                // Scan resources used in example to check for missing resource files
+                // WARNING: Some paths could be for files to save, not files to load, verify it
+                char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name), &exInfo->resCount);
+                if (exInfo->resCount > 0)
+                {
+                    for (int r = 0; r < exInfo->resCount; r++)
+                    {
+                        // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
+                        // In this case, multiple resources are required: glsl100, glsl120, glsl330
+                        if (TextFindIndex(resPaths[r], "glsl%i") > -1)
+                        {
+                            int glslVer[3] = { 100, 120, 330 };
+
+                            for (int v = 0; v < 3; v++)
+                            {
+                                char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", TextFormat("glsl%i", glslVer[v]));
+                                if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPathUpdated)))
+                                {
+                                    exInfo->status |= VALID_MISSING_RESOURCES;
+                                    // Logging missing resources for convenience
+                                    LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPathUpdated);
+                                }
+                                RL_FREE(resPathUpdated);
+                            }
+                        }
+                        else
+                        {
+                            if (!FileExists(TextFormat("%s/%s/%s", exBasePath, exInfo->category, resPaths[r])))
+                            {
+                                exInfo->status |= VALID_MISSING_RESOURCES;
+                                LOG("WARNING: [%s] Missing resource: %s\n", exInfo->name, resPaths[r]);
+                            }
+                        }
+                    }
+                }
+                UnloadExampleResourcePaths(resPaths);
+
+                // Validate: raylib.com/examples/<category>/<category>_example_name.html -> File exists?
+                // Validate: raylib.com/examples/<category>/<category>_example_name.data -> File exists?
+                // Validate: raylib.com/examples/<category>/<category>_example_name.wasm -> File exists?
+                // Validate: raylib.com/examples/<category>/<category>_example_name.js   -> File exists?
+                if (!TextIsEqual(exInfo->category, "others") &&
+                    (!FileExists(TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name)) ||
+                     !FileExists(TextFormat("%s/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name)) ||
+                     !FileExists(TextFormat("%s/%s/%s.js", exWebPath, exInfo->category, exInfo->name)) ||
+                     ((exInfo->resCount > 0) && !FileExists(TextFormat("%s/%s/%s.data", exWebPath, exInfo->category, exInfo->name)))))
+                {
+                     exInfo->status |= VALID_MISSING_WEB_OUTPUT;
+                }
+
+                // Validate: raylib.com/examples/<category>/<category>_example_name.html -> Metadata
+                if (FileExists(TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name)))
+                {
+                    char *exHtmlText = LoadFileText(TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name));
+
+                    if ((TextFindIndex(exHtmlText, "raylib web game") > -1) || // title
+                        (TextFindIndex(exHtmlText, "New raylib web videogame, developed using raylib videogames library") > -1) || // description
+                        (TextFindIndex(exHtmlText, "https://www.raylib.com/common/raylib_logo.png") > -1) || // image
+                        (TextFindIndex(exHtmlText, "https://www.raylib.com/games.html") > -1) || // url
+                        (TextFindIndex(exHtmlText, "https://github.com/raysan5/raylib';") > -1)) // source code button
+                    {
+                        exInfo->status |= VALID_MISSING_WEB_METADATA;
+                    }
+
+                    UnloadFileText(exHtmlText);
+                }
+
+                // NOTE: Additional validation elements
+                // Validate: Example naming conventions: <category>/<category>_example_name, valid category
+                if ((TextFindIndex(exInfo->name, exInfo->category) == -1) ||
+                    (!TextIsEqual(exInfo->category, "core") && !TextIsEqual(exInfo->category, "shapes") &&
+                     !TextIsEqual(exInfo->category, "textures") && !TextIsEqual(exInfo->category, "text") &&
+                     !TextIsEqual(exInfo->category, "models") && !TextIsEqual(exInfo->category, "shaders") &&
+                     !TextIsEqual(exInfo->category, "audio") && !TextIsEqual(exInfo->category, "others"))) exInfo->status |= VALID_INVALID_CATEGORY;
+
+                // Validate: Example info (stars, author, github) missmatches with example header content
+                rlExampleInfo *exInfoHeader = LoadExampleInfo(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name));
+
+                if ((strcmp(exInfo->name, exInfoHeader->name) != 0) ||     // NOTE: Get it from example, not file
+                    (strcmp(exInfo->category, exInfoHeader->category) != 0) ||
+                    (strcmp(exInfo->author, exInfoHeader->author) != 0) ||
+                    (strcmp(exInfo->authorGitHub, exInfoHeader->authorGitHub) != 0) ||
+                    (exInfo->stars != exInfoHeader->stars) ||
+                    (strcmp(exInfo->verCreated, exInfoHeader->verCreated) != 0) ||
+                    (strcmp(exInfo->verUpdated, exInfoHeader->verUpdated) != 0))
+                {
+                    exInfo->status |= VALID_INCONSISTENT_INFO;
+                }
+
+                if (exInfo->status == 0) LOG("INFO: [%s] Validation result: OK\n", exInfo->name);
+                else LOG("WARNING: [%s] Validation result: ISSUES FOUND\n", exInfo->name);
+
+                UnloadExampleInfo(exInfoHeader);
+            }
+
+            if (opCode == OP_UPDATE)
+            {
+                LOG("INFO: Updating examples with issues in collection...\n");
+
+                // Actions to fix/review anything possible from validation results
+                //------------------------------------------------------------------------------------------------
+                // Update files: Makefile, Makefile.Web, README.md, examples.js
+                // Solves: VALID_NOT_IN_MAKEFILE, VALID_NOT_IN_MAKEFILE_WEB, VALID_NOT_IN_README, VALID_NOT_IN_JS
+                // WARNING: Makefile.Web needs to be updated before trying to rebuild web example!
+                UpdateRequiredFiles();
+                for (int i = 0; i < exCollectionCount; i++)
+                {
+                    exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE;
+                    exCollection[i].status &= ~VALID_NOT_IN_MAKEFILE_WEB;
+                    exCollection[i].status &= ~VALID_NOT_IN_README;
+                    exCollection[i].status &= ~VALID_NOT_IN_JS;
+                }
+                
+                // Check examples "status" information
+                for (int i = 0; i < exCollectionCount; i++)
+                {
+                    rlExampleInfo *exInfo = &exCollection[i];
+
+                    if (exInfo->status & VALID_MISSING_C) LOG("WARNING: [%s] Missing code file\n", exInfo->name);
+                    else
+                    {
+                        // NOTE: Some issues can not be automatically fixed, only logged
+                        if (exInfo->status & VALID_MISSING_PNG) LOG("WARNING: [%s] Missing screenshot file\n", exInfo->name);
+                        if (exInfo->status & VALID_INVALID_PNG) LOG("WARNING: [%s] Invalid screenshot file (using template)\n", exInfo->name);
+                        if (exInfo->status & VALID_MISSING_RESOURCES) LOG("WARNING: [%s] Missing resources detected\n", exInfo->name);
+                        if (exInfo->status & VALID_INCONSISTENT_INFO) LOG("WARNING: [%s] Inconsistent example header info\n", exInfo->name);
+                        if (exInfo->status & VALID_INVALID_CATEGORY) LOG("WARNING: [%s] Invalid example category\n", exInfo->name);
+
+                        // NOTE: Some examples should be excluded from VS2022 solution because
+                        // they have specific platform/linkage requirements:
+                        if ((strcmp(exInfo->name, "web_basic_window") == 0) ||
+                            (strcmp(exInfo->name, "raylib_opengl_interop") == 0)) continue;
+
+                        // Review: Add: raylib/projects/VS2022/examples/<category>_example_name.vcxproj
+                        // Review: Add: raylib/projects/VS2022/raylib.sln
+                        // Solves: VALID_MISSING_VCXPROJ, VALID_NOT_IN_VCXSOL
+                        if (exInfo->status & VALID_MISSING_VCXPROJ)
+                        {
+                            LOG("WARNING: [%s] Missing VS2022 project file\n", exInfo->name);
+                            LOG("INFO: [%s.vcxproj] Creating VS2022 project file\n", exInfo->name);
+                            FileCopy(TextFormat("%s/../projects/VS2022/examples/core_basic_window.vcxproj", exBasePath),
+                                TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name));
+                            FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name),
+                                "core_basic_window", exInfo->name);
+                            FileTextReplace(TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name),
+                                "..\\..\\examples\\core", TextFormat("..\\..\\examples\\%s", exInfo->category));
+
+                            exInfo->status &= ~VALID_MISSING_VCXPROJ;
+                        }
+
+                        // Add project (.vcxproj) to raylib solution (.sln)
+                        if (exInfo->status & VALID_NOT_IN_VCXSOL)
+                        {
+                            LOG("WARNING: [%s.vcxproj] Project not included in raylib solution (.sln)\n", exInfo->name);
+                            LOG("INFO: [%s.vcxproj] Adding project to raylib solution (.sln)\n", exInfo->name);
+                            AddVSProjectToSolution(exVSProjectSolutionFile,
+                                TextFormat("%s/../projects/VS2022/examples/%s.vcxproj", exBasePath, exInfo->name), exInfo->category);
+
+                            exInfo->status &= ~VALID_NOT_IN_VCXSOL;
+                        }
+
+                        // Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.html
+                        // Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.data
+                        // Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.wasm
+                        // Review: Add/Remove: raylib.com/examples/<category>/<category>_example_name.js
+                        // Solves: VALID_MISSING_WEB_OUTPUT
+                        if ((strcmp(exInfo->category, "others") != 0) && // Skipping "others" category
+                            ((exInfo->status & VALID_MISSING_WEB_OUTPUT) || (exInfo->status & VALID_MISSING_WEB_METADATA)))
+                        {
+                            LOG("WARNING: [%s] Example not available on raylib web\n", exInfo->name);
+
+                            // Build example for PLATFORM_WEB
+                        #if defined(_WIN32)
+                            LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exInfo->name);
+                            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+                            system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exInfo->category, exInfo->name));
+                        #else
+                            LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exInfo->name);
+                            system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exInfo->category, exInfo->name));
+                        #endif
+
+                            // Update generated .html metadata
+                            LOG("INFO: [%s.html] Updating HTML Metadata...\n", exInfo->name);
+                            UpdateWebMetadata(TextFormat("%s/%s/%s.html", exBasePath, exInfo->category, exInfo->name),
+                                TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name));
+
+                            // Copy results to web side
+                            LOG("INFO: [%s] Copy example build to raylib.com\n", exInfo->name);
+                            FileCopy(TextFormat("%s/%s/%s.html", exBasePath, exInfo->category, exInfo->name),
+                                TextFormat("%s/%s/%s.html", exWebPath, exInfo->category, exInfo->name));
+                            FileCopy(TextFormat("%s/%s/%s.data", exBasePath, exInfo->category, exInfo->name),
+                                TextFormat("%s/%s/%s.data", exWebPath, exInfo->category, exInfo->name));
+                            FileCopy(TextFormat("%s/%s/%s.wasm", exBasePath, exInfo->category, exInfo->name),
+                                TextFormat("%s/%s/%s.wasm", exWebPath, exInfo->category, exInfo->name));
+                            FileCopy(TextFormat("%s/%s/%s.js", exBasePath, exInfo->category, exInfo->name),
+                                TextFormat("%s/%s/%s.js", exWebPath, exInfo->category, exInfo->name));
+
+                            exInfo->status &= ~VALID_MISSING_WEB_OUTPUT;
+                            exInfo->status &= ~VALID_MISSING_WEB_METADATA;
+                        }
+
+                        if (exInfo->status & VALID_INCONSISTENT_INFO)
+                        {
+                            // Update source code header info
+                            LOG("WARNING: [%s.c] Inconsistent source code metadata\n", exInfo->name);
+                            LOG("INFO: [%s.c] Updating source code metadata...\n", exInfo->name);
+                            UpdateSourceMetadata(TextFormat("%s/%s/%s.c", exBasePath, exInfo->category, exInfo->name), exInfo);
+
+                            exInfo->status &= ~VALID_INCONSISTENT_INFO;
+                        }
+                    }
+                }
+                //------------------------------------------------------------------------------------------------
+            }
+
+            // Generate validation report/table with results (.md)
+            //-----------------------------------------------------------------------------------------------------
+            /*
+            Columns:
+            [C]     VALID_MISSING_C             // Missing .c source file
+            [PNG]   VALID_MISSING_PNG           // Missing screenshot .png
+            [WPNG]  VALID_INVALID_PNG           // Invalid png screenshot (using template one)
+            [RES]   VALID_MISSING_RESOURCES     // Missing resources listed in the code
+            [VCX]   VALID_MISSING_VCXPROJ       // Missing Visual Studio .vcxproj file
+            [SOL]   VALID_NOT_IN_VCXSOL         // Project not included in solution file
+            [MK]    VALID_NOT_IN_MAKEFILE       // Not listed in Makefile
+            [MKWEB] VALID_NOT_IN_MAKEFILE_WEB   // Not listed in Makefile.Web
+            [RDME]  VALID_NOT_IN_README         // Not listed in README.md
+            [JS]    VALID_NOT_IN_JS             // Not listed in examples.js
+            [WOUT]  VALID_MISSING_WEB_OUTPUT    // Missing .html/.data/.wasm/.js
+            [WMETA] VALID_MISSING_WEB_METADATA  // Missing .html example metadata
+            [INFO]  VALID_INCONSISTENT_INFO     // Inconsistent info between collection and example header (stars, author...)
+            [CAT]   VALID_INVALID_CATEGORY      // Not a recognized category
+
+            | [EXAMPLE NAME]               | [C] |[CAT]|[INFO]|[PNG]|[WPNG]|[RES]|[MK] |[MKWEB]|[VCX]|[SOL]|[RDME]|[JS] |[WOUT]|[WMETA]|
+            |:-----------------------------|:---:|:---:|:----:|:---:|:----:|:---:|:---:|:-----:|:---:|:---:|:----:|:---:|:----:|:-----:|
+            | core_basic_window            |  ✔ |  ✔  |  ✔  |  ✔ |  ✔  |  ✔  |  ✔ |   ✔  |  ✔  |  ✔ |  ✔  |  ✔ |   ✔  |   ✔  |
+            | shapes_colors_palette        |  ✘ |  ✔  |  ✘  |  ✔ |  ✘  |  ✔  |  ✔ |   ✘  |  ✔  |  ✔ |  ✔  |  ✔ |   ✔  |   ✔  |
+            | text_format_text             |  ✘ |  ✘  |  ✘  |  ✘ |  ✘  |  ✘  |  ✘ |   ✘  |  ✔  |  ✘ |  ✔  |  ✔ |   ✔  |   ✔  |
+            */
+            LOG("INFO: [examples_validation.md] Generating examples validation report...\n");
+
+            char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+            int repIndex = 0;
+            repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION - VALIDATION REPORT\n\n");
+
+            repIndex += sprintf(report + repIndex, "```\nExample elements validated:\n\n");
+            repIndex += sprintf(report + repIndex, " - [C]     : Missing .c source file\n");
+            repIndex += sprintf(report + repIndex, " - [CAT]   : Not a recognized category\n");
+            repIndex += sprintf(report + repIndex, " - [INFO]  : Inconsistent example header info (stars, author...)\n");
+            repIndex += sprintf(report + repIndex, " - [PNG]   : Missing screenshot .png\n");
+            repIndex += sprintf(report + repIndex, " - [WPNG]  : Invalid png screenshot (using default one)\n");
+            repIndex += sprintf(report + repIndex, " - [RES]   : Missing resources listed in the code\n");
+            repIndex += sprintf(report + repIndex, " - [MK]    : Not listed in Makefile\n");
+            repIndex += sprintf(report + repIndex, " - [MKWEB] : Not listed in Makefile.Web\n");
+            repIndex += sprintf(report + repIndex, " - [VCX]   : Missing Visual Studio project file\n");
+            repIndex += sprintf(report + repIndex, " - [SOL]   : Project not included in solution file\n");
+            repIndex += sprintf(report + repIndex, " - [RDME]  : Not listed in README.md\n");
+            repIndex += sprintf(report + repIndex, " - [JS]    : Not listed in Web (examples.js)\n");
+            repIndex += sprintf(report + repIndex, " - [WOUT]  : Missing Web build (.html/.data/.wasm/.js)\n");
+            repIndex += sprintf(report + repIndex, " - [WMETA] : Missing Web .html example metadata\n```\n");
+
+            repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME**                 | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]|\n");
+            repIndex += sprintf(report + repIndex, "|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:|\n");
+
+            for (int i = 0; i < exCollectionCount; i++)
+            {
+                repIndex += sprintf(report + repIndex, "| %-32s |  %s |  %s  |  %s  |  %s |  %s  |  %s  |  %s |   %s  |  %s  |  %s |  %s  |  %s |  %s  |  %s  |\n",
+                    exCollection[i].name,
+                    (exCollection[i].status & VALID_MISSING_C)? "❌" : "✔",
+                    (exCollection[i].status & VALID_INVALID_CATEGORY)? "❌" : "✔",
+                    (exCollection[i].status & VALID_INCONSISTENT_INFO)? "❌" : "✔",
+                    (exCollection[i].status & VALID_MISSING_PNG)? "❌" : "✔",
+                    (exCollection[i].status & VALID_INVALID_PNG)? "❌" : "✔",
+                    (exCollection[i].status & VALID_MISSING_RESOURCES)? "❌" : "✔",
+                    (exCollection[i].status & VALID_NOT_IN_MAKEFILE)? "❌" : "✔",
+                    (exCollection[i].status & VALID_NOT_IN_MAKEFILE_WEB)? "❌" : "✔",
+                    (exCollection[i].status & VALID_MISSING_VCXPROJ)? "❌" : "✔",
+                    (exCollection[i].status & VALID_NOT_IN_VCXSOL)? "❌" : "✔",
+                    (exCollection[i].status & VALID_NOT_IN_README)? "❌" : "✔",
+                    (exCollection[i].status & VALID_NOT_IN_JS)? "❌" : "✔",
+                    (exCollection[i].status & VALID_MISSING_WEB_OUTPUT)? "❌" : "✔",
+                    (exCollection[i].status & VALID_MISSING_WEB_METADATA)? "❌" : "✔");
+            }
+
+            SaveFileText(TextFormat("%s/../tools/rexm/reports/%s", exBasePath, "examples_validation.md"), report);
+            RL_FREE(report);
+            //-----------------------------------------------------------------------------------------------------
+
+            // Generate a report with only the examples missing some elements
+            //-----------------------------------------------------------------------------------------------------
+            LOG("INFO: [examples_issues.md] Generating examples issues report...\n");
+
+            char *reportIssues = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+            repIndex = 0;
+            repIndex += sprintf(reportIssues + repIndex, "# EXAMPLES COLLECTION - VALIDATION REPORT\n\n");
+
+            repIndex += sprintf(reportIssues + repIndex, "```\nExample elements validated:\n\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [C]     : Missing .c source file\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [CAT]   : Not a recognized category\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [INFO]  : Inconsistent example header info (stars, author...)\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [PNG]   : Missing screenshot .png\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [WPNG]  : Invalid png screenshot (using default one)\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [RES]   : Missing resources listed in the code\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [MK]    : Not listed in Makefile\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [MKWEB] : Not listed in Makefile.Web\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [VCX]   : Missing Visual Studio project file\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [SOL]   : Project not included in solution file\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [RDME]  : Not listed in README.md\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [JS]    : Not listed in Web (examples.js)\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [WOUT]  : Missing Web build (.html/.data/.wasm/.js)\n");
+            repIndex += sprintf(reportIssues + repIndex, " - [WMETA] : Missing Web .html example metadata\n```\n");
+
+            repIndex += sprintf(reportIssues + repIndex, "| **EXAMPLE NAME**                 | [C] | [CAT]| [INFO]|[PNG]|[WPNG]| [RES]| [MK] |[MKWEB]| [VCX]| [SOL]|[RDME]|[JS] | [WOUT]|[WMETA]|\n");
+            repIndex += sprintf(reportIssues + repIndex, "|:---------------------------------|:---:|:----:|:-----:|:---:|:----:|:----:|:----:|:-----:|:----:|:----:|:----:|:---:|:-----:|:-----:|\n");
+
+            for (int i = 0; i < exCollectionCount; i++)
+            {
+                if (exCollection[i].status > 0)
+                {
+                    repIndex += sprintf(reportIssues + repIndex, "| %-32s |  %s |  %s  |  %s  |  %s |  %s  |  %s  |  %s |   %s  |  %s  |  %s |  %s  |  %s |  %s  |  %s  |\n",
+                        exCollection[i].name,
+                        (exCollection[i].status & VALID_MISSING_C)? "❌" : "✔",
+                        (exCollection[i].status & VALID_INVALID_CATEGORY)? "❌" : "✔",
+                        (exCollection[i].status & VALID_INCONSISTENT_INFO)? "❌" : "✔",
+                        (exCollection[i].status & VALID_MISSING_PNG)? "❌" : "✔",
+                        (exCollection[i].status & VALID_INVALID_PNG)? "❌" : "✔",
+                        (exCollection[i].status & VALID_MISSING_RESOURCES)? "❌" : "✔",
+                        (exCollection[i].status & VALID_NOT_IN_MAKEFILE)? "❌" : "✔",
+                        (exCollection[i].status & VALID_NOT_IN_MAKEFILE_WEB)? "❌" : "✔",
+                        (exCollection[i].status & VALID_MISSING_VCXPROJ)? "❌" : "✔",
+                        (exCollection[i].status & VALID_NOT_IN_VCXSOL)? "❌" : "✔",
+                        (exCollection[i].status & VALID_NOT_IN_README)? "❌" : "✔",
+                        (exCollection[i].status & VALID_NOT_IN_JS)? "❌" : "✔",
+                        (exCollection[i].status & VALID_MISSING_WEB_OUTPUT)? "❌" : "✔",
+                        (exCollection[i].status & VALID_MISSING_WEB_METADATA)? "❌" : "✔");
+                }
+            }
+
+            SaveFileText(TextFormat("%s/../tools/rexm/reports/%s", exBasePath, "examples_issues.md"), reportIssues);
+            RL_FREE(reportIssues);
+            //-----------------------------------------------------------------------------------------------------
+
+            UnloadExampleData(exCollection);
+            //------------------------------------------------------------------------------------------------
+
+        } break;
+        case OP_TEST:
+        {
+            LOG("INFO: Command requested: TEST\n");
+            LOG("INFO: Example(s) to be build and tested: %i [%s]\n", exBuildListCount, (exBuildListCount == 1)? exBuildList[0] : argv[2]);
+
+#if defined(_WIN32)
+            // Set required environment variables
+            //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+            //_putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+            //putenv("MAKE=mingw32-make");
+            //ChangeDirectory(exBasePath);
+            //_putenv("MAKE_PATH=C:\\raylib\\w64devkit\\bin");
+            //_putenv("EMSDK_PATH = C:\\raylib\\emsdk");
+            //_putenv("PYTHON_PATH=$(EMSDK_PATH)\\python\\3.9.2-nuget_64bit");
+            //_putenv("NODE_PATH=$(EMSDK_PATH)\\node\\20.18.0_64bit\\bin");
+            //_putenv("PATH=%PATH%;$(MAKE_PATH);$(EMSDK_PATH);$(NODE_PATH);$(PYTHON_PATH)");
+
+            _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin;C:\\raylib\\emsdk\\python\\3.9.2-nuget_64bit;C:\\raylib\\emsdk\\node\\20.18.0_64bit\\bin");
+#endif
+
+            for (int i = 0; i < exBuildListCount; i++)
+            {
+                // Get example name and category
+                memset(exName, 0, 64);
+                strcpy(exName, exBuildList[i]);
+                memset(exCategory, 0, 32);
+                strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+
+                // Skip some examples from building
+                if ((strcmp(exName, "core_custom_logging") == 0) ||
+                    (strcmp(exName, "core_window_should_close") == 0) ||
+                    (strcmp(exName, "core_custom_frame_control") == 0)) continue;
+
+                LOG("INFO: [%i/%i] Testing example: [%s]\n", i + 1, exBuildListCount, exName);
+
+                // Create directory for logs (build and run logs)
+                MakeDirectory(TextFormat("%s/%s/logs", exBasePath, exCategory));
+
+                // Steps to follow
+                // STEP 1: Load example.c and replace required code to inject basic testing code: frames to run
+                //    OPTION 1: Code injection required multiple changes for testing but it does not require raylib changes!
+                //    OPTION 2: Support testing on raylib side: Args processing and events injection: SUPPORT_AUTOMATD_TESTING_SYSTEM, EVENTS_TESTING_MODE
+                // STEP 2: Build example (PLATFORM_DESKTOP)
+                // STEP 3: Run example with arguments: --frames 2 > <example>.out.log
+                // STEP 4: Load <example>.out.log and check "WARNING:" messages -> Some could maybe be ignored
+                // STEP 5: Generate report with results
+
+                // STEP 1: Load example and inject required code
+                //    PROBLEM: As we need to modify the example source code for building, we need to keep a copy or something
+                //      WARNING: If we make a copy and something fails, it could not be restored at the end
+                //    PROBLEM: Trying to build a copy won't work because Makefile is setup to look for specific example on specific path -> No output dir config
+                //    IDEA: Create directory for testing data -> It implies moving files and set working dir...
+                //    SOLUTION: Make a copy of original file -> Modify original -> Build -> Rename to <example>.test.exe
+                FileCopy(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+                char *srcText = LoadFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+
+//#define BUILD_TESTING_WEB
+#if defined(BUILD_TESTING_WEB)
+                static const char *mainReplaceText =
+                    "#include <stdio.h>\n"
+                    "#include <string.h>\n"
+                    "#include <stdlib.h>\n"
+                    "#include <emscripten/emscripten.h>\n\n"
+                    "static char logText[4096] = {0};\n"
+                    "static int logTextOffset = 0;\n\n"
+                    "void CustomTraceLog(int msgType, const char *text, va_list args)\n{\n"
+                    "    if (logTextOffset < 3800)\n    {\n"
+                    "    switch (msgType)\n    {\n"
+                    "        case LOG_INFO: logTextOffset += sprintf(logText + logTextOffset, \"INFO: \"); break;\n"
+                    "        case LOG_ERROR: logTextOffset += sprintf(logText + logTextOffset, \"ERROR: \"); break;\n"
+                    "        case LOG_WARNING: logTextOffset += sprintf(logText + logTextOffset, \"WARNING: \"); break;\n"
+                    "        case LOG_DEBUG: logTextOffset += sprintf(logText + logTextOffset, \"DEBUG: \"); break;\n"
+                    "        default: break;\n    }\n"
+                    "    logTextOffset += vsprintf(logText + logTextOffset, text, args);\n"
+                    "    logTextOffset += sprintf(logText + logTextOffset, \"\\n\");\n}\n}\n\n"
+                    "int main(int argc, char *argv[])\n{\n"
+                    "    SetTraceLogCallback(CustomTraceLog);\n"
+                    "    int requestedTestFrames = 0;\n"
+                    "    int testFramesCount = 0;\n"
+                    "    if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
+
+                static const char *returnReplaceText =
+                    "    SaveFileText(\"outputLogFileName\", logText);\n"
+                    "    emscripten_run_script(\"saveFileFromMEMFSToDisk('outputLogFileName','outputLogFileName')\");\n\n"
+                    "    return 0";
+                char *returnReplaceTextUpdated = TextReplace(returnReplaceText, "outputLogFileName", TextFormat("%s.log", exName));
+
+                char *srcTextUpdated[4] = { 0 };
+                srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText);
+                srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
+                srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
+                srcTextUpdated[3] = TextReplace(srcTextUpdated[2], "    return 0", returnReplaceTextUpdated);
+                MemFree(returnReplaceTextUpdated);
+                UnloadFileText(srcText);
+
+                SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[3]);
+                for (int i = 0; i < 4; i++) { MemFree(srcTextUpdated[i]); srcTextUpdated[i] = NULL; }
+
+                // Build example for PLATFORM_WEB
+                // Build: raylib.com/examples/<category>/<category>_example_name.html
+                // Build: raylib.com/examples/<category>/<category>_example_name.data
+                // Build: raylib.com/examples/<category>/<category>_example_name.wasm
+                // Build: raylib.com/examples/<category>/<category>_example_name.js
+    #if defined(_WIN32)
+                LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: Win32)\n", exName);
+                system(TextFormat("mingw32-make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B > %s/%s/logs/%s.build.log 2>&1",
+                    exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+    #else
+                LOG("INFO: [%s] Building example for PLATFORM_WEB (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s -f Makefile.Web %s/%s PLATFORM=PLATFORM_WEB -B", exBasePath, exCategory, exName));
+    #endif
+                // Restore original source code before continue
+                FileCopy(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+                FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+
+                // STEP 3: Run example on browser
+                if (FileExists(TextFormat("%s/%s/%s.html", exBasePath, exCategory, exName)) &&
+                    FileExists(TextFormat("%s/%s/%s.wasm", exBasePath, exCategory, exName)) &&
+                    FileExists(TextFormat("%s/%s/%s.js", exBasePath, exCategory, exName)))
+                {
+                    // WARNING: Example download is asynchronous so reading fails on next step
+                    // when looking for a file that could not have been downloaded yet
+                    ChangeDirectory(TextFormat("%s", exBasePath));
+                    if (i == 0) system("start python -m http.server 8080"); // Init localhost just once
+                    system(TextFormat("start explorer \"http:\\localhost:8080/%s/%s.html", exCategory, exName));
+                }
+
+                // NOTE: Example .log is automatically downloaded into system Downloads directory on browser-example exectution
+
+#else // BUILD_TESTING_DESKTOP
+
+                static const char *mainReplaceText =
+                    "#include <string.h>\n"
+                    "#include <stdlib.h>\n"
+                    "int main(int argc, char *argv[])\n{\n"
+                    "    int requestedTestFrames = 0;\n"
+                    "    int testFramesCount = 0;\n"
+                    "    if ((argc > 1) && (argc == 3) && (strcmp(argv[1], \"--frames\") != 0)) requestedTestFrames = atoi(argv[2]);\n";
+
+                char *srcTextUpdated[3] = { 0 };
+                srcTextUpdated[0] = TextReplace(srcText, "int main(void)\n{", mainReplaceText);
+                srcTextUpdated[1] = TextReplace(srcTextUpdated[0], "WindowShouldClose()", "WindowShouldClose() && (testFramesCount < requestedTestFrames)");
+                srcTextUpdated[2] = TextReplace(srcTextUpdated[1], "EndDrawing();", "EndDrawing(); testFramesCount++;");
+                UnloadFileText(srcText);
+
+                SaveFileText(TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName), srcTextUpdated[2]);
+                for (int i = 0; i < 3; i++) { MemFree(srcTextUpdated[i]); srcTextUpdated[i] = NULL; }
+
+                // STEP 2: Build example for DESKTOP platform
+    #if defined(_WIN32)
+                // Set required environment variables
+                //putenv(TextFormat("RAYLIB_DIR=%s\\..", exBasePath));
+                _putenv("PATH=%PATH%;C:\\raylib\\w64devkit\\bin");
+                //putenv("MAKE=mingw32-make");
+                //ChangeDirectory(exBasePath);
+    #endif
+                // Build example for PLATFORM_DESKTOP
+    #if defined(_WIN32)
+                LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: Win32)\n", exName);
+                system(TextFormat("mingw32-make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B > %s/%s/logs/%s.build.log 2>&1",
+                    exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+    #elif defined(PLATFORM_DRM)
+                LOG("INFO: [%s] Building example for PLATFORM_DRM (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DRM -B > %s/%s/logs/%s.build.log 2>&1",
+                    exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+    #else
+                LOG("INFO: [%s] Building example for PLATFORM_DESKTOP (Host: POSIX)\n", exName);
+                system(TextFormat("make -C %s %s/%s PLATFORM=PLATFORM_DESKTOP -B > %s/%s/logs/%s.build.log 2>&1",
+                    exBasePath, exCategory, exName, exBasePath, exCategory, exName));
+    #endif
+                // Restore original source code before continue
+                FileCopy(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName),
+                    TextFormat("%s/%s/%s.c", exBasePath, exCategory, exName));
+                FileRemove(TextFormat("%s/%s/%s.original.c", exBasePath, exCategory, exName));
+
+                // STEP 3: Run example with required arguments
+                // NOTE: Not easy to retrieve process return value from system(), it's platform dependant
+                ChangeDirectory(TextFormat("%s/%s", exBasePath, exCategory));
+
+    #if defined(_WIN32)
+                system(TextFormat("%s --frames 2 > logs/%s.log", exName, exName));
+    #else
+                system(TextFormat("./%s --frames 2 > logs/%s.log", exName, exName));
+    #endif
+#endif
+            }
+        } break;
+        case OP_TESTLOG:
+        {
+            // STEP 4: Load and validate available logs info
+            //---------------------------------------------------------------------------------------------
+            rlExampleTesting *testing = (rlExampleTesting *)RL_CALLOC(exBuildListCount, sizeof(rlExampleTesting));
+
+            for (int i = 0; i < exBuildListCount; i++)
+            {
+                // Get example name and category
+                memset(exName, 0, 64);
+                strcpy(exName, exBuildList[i]);
+                memset(exCategory, 0, 32);
+                strncpy(exCategory, exName, TextFindIndex(exName, "_"));
+
+                // Skip some examples from building
+                if ((strcmp(exName, "core_custom_logging") == 0) ||
+                    (strcmp(exName, "core_window_should_close") == 0) ||
+                    (strcmp(exName, "core_custom_frame_control") == 0)) continue;
+
+                LOG("INFO: [%i/%i] Checking example log: [%s]\n", i + 1, exBuildListCount, exName);
+
+                // Load <example_name>.build.log to check for compilation warnings
+                char *exTestBuildLog = LoadFileText(TextFormat("%s/%s/logs/%s.build.log", exBasePath, exCategory, exName));
+                if (exTestBuildLog == NULL)
+                {
+                    LOG("WARNING: [%s] Build log could not be loaded\n", exName);
+                    continue;
+                }
+
+                // Load build log text lines
+                int exTestBuildLogLinesCount = 0;
+                char **exTestBuildLogLines = LoadTextLines(exTestBuildLog, &exTestBuildLogLinesCount);
+
+                for (int k = 0; k < exTestBuildLogLinesCount; k++)
+                {
+                    // Checking compilation warnings generated
+                    if (TextFindIndex(exTestBuildLogLines[k], "warning:") >= 0) testing[i].buildwarns++;
+                }
+
+                UnloadTextLines(exTestBuildLogLines, exTestBuildLogLinesCount);
+                UnloadFileText(exTestBuildLog);
+
+#if defined(BUILD_TESTING_WEB)
+                // TODO: REVIEW: Hardcoded path where web logs are copied after automatic download
+                char *exTestLog = LoadFileText(TextFormat("D:/testing_logs_web/%s.log", exName));
+#else
+                char *exTestLog = LoadFileText(TextFormat("%s/%s/logs/%s.log", exBasePath, exCategory, exName));
+#endif
+                if (exTestLog == NULL)
+                {
+                    LOG("WARNING: [%s] Execution log could not be loaded\n", exName);
+                    testing[i].status = 0b1111111;
+                    continue;
+                }
+
+                /*
+                TESTING_FAIL_INIT      = 1 << 0,   // Initialization (InitWindow())    -> "INFO: DISPLAY: Device initialized successfully"
+                TESTING_FAIL_CLOSE     = 1 << 1,   // Closing (CloseWindow())          -> "INFO: Window closed successfully"
+                TESTING_FAIL_ASSETS    = 1 << 2,   // Assets loading (WARNING: FILE:)  -> "WARNING: FILEIO:"
+                TESTING_FAIL_RLGL      = 1 << 3,   // OpenGL-wrapped initialization    -> "INFO: RLGL: Default OpenGL state initialized successfully"
+                TESTING_FAIL_PLATFORM  = 1 << 4,   // Platform initialization          -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+                TESTING_FAIL_FONT      = 1 << 5,   // Font default initialization      -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+                TESTING_FAIL_TIMER     = 1 << 6,   // Timer initialization             -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+                */
+
+                if (TextFindIndex(exTestLog, "INFO: DISPLAY: Device initialized successfully") == -1) testing[i].status |= TESTING_FAIL_INIT;
+                if (TextFindIndex(exTestLog, "INFO: Window closed successfully") == -1) testing[i].status |= TESTING_FAIL_CLOSE;
+                if (TextFindIndex(exTestLog, "WARNING: FILEIO:") >= 0) testing[i].status |= TESTING_FAIL_ASSETS;
+                if (TextFindIndex(exTestLog, "INFO: RLGL: Default OpenGL state initialized successfully") == -1) testing[i].status |= TESTING_FAIL_RLGL;
+                if (TextFindIndex(exTestLog, "INFO: PLATFORM:") == -1) testing[i].status |= TESTING_FAIL_PLATFORM;
+                if (TextFindIndex(exTestLog, "INFO: FONT: Default font loaded successfully") == -1) testing[i].status |= TESTING_FAIL_FONT;
+                if (TextFindIndex(exTestLog, "INFO: TIMER: Target time per frame:") == -1) testing[i].status |= TESTING_FAIL_TIMER;
+
+                // Load build log text lines
+                int exTestLogLinesCount = 0;
+                char **exTestLogLines = LoadTextLines(exTestLog, &exTestLogLinesCount);
+                for (int k = 0; k < exTestLogLinesCount; k++)
+                {
+#if defined(BUILD_TESTING_WEB)
+                    if (TextFindIndex(exTestLogLines[k], "WARNING: GL: NPOT") >= 0) continue; // Ignore web-specific warning
+#endif
+#if defined(PLATFORM_DRM)
+                    if (TextFindIndex(exTestLogLines[k], "WARNING: DISPLAY: No graphic") >= 0) continue; // Ignore specific warning
+                    if (TextFindIndex(exTestLogLines[k], "WARNING: GetCurrentMonitor()") >= 0) continue; // Ignore specific warning
+                    if (TextFindIndex(exTestLogLines[k], "WARNING: SetWindowPosition()") >= 0) continue; // Ignore specific warning
+#endif
+                    if (TextFindIndex(exTestLogLines[k], "WARNING") >= 0) testing[i].warnings++;
+                }
+                UnloadTextLines(exTestLogLines, exTestLogLinesCount);
+                UnloadFileText(exTestLog);
+            }
+            //---------------------------------------------------------------------------------------------
+
+            // STEP 5: Generate testing report/table with results (.md)
+            //-----------------------------------------------------------------------------------------------------
+#if defined(BUILD_TESTING_WEB)
+            const char *osName = "Web";
+#else
+    #if defined(PLATFORM_DRM)
+            const char *osName = "DRM";
+    #elif defined(PLATFORM_DESKTOP)
+        #if defined(_WIN32)
+            const char *osName = "Windows";
+        #elif defined(__linux__)
+            const char *osName = "Linux";
+        #elif defined(__FreeBSD__)
+            const char *osName = "FreeBSD";
+        #elif defined(__APPLE__)
+            const char *osName = "macOS";
+        #endif // Desktop OSs
+    #endif
+#endif
+            /*
+            Columns:
+             - [CWARN]  : Compilation WARNING messages
+             - [LWARN]  : Log WARNING messages count
+             - [INIT]   : Initialization
+             - [CLOSE]  : Closing
+             - [ASSETS] : Assets loading
+             - [RLGL]   : OpenGL-wrapped initialization
+             - [PLAT]   : Platform initialization
+             - [FONT]   : Font default initialization
+             - [TIMER]  : Timer initialization
+
+            | **EXAMPLE NAME**                 | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |
+            |:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|
+            | core_basic window                |    0    |    0    |   ✔   |    ✔    |    ✔    |   ✔   |    ✔   |   ✔   |    ✔   |
+            */
+            LOG("INFO: [examples_testing_os.md] Generating examples testing report...\n");
+
+            char *report = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+            int repIndex = 0;
+            repIndex += sprintf(report + repIndex, "# EXAMPLES COLLECTION - TESTING REPORT\n\n");
+            repIndex += sprintf(report + repIndex, TextFormat("## Tested Platform: %s\n\n", osName));
+
+            repIndex += sprintf(report + repIndex, "```\nExample automated testing elements validated:\n");
+            repIndex += sprintf(report + repIndex, " - [CWARN]  : Compilation WARNING messages\n");
+            repIndex += sprintf(report + repIndex, " - [LWARN]  : Log WARNING messages count\n");
+            repIndex += sprintf(report + repIndex, " - [INIT]   : Initialization\n");
+            repIndex += sprintf(report + repIndex, " - [CLOSE]  : Closing\n");
+            repIndex += sprintf(report + repIndex, " - [ASSETS] : Assets loading\n");
+            repIndex += sprintf(report + repIndex, " - [RLGL]   : OpenGL-wrapped initialization\n");
+            repIndex += sprintf(report + repIndex, " - [PLAT]   : Platform initialization\n");
+            repIndex += sprintf(report + repIndex, " - [FONT]   : Font default initialization\n");
+            repIndex += sprintf(report + repIndex, " - [TIMER]  : Timer initialization\n```\n");
+
+            repIndex += sprintf(report + repIndex, "| **EXAMPLE NAME**                 | [CWARN] | [LWARN] | [INIT] | [CLOSE] | [ASSETS] | [RLGL] | [PLAT] | [FONT] | [TIMER] |\n");
+            repIndex += sprintf(report + repIndex, "|:---------------------------------|:-------:|:-------:|:------:|:-------:|:--------:|:------:|:------:|:------:|:-------:|\n");
+
+            /*
+            TESTING_FAIL_INIT      = 1 << 0,   // Initialization (InitWindow())    -> "INFO: DISPLAY: Device initialized successfully"
+            TESTING_FAIL_CLOSE     = 1 << 1,   // Closing (CloseWindow())          -> "INFO: Window closed successfully"
+            TESTING_FAIL_ASSETS    = 1 << 2,   // Assets loading (WARNING: FILE:)  -> "WARNING: FILEIO:"
+            TESTING_FAIL_RLGL      = 1 << 3,   // OpenGL-wrapped initialization    -> "INFO: RLGL: Default OpenGL state initialized successfully"
+            TESTING_FAIL_PLATFORM  = 1 << 4,   // Platform initialization          -> "INFO: PLATFORM: DESKTOP (GLFW - Win32): Initialized successfully"
+            TESTING_FAIL_FONT      = 1 << 5,   // Font default initialization      -> "INFO: FONT: Default font loaded successfully (224 glyphs)"
+            TESTING_FAIL_TIMER     = 1 << 6,   // Timer initialization             -> "INFO: TIMER: Target time per frame: 16.667 milliseconds"
+            */
+            for (int i = 0; i < exBuildListCount; i++)
+            {
+                if ((testing[i].buildwarns > 0) || (testing[i].warnings > 0) || (testing[i].status > 0))
+                {
+                    repIndex += sprintf(report + repIndex, "| %-32s |    %i    |    %i    |   %s   |    %s    |   %s    |   %s   |   %s   |   %s   |   %s   |\n",
+                        exBuildList[i],
+                        testing[i].buildwarns,
+                        testing[i].warnings,
+                        (testing[i].status & TESTING_FAIL_INIT)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_CLOSE)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_ASSETS)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_RLGL)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_PLATFORM)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_FONT)? "❌" : "✔",
+                        (testing[i].status & TESTING_FAIL_TIMER)? "❌" : "✔");
+                }
+            }
+
+            repIndex += sprintf(report + repIndex, "\n");
+
+            SaveFileText(TextFormat("%s/../tools/rexm/reports/examples_testing_%s.md", exBasePath, TextToLower(osName)), report);
+
+            RL_FREE(report);
+            //-----------------------------------------------------------------------------------------------------
+
+        } break;
+        default:    // Help
+        {
+            // Supported commands:
+            //    create <new_example_name>     : Creates an empty example, from internal template
+            //    add <example_name>            : Add existing example, category extracted from name
+            //    rename <old_examples_name> <new_example_name> : Rename an existing example
+            //    remove <example_name>         : Remove an existing example
+            //    build <example_name>          : Build example for Desktop and Web platforms
+            //    validate                      : Validate examples collection, generates report
+            //    update                        : Validate and update examples collection, generates report
+
+            printf("\n////////////////////////////////////////////////////////////////////////////////////////////\n");
+            printf("//                                                                                        //\n");
+            printf("// rexm [raylib examples manager] - A simple command-line tool to manage raylib examples  //\n");
+            printf("// powered by raylib v5.6-dev                                                             //\n");
+            printf("//                                                                                        //\n");
+            printf("// Copyright (c) 2025-2026 Ramon Santamaria (@raysan5)                                    //\n");
+            printf("//                                                                                        //\n");
+            printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n");
+
+            printf("USAGE:\n\n");
+            printf("    > rexm <command> <example_name> [<example_rename>]\n\n");
+
+            printf("COMMANDS:\n\n");
+            printf("    create <new_example_name>     : Creates an empty example, from internal template\n");
+            printf("    add <example_name>            : Add existing example, category extracted from name\n");
+            printf("                                    Supported categories: core, shapes, textures, text, models\n");
+            printf("    rename <old_examples_name> <new_example_name> : Rename an existing example\n");
+            printf("    remove <example_name>         : Remove an existing example\n");
+            printf("    build <example_name>          : Build example for Desktop and Web platforms\n");
+            printf("    test <example_name>           : Build and Test example for Desktop and Web platforms\n");
+            printf("    validate                      : Validate examples collection, generates report\n");
+            printf("    update                        : Validate and update examples collection, generates report\n\n");
+            printf("OPTIONS:\n\n");
+            printf("    -h, --help                    : Show tool version and command line usage help\n");
+            printf("    -v, --verbose                 : Verbose mode, show additional logs on processes\n");
+            printf("\nEXAMPLES:\n\n");
+            printf("    > rexm add shapes_custom_stars\n");
+            printf("        Add and updates new example provided <shapes_custom_stars>\n\n");
+            printf("    > rexm rename core_basic_window core_cool_window\n");
+            printf("        Renames and updates example <core_basic_window> to <core_cool_window>\n\n");
+            printf("    > rexm update\n");
+            printf("        Validates all examples in collection and updates missing elements\n\n");
+        } break;
+    }
+
+    return 0;
+}
+
+//----------------------------------------------------------------------------------
+// Module Internal Functions Definition
+//----------------------------------------------------------------------------------
+// Update required files from examples collection
+static int UpdateRequiredFiles(void)
+{
+    int result = 0;
+
+    // Edit: Example source code metadata for consistency
+    //------------------------------------------------------------------------------------------------
+    LOG("INFO: Updating all examples metadata...\n");
+    int exListCount = 0;
+    rlExampleInfo *exList = LoadExampleData("ALL", true, &exListCount);
+    for (int i = 0; i < exListCount; i++)
+    {
+        rlExampleInfo *info = &exList[i];
+        UpdateSourceMetadata(TextFormat("%s/%s/%s.c", exBasePath, info->category, info->name), info);
+    }
+    UnloadExampleData(exList);
+    //------------------------------------------------------------------------------------------------
+
+    // Edit: raylib/examples/Makefile --> Update from collection
+    //------------------------------------------------------------------------------------------------
+    LOG("INFO: Updating raylib/examples/Makefile\n");
+    char *mkText = LoadFileText(TextFormat("%s/Makefile", exBasePath));
+    char *mkTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated Makefile copy, 2MB
+
+    int mkListStartIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_START");
+    int mkListEndIndex = TextFindIndex(mkText, "#EXAMPLES_LIST_END");
+
+    int mkIndex = 0;
+    memcpy(mkTextUpdated, mkText, mkListStartIndex);
+    mkIndex = sprintf(mkTextUpdated + mkListStartIndex, "#EXAMPLES_LIST_START\n");
+
+    for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
+    {
+        mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
+
+        int exCollectionCount = 0;
+        rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
+
+        for (int x = 0; x < exCollectionCount - 1; x++) mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("    %s/%s \\\n", exCollection[x].category, exCollection[x].name));
+        mkIndex += sprintf(mkTextUpdated + mkListStartIndex + mkIndex, TextFormat("    %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
+
+        UnloadExampleData(exCollection);
+    }
+
+    // Add the remaining part of the original file
+    memcpy(mkTextUpdated + mkListStartIndex + mkIndex - 1, mkText + mkListEndIndex, strlen(mkText) - mkListEndIndex);
+
+    // Save updated file
+    SaveFileText(TextFormat("%s/Makefile", exBasePath), mkTextUpdated);
+    UnloadFileText(mkText);
+    RL_FREE(mkTextUpdated);
+    //------------------------------------------------------------------------------------------------
+
+    // Edit: raylib/examples/Makefile.Web --> Update from collection
+    // NOTE: We avoid the "others" category on web building
+    //------------------------------------------------------------------------------------------------
+    LOG("INFO: Updating raylib/examples/Makefile.Web\n");
+    char *mkwText = LoadFileText(TextFormat("%s/Makefile.Web", exBasePath));
+    char *mkwTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated Makefile copy, 2MB
+
+    int mkwListStartIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_START");
+    int mkwListEndIndex = TextFindIndex(mkwText, "#EXAMPLES_LIST_END");
+
+    int mkwIndex = 0;
+    memcpy(mkwTextUpdated, mkwText, mkwListStartIndex);
+    mkwIndex = sprintf(mkwTextUpdated + mkwListStartIndex, "#EXAMPLES_LIST_START\n");
+
+    // NOTE: We avoid the "others" category on web building
+    for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
+    {
+        mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("%s = \\\n", TextToUpper(exCategories[i])));
+
+        int exCollectionCount = 0;
+        rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
+
+        for (int x = 0; x < exCollectionCount - 1; x++) mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("    %s/%s \\\n", exCollection[x].category, exCollection[x].name));
+        mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("    %s/%s\n\n", exCollection[exCollectionCount - 1].category, exCollection[exCollectionCount - 1].name));
+
+        UnloadExampleData(exCollection);
+    }
+
+    // Add examples individual targets, considering every example resources
+    // Some required makefile code...
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "# Default target entry\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO)\n\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "core: $(CORE)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shapes: $(SHAPES)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "textures: $(TEXTURES)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "text: $(TEXT)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "models: $(MODELS)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "shaders: $(SHADERS)\n");
+    mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "audio: $(AUDIO)\n\n");
+
+    // NOTE: We avoid the "others" category on web building
+    for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
+    {
+        mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, TextFormat("# Compile %s examples\n", TextToUpper(exCategories[i])));
+
+        int exCollectionCount = 0;
+        rlExampleInfo *exCollection = LoadExampleData(exCategories[i], true, &exCollectionCount);
+
+        for (int x = 0; x < exCollectionCount; x++)
+        {
+            // Scan resources used in example to list
+            int resPathCount = 0;
+            char **resPaths = LoadExampleResourcePaths(TextFormat("%s/%s/%s.c", exBasePath, exCollection[x].category, exCollection[x].name), &resPathCount);
+
+            if (resPathCount > 0)
+            {
+                /*
+                // WARNING: Compilation line starts with [TAB]
+                shaders/shaders_vertex_displacement: shaders/shaders_vertex_displacement.c
+                    $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \
+                    --preload-file shaders/resources/shaders/glsl100/vertex_displacement.vs@resources/shaders/glsl100/vertex_displacement.vs \
+                    --preload-file shaders/resources/shaders/glsl330/vertex_displacement.vs@resources/shaders/glsl330/vertex_displacement.vs \
+                    --preload-file shaders/resources/shaders/glsl100/vertex_displacement.fs@resources/shaders/glsl100/vertex_displacement.fs \
+                    --preload-file shaders/resources/shaders/glsl330/vertex_displacement.fs@resources/shaders/glsl330/vertex_displacement.fs
+                */
+                mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
+                    TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name));
+                mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "	$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \\\n");
+
+                for (int r = 0; r < resPathCount; r++)
+                {
+                    // WARNING: Special case to consider: shaders, resource paths could use conditions: "glsl%i"
+                    // In this case, we focus on web building for: glsl100
+                    if (TextFindIndex(resPaths[r], "glsl%i") > -1)
+                    {
+                        char *resPathUpdated = TextReplace(resPaths[r], "glsl%i", "glsl100");
+                        memset(resPaths[r], 0, 256);
+                        strcpy(resPaths[r], resPathUpdated);
+                        RL_FREE(resPathUpdated);
+                    }
+
+                    if (r < (resPathCount - 1))
+                    {
+                        mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
+                            TextFormat("    --preload-file %s/%s@%s \\\n", exCollection[x].category, resPaths[r], resPaths[r]));
+                    }
+                    else
+                    {
+                        mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
+                            TextFormat("    --preload-file %s/%s@%s\n\n", exCollection[x].category, resPaths[r], resPaths[r]));
+                    }
+                }
+            }
+            else  // Example does not require resources
+            {
+                /*
+                // WARNING: Compilation line starts with [TAB]
+                core/core_2d_camera: core/core_2d_camera.c
+                    $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)
+                */
+                mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex,
+                    TextFormat("%s/%s: %s/%s.c\n", exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name));
+                mkwIndex += sprintf(mkwTextUpdated + mkwListStartIndex + mkwIndex, "	$(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM)\n\n");
+            }
+
+            UnloadExampleResourcePaths(resPaths);
+        }
+
+        UnloadExampleData(exCollection);
+    }
+
+    // Add the remaining part of the original file
+    memcpy(mkwTextUpdated + mkwListStartIndex + mkwIndex - 1, mkwText + mkwListEndIndex, strlen(mkwText) - mkwListEndIndex);
+
+    // Save updated file
+    SaveFileText(TextFormat("%s/Makefile.Web", exBasePath), mkwTextUpdated);
+    UnloadFileText(mkwText);
+    RL_FREE(mkwTextUpdated);
+    //------------------------------------------------------------------------------------------------
+
+    // Edit: raylib/examples/README.md --> Update from collection
+    //------------------------------------------------------------------------------------------------
+    LOG("INFO: Updating raylib/examples/README.md\n");
+    // NOTE: Using [examples_list.txt] to update/regen README.md
+    // Lines format: | 01 | [core_basic_window](core/core_basic_window.c) | <img src="core/core_basic_window.png" alt="core_basic_window" width="80"> | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) |
+    char *mdText = LoadFileText(TextFormat("%s/README.md", exBasePath));
+    char *mdTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB
+
+    int mdListStartIndex = TextFindIndex(mdText, "## EXAMPLES COLLECTION");
+
+    int mdIndex = 0;
+    memcpy(mdTextUpdated, mdText, mdListStartIndex);
+
+    int exCollectionFullCount = 0;
+    rlExampleInfo *exCollectionFull = LoadExampleData("ALL", false, &exCollectionFullCount);
+    UnloadExampleData(exCollectionFull);
+
+    mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("## EXAMPLES COLLECTION [TOTAL: %i]\n", exCollectionFullCount));
+
+    // NOTE: We keep a global examples counter
+    for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES; i++)
+    {
+        int exCollectionCount = 0;
+        rlExampleInfo *exCollection = LoadExampleData(exCategories[i], false, &exCollectionCount);
+
+        // Every category includes some introductory text, as it is quite short, just copying it here
+        if (i == 0)         // "core"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: core [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib [core](../src/rcore.c) module platform functionality: window creation, inputs, drawing modes and system functionality.\n\n");
+        }
+        else if (i == 1)    // "shapes"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shapes [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib shapes drawing functionality, provided by raylib [shapes](../src/rshapes.c) module.\n\n");
+        }
+        else if (i == 2)    // "textures"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: textures [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib textures functionality, including image/textures loading/generation and drawing, provided by raylib [textures](../src/rtextures.c) module.\n\n");
+        }
+        else if (i == 3)    // "text"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: text [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib text functionality, including sprite fonts loading/generation and text drawing, provided by raylib [text](../src/rtext.c) module.\n\n");
+        }
+        else if (i == 4)    // "models"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: models [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib models functionality, including models loading/generation and drawing, provided by raylib [models](../src/rmodels.c) module.\n\n");
+        }
+        else if (i == 5)    // "shaders"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: shaders [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib shaders functionality, including shaders loading, parameters configuration and drawing using them (model shaders and postprocessing shaders). This functionality is directly provided by raylib [rlgl](../src/rlgl.c) module.\n\n");
+        }
+        else if (i == 6)    // "audio"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: audio [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples using raylib audio functionality, including sound/music loading and playing. This functionality is provided by raylib [raudio](../src/raudio.c) module. Note this module can be used standalone independently of raylib.\n\n");
+        }
+        else if (i == 7)    // "others"
+        {
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, TextFormat("\n### category: others [%i]\n\n", exCollectionCount));
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                "Examples showing raylib misc functionality that does not fit in other categories, like standalone modules usage or examples integrating external libraries.\n\n");
+        }
+
+        // Table header required
+        mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|  example  | image  | difficulty<br>level | version<br>created | last version<br>updated | original<br>developer |\n");
+        mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex, "|-----------|--------|:-------------------:|:------------------:|:-----------------------:|:----------------------|\n");
+
+        char starsText[16] = { 0 };
+        for (int x = 0; x < exCollectionCount; x++)
+        {
+            for (int s = 0; s < 4; s++)
+            {
+                if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual
+                else strcpy(starsText + 3*s, "☆");
+            }
+
+            mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+                TextFormat("| [%s](%s/%s.c) | <img src=\"%s/%s.png\" alt=\"%s\" width=\"80\"> | %s | %s | %s | [%s](https://github.com/%s) |\n",
+                    exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].category, exCollection[x].name, exCollection[x].name,
+                    starsText, exCollection[x].verCreated, exCollection[x].verUpdated, exCollection[x].author, exCollection[x].authorGitHub));
+        }
+
+        UnloadExampleData(exCollection);
+    }
+
+    mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+        "\nSome example missing? As always, contributions are welcome, feel free to send new examples!\n");
+    mdIndex += sprintf(mdTextUpdated + mdListStartIndex + mdIndex,
+        "Here is an [examples template](examples_template.c) with instructions to start with!\n");
+
+    // Save updated file
+    SaveFileText(TextFormat("%s/README.md", exBasePath), mdTextUpdated);
+    UnloadFileText(mdText);
+    RL_FREE(mdTextUpdated);
+    //------------------------------------------------------------------------------------------------
+
+    // Edit: raylib.com/common/examples.js --> Update from collection
+    // NOTE: Entries format: exampleEntry('⭐️☆☆☆' , 'core'    , 'basic_window'),
+    //------------------------------------------------------------------------------------------------
+    LOG("INFO: Updating raylib.com/common/examples.js\n");
+    char *jsText = LoadFileText(TextFormat("%s/../common/examples.js", exWebPath));
+    if (!jsText)
+    {
+        LOG("INFO: examples.js not found, skipping web examples list update\n");
+    }
+    else
+    {
+        int jsListStartIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_START");
+        int jsListEndIndex = TextFindIndex(jsText, "//EXAMPLE_DATA_LIST_END");
+        if ((jsListStartIndex < 0) || (jsListEndIndex < 0))
+        {
+            LOG("WARNING: examples.js markers not found, skipping update\n");
+            UnloadFileText(jsText);
+        }
+        else
+        {
+            char *jsTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1); // Updated examples.js copy, 2MB
+            int jsIndex = 0;
+            memcpy(jsTextUpdated, jsText, jsListStartIndex);
+            jsIndex = sprintf(jsTextUpdated + jsListStartIndex, "//EXAMPLE_DATA_LIST_START\n");
+            jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex, "    var exampleData = [\n");
+
+            char starsText[16] = { 0 };
+
+            // NOTE: We avoid "others" category
+            for (int i = 0; i < REXM_MAX_EXAMPLE_CATEGORIES - 1; i++)
+            {
+                int exCollectionCount = 0;
+                rlExampleInfo *exCollection = LoadExampleData(exCategories[i], false, &exCollectionCount);
+                for (int x = 0; x < exCollectionCount; x++)
+                {
+                    for (int s = 0; s < 4; s++)
+                    {
+                        if (s < exCollection[x].stars) strcpy(starsText + 3*s, "⭐️"); // WARNING: Different than '★', more visual
+                        else strcpy(starsText + 3*s, "☆");
+                    }
+
+                    if ((i == 6) && (x == (exCollectionCount - 1)))
+                    {
+                        // NOTE: Last line to add, special case to consider
+                        jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex,
+                            TextFormat("        exampleEntry('%s', '%s', '%s')];\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1));
+                    }
+                    else
+                    {
+                        jsIndex += sprintf(jsTextUpdated + jsListStartIndex + jsIndex,
+                            TextFormat("        exampleEntry('%s', '%s', '%s'),\n", starsText, exCollection[x].category, exCollection[x].name + strlen(exCollection[x].category) + 1));
+                    }
+                }
+
+                UnloadExampleData(exCollection);
+            }
+
+            // Add the remaining part of the original file
+            memcpy(jsTextUpdated + jsListStartIndex + jsIndex, jsText + jsListEndIndex, strlen(jsText) - jsListEndIndex);
+
+            // Save updated file
+            SaveFileText(TextFormat("%s/../common/examples.js", exWebPath), jsTextUpdated);
+            UnloadFileText(jsText);
+            RL_FREE(jsTextUpdated);
+        }
+    }
+    //------------------------------------------------------------------------------------------------
+
+    return result;
+}
+
+// Load examples information from collection data
+static rlExampleInfo *LoadExampleData(const char *filter, bool sort, int *exCount)
+{
+    #define MAX_EXAMPLES_INFO   256
+
+    rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(MAX_EXAMPLES_INFO, sizeof(rlExampleInfo));
+    int exCounter = 0;
+    *exCount = 0;
+
+    // Load main collection list file: "raylib/examples/examples_list.txt"
+    char *text = LoadFileText(exCollectionFilePath);
+
+    if (text != NULL)
+    {
+        int lineCount = 0;
+        char **lines = LoadTextLines(text, &lineCount);
+
+        for (int i = 0; i < lineCount; i++)
+        {
+            // Basic validation for lines start categories
+            if ((lines[i][0] != '#') &&
+                ((lines[i][0] == 'c') ||      // core
+                 (lines[i][0] == 's') ||      // shapes, shaders
+                 (lines[i][0] == 't') ||      // textures, text
+                 (lines[i][0] == 'm') ||      // models
+                 (lines[i][0] == 'a') ||      // audio
+                 (lines[i][0] == 'o')))       // TODO: Get others category?
+            {
+                rlExampleInfo info = { 0 };
+                int result = ParseExampleInfoLine(lines[i], &info);
+                if (result == 1) // Success on parsing
+                {
+                    if (strcmp(filter, "ALL") == 0)
+                    {
+                        // Add all examples to the list
+                        memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
+                        exCounter++;
+                    }
+                    else if (strcmp(info.category, filter) == 0)
+                    {
+                        // Get only specific category examples
+                        memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
+                        exCounter++;
+                    }
+                    else if (strcmp(info.name, filter) == 0)
+                    {
+                        // Get only requested example
+                        memcpy(&exInfo[exCounter], &info, sizeof(rlExampleInfo));
+                        exCounter++;
+                        break;
+                    }
+                }
+            }
+        }
+
+        UnloadTextLines(lines, lineCount);
+        UnloadFileText(text);
+    }
+
+    // Sorting required
+    if (sort) SortExampleByName(exInfo, exCounter);
+
+    *exCount = exCounter;
+    return exInfo;
+}
+
+// Unload examples collection data
+static void UnloadExampleData(rlExampleInfo *exInfo)
+{
+    RL_FREE(exInfo);
+}
+
+// Get example info from example file header
+// WARNING: Expecting the example to follow raylib_example_template.c
+static rlExampleInfo *LoadExampleInfo(const char *exFileName)
+{
+    rlExampleInfo *exInfo = NULL;
+
+    if (FileExists(exFileName) && IsFileExtension(exFileName, ".c"))
+    {
+        // Example found in collection
+        exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo));
+
+        strncpy(exInfo->name, GetFileNameWithoutExt(exFileName), 128 - 1);
+        strncpy(exInfo->category, exInfo->name, TextFindIndex(exInfo->name, "_"));
+
+        char *exText = LoadFileText(exFileName);
+
+        // Get example difficulty stars
+        // NOTE: Counting the unicode char occurrences: ★
+        int starsIndex = TextFindIndex(exText, "★");
+        if (starsIndex > 0)
+        {
+            const char *starPtr = exText + starsIndex;
+            while (*starPtr)
+            {
+                if (((unsigned char)starPtr[0] == 0xe2) &&
+                    ((unsigned char)starPtr[1] == 0x98) &&
+                    ((unsigned char)starPtr[2] == 0x85))
+                {
+                    exInfo->stars++;
+                    starPtr += 3; // Advance past multibyte character
+                }
+                else starPtr++;
+            }
+        }
+
+        // Get example create with raylib version
+        int verCreateIndex = TextFindIndex(exText, "created with raylib "); // Version = index + 20
+        int verCreatedLen = 0;
+        for (int i = verCreateIndex + 20; (exText[i] != ' ') && (exText[i] != '\n') && (exText[i] != ','); i++) verCreatedLen++;
+        if (verCreateIndex > 0) strncpy(exInfo->verCreated, exText + verCreateIndex + 20, verCreatedLen);
+        else strcpy(exInfo->verCreated, RAYLIB_VERSION); // Use current raylib version
+
+        // Get example update with raylib version
+        int verUpdateIndex = TextFindIndex(exText, "updated with raylib "); // Version = index + 20
+        int verUpdateLen = 0;
+        for (int i = verUpdateIndex + 20; (exText[i] != ' ') && (exText[i] != '\n') && (exText[i] != ','); i++) verUpdateLen++;
+        if (verUpdateIndex > 0) strncpy(exInfo->verUpdated, exText + verUpdateIndex + 20, verUpdateLen);
+        else strcpy(exInfo->verUpdated, RAYLIB_VERSION); // Use current raylib version
+
+        // Get example years created/reviewed and creator and github user
+        // NOTE: Using copyright line instead of "Example contributed by " because
+        // most examples do not contain that line --> TODO: Review examples header formating?
+        // Expected format: Copyright (c) <year_created>-<year_updated> <user_name> (@<user_github>)
+        // Alternatives:  Copyright (c) <year_created> <author_name> (@<user_github>) and <contrib_name> (@<contrib_user>)
+        int copyrightIndex = TextFindIndex(exText, "Copyright (c) ");
+        int yearStartIndex = copyrightIndex + 14;
+        char yearText[5] = { 0 };
+        strncpy(yearText, exText + yearStartIndex, 4);
+        exInfo->yearCreated = TextToInteger(yearText);
+        // Check for review year included (or just use creation year)
+        if (exText[yearStartIndex + 4] == '-') strncpy(yearText, exText + yearStartIndex + 5, 4);
+        exInfo->yearReviewed = TextToInteger(yearText);
+        int yearEndIndex = TextFindIndex(exText + yearStartIndex, " ");
+        int authorStartIndex = yearStartIndex + yearEndIndex + 1;
+        int authorEndIndex = TextFindIndex(exText + authorStartIndex, " (@");
+        if (authorEndIndex != -1) // Github user also available
+        {
+            authorEndIndex += authorStartIndex;
+            strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex);
+
+            // Get GitHub user
+            int userStartIndex = authorEndIndex + 3;
+            int userEndIndex = TextFindIndex(exText + userStartIndex, ")");
+            userEndIndex += userStartIndex;
+            strncpy(exInfo->authorGitHub, exText + userStartIndex, userEndIndex - userStartIndex);
+        }
+        else // GitHub user not found to set end, using '\n'
+        {
+            authorEndIndex = TextFindIndex(exText + authorStartIndex, "\n");
+            authorEndIndex += authorStartIndex;
+            strncpy(exInfo->author, exText + authorStartIndex, authorEndIndex - authorStartIndex);
+        }
+
+        UnloadFileText(exText);
+
+        exInfo->resPaths = LoadExampleResourcePaths(exFileName, &exInfo->resCount);
+    }
+
+    return exInfo;
+}
+
+// Unload example information
+static void UnloadExampleInfo(rlExampleInfo *exInfo)
+{
+    UnloadExampleResourcePaths(exInfo->resPaths);
+    RL_FREE(exInfo);
+}
+
+// raylib example line info parser
+// Parses following line format: core;core_basic_window;★☆☆☆;1.0;1.0;2013;2026;"Ray";@raysan5
+static int ParseExampleInfoLine(const char *line, rlExampleInfo *entry)
+{
+    #define MAX_EXAMPLE_INFO_LINE_LEN   512
+
+    char temp[MAX_EXAMPLE_INFO_LINE_LEN] = { 0 };
+    strncpy(temp, line, MAX_EXAMPLE_INFO_LINE_LEN);
+    temp[MAX_EXAMPLE_INFO_LINE_LEN - 1] = '\0'; // Ensure null termination
+
+    int tokenCount = 0;
+    char **tokens = TextSplit(line, ';', &tokenCount);
+
+    if (tokenCount != 9)
+    {
+        LOG("REXM: WARNING: Example collection line contains invalid number of tokens: %i\n", tokenCount);
+    }
+
+    // Get category and name
+    strcpy(entry->category, tokens[0]);
+    strcpy(entry->name, tokens[1]);
+
+    // Parsing stars
+    // NOTE: Counting the unicode char occurrences: ★
+    const char *starPtr = tokens[2];
+    while (*starPtr)
+    {
+        if (((unsigned char)starPtr[0] == 0xe2) &&
+            ((unsigned char)starPtr[1] == 0x98) &&
+            ((unsigned char)starPtr[2] == 0x85))
+        {
+            entry->stars++;
+            starPtr += 3; // Advance past multibyte character
+        }
+        else starPtr++;
+    }
+
+    // Get raylib creation/update versions
+    strcpy(entry->verCreated, tokens[3]);
+    strcpy(entry->verUpdated, tokens[4]);
+
+    // Get year created and year reviewed
+    entry->yearCreated = TextToInteger(tokens[5]);
+    entry->yearReviewed = TextToInteger(tokens[6]);
+
+    // Get author and github
+    if (tokens[7][0] == '"') tokens[7] += 1;
+    if (tokens[7][strlen(tokens[7]) - 1] == '"') tokens[7][strlen(tokens[7]) - 1] = '\0';
+    strcpy(entry->author, tokens[7]);
+    strcpy(entry->authorGitHub, tokens[8] + 1); // Skip '@'
+
+    return 1;
+}
+
+// Text compare, required for qsort() function
+static int rlExampleInfoCompare(const void *a, const void *b)
+{
+    const rlExampleInfo *ex1 = (const rlExampleInfo *)a;
+    const rlExampleInfo *ex2 = (const rlExampleInfo *)b;
+
+    return strcmp(ex1->name, ex2->name);
+}
+
+// Sort array of strings by name
+// WARNING: items[] pointers are reorganized
+static void SortExampleByName(rlExampleInfo *items, int count)
+{
+    qsort(items, count, sizeof(rlExampleInfo), rlExampleInfoCompare);
+}
+
+// Scan resource paths in example file
+// WARNING: Supported resource file extensions is hardcoded by used file types
+// but new examples could require other file extensions to be added,
+// maybe it should look for '.xxx")' patterns instead
+// TODO: WARNING: Some resources could require linked resources: .fnt --> .png, .mtl --> .png, .gltf --> .png, ...
+static char **LoadExampleResourcePaths(const char *filePath, int *resPathCount)
+{
+    #define REXM_MAX_RESOURCE_PATH_LEN    256
+
+    char **paths = (char **)RL_CALLOC(REXM_MAX_RESOURCE_PATHS, sizeof(char **));
+    for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) paths[i] = (char *)RL_CALLOC(REXM_MAX_RESOURCE_PATH_LEN, sizeof(char));
+
+    int resCounter = 0;
+    char *code = LoadFileText(filePath);
+
+    if (code != NULL)
+    {
+        // Resources extensions to check
+        const char *exts[] = { ".png", ".bmp", ".jpg", ".qoi", ".gif", ".raw", ".hdr", ".ttf", ".fnt", ".wav", ".ogg", ".mp3", ".flac", ".mod", ".qoa", ".obj", ".iqm", ".glb", ".m3d", ".vox", ".vs", ".fs", ".txt" };
+        const int extCount = sizeof(exts)/sizeof(char *);
+
+        char *ptr = code;
+        while ((ptr = strchr(ptr, '"')) != NULL)
+        {
+            char *start = ptr + 1;
+            char *end = strchr(start, '"');
+            if (!end) break;
+
+            // WARNING: Some paths could be for saving files, not loading, those "resource" files must be omitted
+            // TODO: HACK: Just check previous position from pointer for function name including the string and the index "distance"
+            // This is a quick solution, the good one would be getting the data loading function names...
+            int functionIndex01 = TextFindIndex(ptr - 40, "ExportImage");       // Check ExportImage()
+            int functionIndex02 = TextFindIndex(ptr - 10, "TraceLog");          // Check TraceLog()
+            int functionIndex03 = TextFindIndex(ptr - 40, "TakeScreenshot");    // Check TakeScreenshot()
+            int functionIndex04 = TextFindIndex(ptr - 40, "SaveFileData");      // Check SaveFileData()
+            int functionIndex05 = TextFindIndex(ptr - 40, "SaveFileText");      // Check SaveFileText()
+
+            if (!((functionIndex01 != -1) && (functionIndex01 < 40)) &&  // Not found ExportImage() before ""
+                !((functionIndex02 != -1) && (functionIndex02 < 10)) &&  // Not found TraceLog() before ""
+                !((functionIndex03 != -1) && (functionIndex03 < 40)) &&  // Not found TakeScreenshot() before ""
+                !((functionIndex04 != -1) && (functionIndex04 < 40)) &&  // Not found TakeScreenshot() before ""
+                !((functionIndex05 != -1) && (functionIndex05 < 40)))    // Not found SaveFileText() before ""
+            {
+                int len = (int)(end - start);
+                if ((len > 0) && (len < REXM_MAX_RESOURCE_PATH_LEN))
+                {
+                    char buffer[REXM_MAX_RESOURCE_PATH_LEN] = { 0 };
+                    strncpy(buffer, start, len);
+                    buffer[len] = '\0';
+
+                    // Check for known extensions
+                    for (int i = 0; i < extCount; i++)
+                    {
+                        // NOTE: IsFileExtension() expects a NULL terminated fileName string,
+                        // it looks for the last '.' and checks "extension" after that
+                        if (IsFileExtension(buffer, exts[i]))
+                        {
+                            // Avoid duplicates
+                            bool found = false;
+                            for (int j = 0; j < resCounter; j++)
+                            {
+                                if (strcmp(paths[j], buffer) == 0) { found = true; break; }
+                            }
+
+                            if (!found && (resCounter < REXM_MAX_RESOURCE_PATHS))
+                            {
+                                strcpy(paths[resCounter], buffer);
+                                resCounter++;
+                            }
+
+                            break;
+                        }
+                    }
+                }
+            }
+
+            ptr = end + 1;
+        }
+
+        UnloadFileText(code);
+    }
+
+    *resPathCount = resCounter;
+    return paths;
+}
+
+// Clear resource paths scanned
+static void UnloadExampleResourcePaths(char **resPaths)
+{
+    for (int i = 0; i < REXM_MAX_RESOURCE_PATHS; i++) RL_FREE(resPaths[i]);
+
+    RL_FREE(resPaths);
+}
+
+// Add VS project (.vcxproj) to existing VS solution (.sln)
+// WARNING: Adding a .vcxproj to .sln can not be automated with:
+//  - "dotnet" tool (C# projects only)
+//  - "devenv" tool (no adding support, only building)
+// It must be done manually editing the .sln file
+static int AddVSProjectToSolution(const char *slnFile, const char *projFile, const char *category)
+{
+    int result = 0;
+
+    // WARNING: Function uses extensively TextFormat(),
+    // *projFile ptr could be overwriten after a while -> Use copied string
+
+    // Generate unique UUID
+    const char *uuid = GenerateUUIDv4();
+
+    // Replace default UUID (core_basic_window) on project file by new one
+    FileTextReplace(projFile, "0981CA98-E4A5-4DF1-987F-A41D09131EFC", uuid);
+
+    char *slnText = LoadFileText(slnFile);
+    char *slnTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+    // Add project to solution
+    //----------------------------------------------------------------------------------------
+    // Format: Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "<project_name>", "examples\<project_name>.vcxproj", "{<project_uuid>}"
+    // NOTE: Find a position to insert new project: At the end of the projects list, same strategy as VS2022 "Add Project"
+    int prjStartIndex = TextFindIndex(slnText, "Global");
+
+    // Add new project info
+    // WARNING: UUID can actually be duplicated and it still works...
+    strncpy(slnTextUpdated, slnText, prjStartIndex);
+    int offsetIndex = sprintf(slnTextUpdated + prjStartIndex,
+        TextFormat("Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"%s\", \"examples\\%s\", \"{%s}\"\n",
+            GetFileNameWithoutExt(projFile), GetFileName(projFile), uuid));
+    offsetIndex += prjStartIndex;
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "EndProject\n");
+    //----------------------------------------------------------------------------------------
+
+    // Update project config
+    //----------------------------------------------------------------------------------------
+    // Find position to add project config: At the end of global section, same strategy as VS2022 "Add Project"
+    int projConfStartIndex = TextFindIndex(slnText, "GlobalSection(ProjectConfigurationPlatforms) = postSolution");
+    strncpy(slnTextUpdated + offsetIndex, slnText + prjStartIndex, projConfStartIndex - prjStartIndex);
+    offsetIndex += (projConfStartIndex - prjStartIndex);
+
+    int projConfEndIndex = TextFindIndex(slnText + projConfStartIndex, "EndGlobalSection");
+    projConfEndIndex += projConfStartIndex;
+
+    strncpy(slnTextUpdated + offsetIndex, slnText + projConfStartIndex, projConfEndIndex - projConfStartIndex);
+    offsetIndex += (projConfEndIndex - projConfStartIndex);
+
+    // Add project config lines
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t{%s}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x64.Build.0 = Debug.DLL|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.ActiveCfg = Debug|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|ARM64.Build.0 = Debug|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.ActiveCfg = Debug|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x64.Build.0 = Debug|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.ActiveCfg = Debug|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Debug|x86.Build.0 = Debug|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.ActiveCfg = Release.DLL|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x64.Build.0 = Release.DLL|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release.DLL|x86.Build.0 = Release.DLL|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.ActiveCfg = Release|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|ARM64.Build.0 = Release|ARM64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.ActiveCfg = Release|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x64.Build.0 = Release|x64\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.ActiveCfg = Release|Win32\n", uuid));
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, TextFormat("\t\t{%s}.Release|x86.Build.0 = Release|Win32\n", uuid));
+    // Write next section directly to avoid copy logic
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tEndGlobalSection\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tGlobalSection(SolutionProperties) = preSolution\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\t\tHideSolutionNode = FALSE\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tEndGlobalSection\n\t");
+    //----------------------------------------------------------------------------------------
+
+    // Place project to explorer folder
+    //----------------------------------------------------------------------------------------
+    // Find position to add project folder: At the end of global section, same strategy as VS2022 "Add Project"
+    int projFolderStartIndex = TextFindIndex(slnText, "GlobalSection(NestedProjects) = preSolution");
+    int projFolderEndIndex = TextFindIndex(slnText + projFolderStartIndex, "\tEndGlobalSection");
+    projFolderEndIndex += projFolderStartIndex;
+
+    strncpy(slnTextUpdated + offsetIndex, slnText + projFolderStartIndex, projFolderEndIndex - projFolderStartIndex);
+    offsetIndex += (projFolderEndIndex - projFolderStartIndex);
+
+    // Add project folder line
+    // NOTE: Folder uuid depends on category
+    if (strcmp(category, "core") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}\n", uuid));
+    else if (strcmp(category, "shapes") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {278D8859-20B1-428F-8448-064F46E1F021}\n", uuid));
+    else if (strcmp(category, "textures") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {DA049009-21FF-4AC0-84E4-830DD1BCD0CE}\n", uuid));
+    else if (strcmp(category, "text") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A}\n", uuid));
+    else if (strcmp(category, "models") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C}\n", uuid));
+    else if (strcmp(category, "shaders") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}\n", uuid));
+    else if (strcmp(category, "audio") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}\n", uuid));
+    else if (strcmp(category, "other") == 0) offsetIndex += sprintf(slnTextUpdated + offsetIndex, 
+        TextFormat("\t\t{%s} = {E9D708A5-9C1F-4B84-A795-C5F191801762}\n", uuid));
+    else LOG("WARNING: Provided category is not valid: %s\n", category);
+    //----------------------------------------------------------------------------------------
+
+    // Write end of file, no need to copy from original file
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tEndGlobalSection\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tGlobalSection(ExtensibilityGlobals) = postSolution\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\t\tSolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29}\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "\tEndGlobalSection\n");
+    offsetIndex += sprintf(slnTextUpdated + offsetIndex, "EndGlobal\n");
+
+    SaveFileText(slnFile, slnTextUpdated);
+    UnloadFileText(slnText);
+    RL_FREE(slnTextUpdated);
+
+    return result;
+}
+
+// Remove VS project (.vcxproj) to existing VS solution (.sln)
+static int RemoveVSProjectFromSolution(const char *slnFile, const char *exName)
+{
+    int result = 0;
+
+    // Lines to be removed from solution file:
+    //Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_random_values", "examples\core_random_values.vcxproj", "{B332DCA8-3599-4A99-917A-82261BDC27AC}"
+    //EndProject
+    // All lines starting with:
+    //"\t\t{B332DCA8-3599-4A99-917A-82261BDC27AC}."
+
+    char *slnText = LoadFileText(slnFile);
+    char *slnTextUpdated = (char *)RL_CALLOC(REXM_MAX_BUFFER_SIZE, 1);
+
+    int lineCount = 0;
+    char **lines = LoadTextLines(slnText, &lineCount); // WARNING: Max 512 lines, we need +4000!
+
+    char uuid[38] = { 0 };
+    strcpy(uuid, "ABCDEF00-0123-4567-89AB-000000000012"); // Temp value
+    int textUpdatedOfsset = 0;
+    int exNameLen = (int)strlen(exName);
+
+    for (int i = 0, index = 0; i < lineCount; i++)
+    {
+        index = TextFindIndex(lines[i], exName);
+        if (index > 0)
+        {
+            // Found line with project --> get UUID
+            strncpy(uuid, lines[i] + index + exNameLen*2 + 26, 36);
+
+            // Skip copying line and also next one
+            i++;
+        }
+        else
+        {
+            if (TextFindIndex(lines[i], uuid) == -1)
+                textUpdatedOfsset += sprintf(slnTextUpdated + textUpdatedOfsset, "%s\n", lines[i]);
+        }
+    }
+
+    SaveFileText(slnFile, slnTextUpdated);
+
+    UnloadTextLines(lines, lineCount);
+    UnloadFileText(slnText);
+    RL_FREE(slnTextUpdated);
+
+    return result;
+}
+
+// Generate unique UUID v4 string
+// Output format: {9A2F48CC-0DA8-47C0-884E-02E37F9BE6C1}
+static const char *GenerateUUIDv4(void)
+{
+    static char uuid[38] = { 0 };
+    memset(uuid, 0, 38);
+
+    unsigned char bytes[16] = { 0 };
+
+    for (int i = 0; i < 16; i++) bytes[i] = (unsigned char)GetRandomValue(0, 255);
+
+    // Set version (4) and variant (RFC 4122)
+    bytes[6] = (bytes[6] & 0x0F) | 0x40; // Version
+    bytes[8] = (bytes[8] & 0x3F) | 0x80; // Variant
+
+    snprintf(uuid, 38,
+        "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
+        bytes[0], bytes[1], bytes[2], bytes[3],
+        bytes[4], bytes[5],
+        bytes[6], bytes[7],
+        bytes[8], bytes[9],
+        bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
+
+    return uuid;
+}
+
+// Update source code header and comments metadata
+static void UpdateSourceMetadata(const char *exSrcPath, const rlExampleInfo *info)
+{
+    if (FileExists(exSrcPath) && IsFileExtension(exSrcPath, ".c") && (!TextIsEqual(info->category, "others")))
+    {
+        // WARNING: Cache a copy of exSrcPath to avoid modifications by TextFormat()
+        char exSourcePath[512] = { 0 };
+        strcpy(exSourcePath, exSrcPath);
+
+        char *exText = LoadFileText(exSourcePath);
+        char *exTextUpdated[6] = { 0 };     // Pointers to multiple updated text versions
+        char *exTextUpdatedPtr = exText;    // Pointer to current valid text version
+
+        char exNameFormated[256] = { 0 };   // Example name without category and using spaces
+        int exNameIndex = TextFindIndex(info->name, "_");
+        strcpy(exNameFormated, info->name + exNameIndex + 1);
+        int exNameLen = (int)strlen(exNameFormated);
+        for (int i = 0; i < exNameLen; i++) { if (exNameFormated[i] == '_') exNameFormated[i] = ' '; }
+
+        // Update example header title (line #3 - ALWAYS)
+        // String: "*   raylib [shaders] example - texture drawing"
+        exTextUpdated[0] = TextReplaceBetween(exTextUpdatedPtr, "*   raylib [", "\n",
+            TextFormat("%s] example - %s", info->category, exNameFormated));
+        if (exTextUpdated[0] != NULL) exTextUpdatedPtr = exTextUpdated[0];
+
+        // Update example complexity rating
+        // String: "*   Example complexity rating: [★★☆☆] 2/4"
+        // Get example difficulty stars text
+        char starsText[16] = { 0 };
+        for (int i = 0; i < 4; i++)
+        {
+            // NOTE: Every UTF-8 star are 3 bytes
+            if (i < info->stars) strcpy(starsText + 3*i, "★");
+            else strcpy(starsText + 3*i, "☆");
+        }
+        exTextUpdated[1] = TextReplaceBetween(exTextUpdatedPtr, "*   Example complexity rating: [", "/4\n",
+            TextFormat("%s] %i", starsText, info->stars));
+        if (exTextUpdated[1] != NULL) exTextUpdatedPtr = exTextUpdated[1];
+
+        // Update example creation/update raylib versions
+        // String: "*   Example originally created with raylib 2.0, last time updated with raylib 3.7
+        exTextUpdated[2] = TextReplaceBetween(exTextUpdatedPtr, "*   Example originally created with raylib ", "\n",
+            TextFormat("%s, last time updated with raylib %s", info->verCreated, info->verUpdated));
+        if (exTextUpdated[2] != NULL) exTextUpdatedPtr = exTextUpdated[2];
+
+        // Update copyright message
+        // String: "*   Copyright (c) 2019-2026 Contributor Name (@github_user) and Ramon Santamaria (@raysan5)"
+        if (info->yearCreated == info->yearReviewed)
+        {
+            exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")",
+                TextFormat("%i %s (@%s", info->yearCreated, info->author, info->authorGitHub));
+            if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3];
+        }
+        else
+        {
+            exTextUpdated[3] = TextReplaceBetween(exTextUpdatedPtr, "Copyright (c) ", ")",
+                TextFormat("%i-%i %s (@%s", info->yearCreated, info->yearReviewed, info->author, info->authorGitHub));
+            if (exTextUpdated[3] != NULL) exTextUpdatedPtr = exTextUpdated[3];
+        }
+
+        // Update window title
+        // String: "InitWindow(screenWidth, screenHeight, "raylib [shaders] example - texture drawing");"
+        exTextUpdated[4] = TextReplaceBetween(exTextUpdated[3], "InitWindow(screenWidth, screenHeight, \"", "\");",
+            TextFormat("raylib [%s] example - %s", info->category, exNameFormated));
+        if (exTextUpdated[4] != NULL) exTextUpdatedPtr = exTextUpdated[4];
+
+        // Update contributors names
+        // String: "*   Example contributed by Contributor Name (@github_user) and reviewed by Ramon Santamaria (@raysan5)"
+        // WARNING: Not all examples are contributed by someone, so the result of this replace can be NULL (string not found)
+        exTextUpdated[5] = TextReplaceBetween(exTextUpdatedPtr, "*   Example contributed by ", ")",
+            TextFormat("%s (@%s", info->author, info->authorGitHub));
+        if (exTextUpdated[5] != NULL) exTextUpdatedPtr = exTextUpdated[5];
+
+        if (exTextUpdatedPtr != NULL) SaveFileText(exSourcePath, exTextUpdatedPtr);
+
+        for (int i = 0; i < 6; i++) { MemFree(exTextUpdated[i]); exTextUpdated[i] = NULL; }
+
+        UnloadFileText(exText);
+    }
+}
+
+// Update generated Web example .html file metadata
+static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath)
+{
+    if (FileExists(exHtmlPath) && IsFileExtension(exHtmlPath, ".html"))
+    {
+        // WARNING: Cache a copy of exHtmlPath to avoid modifications by TextFormat()
+        char exHtmlPathCopy[512] = { 0 };
+        strcpy(exHtmlPathCopy, exHtmlPath);
+
+        char *exHtmlText = LoadFileText(exHtmlPathCopy);
+        char *exHtmlTextUpdated[6] = { 0 }; // Pointers to multiple updated text versions
+
+        char exName[64] = { 0 };            // Example name: fileName without extension
+        char exCategory[16] = { 0 };        // Example category: core, shapes, text, textures, models, audio, shaders
+        char exDescription[256] = { 0 };    // Example description: example text line #3
+        char exTitle[64] = { 0 };           // Example title: fileName without extension, replacing underscores by spaces
+
+        // Get example name: replace underscore by spaces
+        strncpy(exName, GetFileNameWithoutExt(exHtmlPathCopy), 64 - 1);
+        strcpy(exTitle, exName);
+        for (int i = 0; (i < 64) && (exTitle[i] != '\0'); i++) { if (exTitle[i] == '_') exTitle[i] = ' '; }
+
+        // Get example category from exName: copy until first underscore
+        for (int i = 0; (exName[i] != '_'); i++) exCategory[i] = exName[i];
+
+        // Get example description: copy line #3 from example file
+        char *exText = LoadFileText(exFilePath);
+        int lineCount = 0;
+        char **lines = LoadTextLines(exText, &lineCount);
+        int lineLength = (int)strlen(lines[2]);
+        strncpy(exDescription, lines[2] + 4, lineLength - 4);
+        UnloadTextLines(lines, lineCount);
+        UnloadFileText(exText);
+
+        // Update example.html required text
+        exHtmlTextUpdated[0] = TextReplace(exHtmlText, "raylib web game", exTitle);
+        exHtmlTextUpdated[1] = TextReplace(exHtmlTextUpdated[0], "New raylib web videogame, developed using raylib videogames library", exDescription);
+        exHtmlTextUpdated[2] = TextReplace(exHtmlTextUpdated[1], "https://www.raylib.com/common/raylib_logo.png",
+            TextFormat("https://raw.githubusercontent.com/raysan5/raylib/master/examples/%s/%s.png", exCategory, exName));
+        exHtmlTextUpdated[3] = TextReplace(exHtmlTextUpdated[2], "https://www.raylib.com/games.html",
+            TextFormat("https://www.raylib.com/examples/%s/%s.html", exCategory, exName));
+        exHtmlTextUpdated[4] = TextReplace(exHtmlTextUpdated[3], "raylib - example", TextFormat("raylib - %s", exName)); // og:site_name
+        exHtmlTextUpdated[5] = TextReplace(exHtmlTextUpdated[4], "https://github.com/raysan5/raylib",
+            TextFormat("https://github.com/raysan5/raylib/blob/master/examples/%s/%s.c", exCategory, exName));
+
+        SaveFileText(exHtmlPathCopy, exHtmlTextUpdated[5]);
+
+        //LOG("INFO: [%s] Updated successfully\n",files.paths[i]);
+        //LOG("      - Name / Title: %s / %s\n", exName, exTitle);
+        //LOG("      - Description:  %s\n", exDescription);
+        //LOG("      - URL:          %s\n", TextFormat("https://www.raylib.com/examples/%s/%s.html", exCategory, exName));
+        //LOG("      - URL Source:   %s\n", TextFormat("https://github.com/raysan5/raylib/blob/master/examples/%s/%s.c", exCategory, exName));
+
+        for (int i = 0; i < 6; i++) { MemFree(exHtmlTextUpdated[i]); exHtmlTextUpdated[i] = NULL; }
+
+        UnloadFileText(exHtmlText);
+    }
+}
+
+// Check if text string is a list of strings
+static int GetTextListIndex(const char *text, const char **list, int listCount)
+{
+    int result = -1;
+
+    for (int i = 0; i < listCount; i++)
+    {
+        if (TextIsEqual(text, list[i])) { result = i; break; }
+    }
+
+    return result;
+}
+
+#if defined(__GNUC__) // GCC and Clang
+    #pragma GCC diagnostic pop
+#endif
diff --git a/raylib/tools/rlparser/rlparser.c b/raylib/tools/rlparser/rlparser.c
new file mode 100644
--- /dev/null
+++ b/raylib/tools/rlparser/rlparser.c
@@ -0,0 +1,2013 @@
+/**********************************************************************************************
+
+    rlparser - raylib header API parser, extracts API information as separate tokens
+
+    This parser scans raylib.h to get API information about defines, structs, aliases, enums, callbacks and functions.
+    All data is divided into pieces, usually as strings. The following types are used for data:
+     - struct DefineInfo
+     - struct StructInfo
+     - struct AliasInfo
+     - struct EnumInfo
+     - struct FunctionInfo
+     
+    WARNING: This parser is specifically designed to work with raylib.h, and has some contraints
+    in that regards. Still, it can also work with other header files that follow same file structure
+    conventions as raylib.h: rlgl.h, raymath.h, raygui.h, reasings.h 
+
+    CONSTRAINTS:
+    This parser is specifically designed to work with raylib.h, so, it has some constraints:
+
+     - Functions are expected as a single line with the following structure:
+       <retType> <name>(<paramType[0]> <paramName[0]>, <paramType[1]> <paramName[1]>);  <desc>
+
+       WARNING: Be careful with functions broken into several lines, it breaks the process!
+
+     - Structures are expected as several lines with the following form:
+       <desc>
+       typedef struct <name> {
+           <fieldType[0]> <fieldName[0]>;  <fieldDesc[0]>
+           <fieldType[1]> <fieldName[1]>;  <fieldDesc[1]>
+           <fieldType[2]> <fieldName[2]>;  <fieldDesc[2]>
+       } <name>;
+
+     - Enums are expected as several lines with the following form:
+       <desc>
+       typedef enum {
+           <valueName[0]> = <valueInteger[0]>, <valueDesc[0]>
+           <valueName[1]>,
+           <valueName[2]>, <valueDesc[2]>
+           <valueName[3]>  <valueDesc[3]>
+       } <name>;
+
+       NOTE: Multiple options are supported for enums:
+          - If value is not provided, (<valueInteger[i -1]> + 1) is assigned
+          - Value description can be provided or not
+
+    OTHER NOTES:
+     - This parser could work with other C header files if mentioned constraints are followed.
+     - This parser does not require <string.h> library, all data is parsed directly from char buffers.
+
+    LICENSE: zlib/libpng
+
+    raylib-parser is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+    BSD-like license that allows static linking with closed source software:
+
+    Copyright (c) 2021-2026 Ramon Santamaria (@raysan5)
+
+**********************************************************************************************/
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <stdlib.h>             // Required for: malloc(), calloc(), realloc(), free(), atoi(), strtol()
+#include <stdio.h>              // Required for: printf(), fopen(), fseek(), ftell(), fread(), fclose()
+#include <stdbool.h>            // Required for: bool
+#include <ctype.h>              // Required for: isdigit()
+
+#define MAX_DEFINES_TO_PARSE    2048    // Maximum number of defines to parse
+#define MAX_STRUCTS_TO_PARSE      64    // Maximum number of structures to parse
+#define MAX_ALIASES_TO_PARSE      64    // Maximum number of aliases to parse
+#define MAX_ENUMS_TO_PARSE        64    // Maximum number of enums to parse
+#define MAX_CALLBACKS_TO_PARSE    64    // Maximum number of callbacks to parse
+#define MAX_FUNCS_TO_PARSE      1024    // Maximum number of functions to parse
+
+#define MAX_LINE_LENGTH         1024    // Maximum length of one line (including comments)
+
+#define MAX_STRUCT_FIELDS         64    // Maximum number of struct fields
+#define MAX_ENUM_VALUES          512    // Maximum number of enum values
+#define MAX_FUNCTION_PARAMETERS   12    // Maximum number of function parameters
+
+//----------------------------------------------------------------------------------
+// Types and Structures Definition
+//----------------------------------------------------------------------------------
+
+// Define value type
+typedef enum {
+    UNKNOWN = 0,
+    MACRO,
+    GUARD,
+    INT,
+    INT_MATH,
+    LONG,
+    LONG_MATH,
+    FLOAT,
+    FLOAT_MATH,
+    DOUBLE,
+    DOUBLE_MATH,
+    CHAR,
+    STRING,
+    COLOR
+} DefineType;
+
+// Define info data
+typedef struct DefineInfo {
+    char name[64];              // Define name
+    int type;                   // Define type: enum DefineType
+    char value[256];            // Define value
+    char desc[128];             // Define description
+    bool isHex;                 // Define is hex number (for types INT, LONG)
+} DefineInfo;
+
+// Struct info data
+typedef struct StructInfo {
+    char name[64];              // Struct name
+    char desc[128];             // Struct type description
+    int fieldCount;             // Number of fields in the struct
+    char fieldType[MAX_STRUCT_FIELDS][64];     // Field type
+    char fieldName[MAX_STRUCT_FIELDS][64];     // Field name
+    char fieldDesc[MAX_STRUCT_FIELDS][128];    // Field description
+} StructInfo;
+
+// Alias info data
+typedef struct AliasInfo {
+    char type[64];              // Alias type
+    char name[64];              // Alias name
+    char desc[128];             // Alias description
+} AliasInfo;
+
+// Enum info data
+typedef struct EnumInfo {
+    char name[64];              // Enum name
+    char desc[128];             // Enum description
+    int valueCount;             // Number of values in enumerator
+    char valueName[MAX_ENUM_VALUES][64];    // Value name definition
+    int valueInteger[MAX_ENUM_VALUES];      // Value integer
+    char valueDesc[MAX_ENUM_VALUES][128];   // Value description
+} EnumInfo;
+
+// Function info data
+typedef struct FunctionInfo {
+    char name[64];              // Function name
+    char desc[512];             // Function description (comment at the end)
+    char retType[32];           // Return value type
+    int paramCount;             // Number of function parameters
+    char paramType[MAX_FUNCTION_PARAMETERS][32];   // Parameters type
+    char paramName[MAX_FUNCTION_PARAMETERS][32];   // Parameters name
+    char paramDesc[MAX_FUNCTION_PARAMETERS][128];  // Parameters description
+} FunctionInfo;
+
+// Output format for parsed data
+typedef enum { DEFAULT = 0, JSON, XML, LUA, CODE } OutputFormat;
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static int defineCount = 0;
+static int structCount = 0;
+static int aliasCount = 0;
+static int enumCount = 0;
+static int callbackCount = 0;
+static int funcCount = 0;
+static DefineInfo *defines = NULL;
+static StructInfo *structs = NULL;
+static AliasInfo *aliases = NULL;
+static EnumInfo *enums = NULL;
+static FunctionInfo *callbacks = NULL;
+static FunctionInfo *funcs = NULL;
+
+// Command line variables
+static char apiDefine[32] = { 0 };         // Functions define (i.e. RLAPI for raylib.h, RMDEF for raymath.h, etc.)
+static char truncAfter[32] = { 0 };        // Truncate marker (i.e. "RLGL IMPLEMENTATION" for rlgl.h)
+static int outputFormat = DEFAULT;
+
+// NOTE: Filename max length depends on OS, in Windows MAX_PATH = 256
+static char inFileName[512] = { 0 };        // Input file name (required in case of drag & drop over executable)
+static char outFileName[512] = { 0 };       // Output file name (required for file save/export)
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+static void ShowCommandLineInfo(void);                      // Show command line usage info
+static void ProcessCommandLine(int argc, char *argv[]);     // Process command line input
+
+static char *LoadFileText(const char *fileName, int *length); // Load text file - UnloadFileText() required!
+static void UnloadFileText(char *text);                     // Unload text data
+static char **LoadTextLines(const char *buffer, int length, int *lineCount); // Load all lines from a text buffer (expecting lines ending with '\n') - UnloadTextLines() required
+static void UnloadTextLines(char **lines, int lineCount);   // Unload text lines data
+static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name); // Get data type and name from a string containing both (i.e function param and struct fields)
+static void GetDescription(const char *source, char *description); // Get description comment from a line, do nothing if no comment in line
+static void MoveArraySize(char *name, char *type);          // Move array size from name to type
+static unsigned int TextLength(const char *text);           // Get text length in bytes, check for \0 character
+static bool IsTextEqual(const char *text1, const char *text2, unsigned int count);
+static int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string
+static void MemoryCopy(void *dest, const void *src, unsigned int count); // Memory copy, memcpy() replacement to avoid <string.h>
+static char *EscapeBackslashes(char *text);                 // Replace '\' by "\\" when exporting to JSON and XML
+static const char *StrDefineType(DefineType type);          // Get string of define type
+
+static void ExportParsedData(const char *fileName, int format); // Export parsed data in desired format
+
+//----------------------------------------------------------------------------------
+// Program main entry point
+//----------------------------------------------------------------------------------
+int main(int argc, char* argv[])
+{
+    if (argc > 1) ProcessCommandLine(argc, argv);
+
+    const char *raylibhPath = "../src/raylib.h\0";
+    const char *raylibapiPath = "raylib_api.txt\0";
+    const char *rlapiPath = "RLAPI\0";
+    if (inFileName[0] == '\0') MemoryCopy(inFileName, raylibhPath, TextLength(raylibhPath) + 1);
+    if (outFileName[0] == '\0') MemoryCopy(outFileName, raylibapiPath, TextLength(raylibapiPath) + 1);
+    if (apiDefine[0] == '\0') MemoryCopy(apiDefine, rlapiPath, TextLength(rlapiPath) + 1);
+
+    int length = 0;
+    char *buffer = LoadFileText(inFileName, &length);
+
+    if (buffer == NULL)
+    {
+        printf("Could not read input file: %s\n", inFileName);
+        return 1;
+    }
+
+    // Preprocess buffer to get separate lines
+    // NOTE: LoadTextLines() also removes leading spaces/tabs
+    int lineCount = 0;
+    char **lines = LoadTextLines(buffer, length, &lineCount);
+
+    // Truncate lines (if required)
+    if (truncAfter[0] != '\0')
+    {
+        int newCount = -1;
+        for (int i = 0; i < lineCount; i++)
+        {
+            if (newCount > -1) free(lines[i]);
+            else if (TextFindIndex(lines[i], truncAfter) > -1) newCount = i;
+        }
+        if (newCount > -1) lineCount = newCount;
+        printf("Number of truncated text lines: %i\n", lineCount);
+    }
+
+    // Defines line indices
+    int *defineLines = (int *)malloc(MAX_DEFINES_TO_PARSE*sizeof(int));
+
+    // Structs line indices
+    int *structLines = (int *)malloc(MAX_STRUCTS_TO_PARSE*sizeof(int));
+
+    // Aliases line indices
+    int *aliasLines = (int *)malloc(MAX_ALIASES_TO_PARSE*sizeof(int));
+
+    // Enums line indices
+    int *enumLines = (int *)malloc(MAX_ENUMS_TO_PARSE*sizeof(int));
+
+    // Callbacks line indices
+    int *callbackLines = (int *)malloc(MAX_CALLBACKS_TO_PARSE*sizeof(int));
+
+    // Function line indices
+    int *funcLines = (int *)malloc(MAX_FUNCS_TO_PARSE*sizeof(int));
+
+    // Prepare required lines for parsing
+    //----------------------------------------------------------------------------------
+    // Read define lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        int j = 0;
+        while ((lines[i][j] == ' ') || (lines[i][j] == '\t')) j++; // skip spaces and tabs in the begining
+        // Read define line
+        if (IsTextEqual(lines[i]+j, "#define ", 8))
+        {
+            // Keep the line position in the array of lines,
+            // so, we can scan that position and following lines
+            defineLines[defineCount] = i;
+            defineCount++;
+        }
+    }
+
+    // Read struct lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        // Find structs
+        // starting with "typedef struct ... {" or "typedef struct ... ; \n struct ... {"
+        // ending with "} ... ;"
+        // i.e. excluding "typedef struct rAudioBuffer rAudioBuffer;" -> Typedef and forward declaration only
+        if (IsTextEqual(lines[i], "typedef struct", 14))
+        {
+            bool validStruct = IsTextEqual(lines[i + 1], "struct", 6);
+            if (!validStruct)
+            {
+                for (int c = 0; c < MAX_LINE_LENGTH; c++)
+                {
+                    char v = lines[i][c];
+                    if (v == '{') validStruct = true;
+                    if ((v == '{') || (v == ';') || (v == '\0')) break;
+                }
+            }
+            if (!validStruct) continue;
+            structLines[structCount] = i;
+            while (lines[i][0] != '}') i++;
+            while (lines[i][0] != '\0') i++;
+            structCount++;
+        }
+    }
+
+    // Read alias lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        // Find aliases (lines with "typedef ... ...;")
+        if (IsTextEqual(lines[i], "typedef", 7))
+        {
+            int spaceCount = 0;
+            bool validAlias = false;
+
+            for (int c = 0; c < MAX_LINE_LENGTH; c++)
+            {
+                char v = lines[i][c];
+                if (v == ' ') spaceCount++;
+                if ((v == ';') && (spaceCount == 2)) validAlias = true;
+                if ((v == ';') || (v == '(') || (v == '\0')) break;
+            }
+            if (!validAlias) continue;
+            aliasLines[aliasCount] = i;
+            aliasCount++;
+        }
+    }
+
+    // Read enum lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        // Read enum line
+        if (IsTextEqual(lines[i], "typedef enum {", 14) && (lines[i][TextLength(lines[i])-1] != ';')) // ignore inline enums
+        {
+            // Keep the line position in the array of lines,
+            // so, we can scan that position and following lines
+            enumLines[enumCount] = i;
+            enumCount++;
+        }
+    }
+
+    // Read callback lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        // Find callbacks (lines with "typedef ... (* ... )( ... );")
+        if (IsTextEqual(lines[i], "typedef", 7))
+        {
+            bool hasBeginning = false;
+            bool hasMiddle = false;
+            bool hasEnd = false;
+
+            for (int c = 0; c < MAX_LINE_LENGTH; c++)
+            {
+                if ((lines[i][c] == '(') && (lines[i][c + 1] == '*')) hasBeginning = true;
+                if ((lines[i][c] == ')') && (lines[i][c + 1] == '(')) hasMiddle = true;
+                if ((lines[i][c] == ')') && (lines[i][c + 1] == ';')) hasEnd = true;
+                if (hasEnd) break;
+            }
+
+            if (hasBeginning && hasMiddle && hasEnd)
+            {
+                callbackLines[callbackCount] = i;
+                callbackCount++;
+            }
+        }
+    }
+
+    // Read function lines
+    for (int i = 0; i < lineCount; i++)
+    {
+        // Read function line (starting with `define`, i.e. for raylib.h "RLAPI")
+        if (IsTextEqual(lines[i], apiDefine, TextLength(apiDefine)))
+        {
+            funcLines[funcCount] = i;
+            funcCount++;
+        }
+    }
+
+    // At this point we have all raylib defines, structs, aliases, enums, callbacks, functions lines data to start parsing
+
+    UnloadFileText(buffer); // Unload text buffer
+
+    // Parsing raylib data
+    //----------------------------------------------------------------------------------
+    // Define info data
+    int defineIndex = 0;
+    defines = (DefineInfo *)calloc(MAX_DEFINES_TO_PARSE, sizeof(DefineInfo));
+
+    for (int i = 0; i < defineCount; i++)
+    {
+        char *linePtr = lines[defineLines[i]];
+        int j = 0;
+
+        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs in the begining
+        j += 8;                                                  // Skip "#define "
+        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs after "#define "
+
+        // Extract name
+        int defineNameStart = j;
+        int openBraces = 0;
+        while (linePtr[j] != '\0')
+        {
+            if (((linePtr[j] == ' ') || (linePtr[j] == '\t')) && (openBraces == 0)) break;
+            if (linePtr[j] == '(') openBraces++;
+            if (linePtr[j] == ')') openBraces--;
+            j++;
+        }
+        int defineNameEnd = j-1;
+
+        // Skip duplicates
+        unsigned int nameLen = defineNameEnd - defineNameStart + 1;
+        bool isDuplicate = false;
+        for (int k = 0; k < defineIndex; k++)
+        {
+            if ((nameLen == TextLength(defines[k].name)) && IsTextEqual(defines[k].name, &linePtr[defineNameStart], nameLen))
+            {
+                isDuplicate = true;
+                break;
+            }
+        }
+        if (isDuplicate) continue;
+
+        MemoryCopy(defines[defineIndex].name, &linePtr[defineNameStart], nameLen);
+
+        // Determine type
+        if (linePtr[defineNameEnd] == ')') defines[defineIndex].type = MACRO;
+
+        while ((linePtr[j] == ' ') || (linePtr[j] == '\t')) j++; // Skip spaces and tabs after name
+
+        int defineValueStart = j;
+        if ((linePtr[j] == '\0') || (linePtr[j] == '/')) defines[defineIndex].type = GUARD;
+        if (linePtr[j] == '"') defines[defineIndex].type = STRING;
+        else if (linePtr[j] == '\'') defines[defineIndex].type = CHAR;
+        else if (IsTextEqual(linePtr+j, "CLITERAL(Color)", 15)) defines[defineIndex].type = COLOR;
+        else if (isdigit(linePtr[j])) // Parsing numbers
+        {
+            bool isFloat = false, isNumber = true, isHex = false;
+            while ((linePtr[j] != ' ') && (linePtr[j] != '\t') && (linePtr[j] != '\0'))
+            {
+                char ch = linePtr[j];
+                if (ch == '.') isFloat = true;
+                if (ch == 'x') isHex = true;
+                if (!(isdigit(ch) ||
+                      ((ch >= 'a') && (ch <= 'f')) ||
+                      ((ch >= 'A') && (ch <= 'F')) ||
+                      (ch == 'x') ||
+                      (ch == 'L') ||
+                      (ch == '.') ||
+                      (ch == '+') ||
+                      (ch == '-'))) isNumber = false;
+                j++;
+            }
+            if (isNumber)
+            {
+                if (isFloat)
+                {
+                    defines[defineIndex].type = (linePtr[j-1] == 'f')? FLOAT : DOUBLE;
+                }
+                else
+                {
+                    defines[defineIndex].type = (linePtr[j-1] == 'L')? LONG : INT;
+                    defines[defineIndex].isHex = isHex;
+                }
+            }
+        }
+
+        // Extracting value
+        while ((linePtr[j] != '\\') && (linePtr[j] != '\0') && !((linePtr[j] == '/') && (linePtr[j+1] == '/'))) j++;
+        int defineValueEnd = j-1;
+        while ((linePtr[defineValueEnd] == ' ') || (linePtr[defineValueEnd] == '\t')) defineValueEnd--; // Remove trailing spaces and tabs
+        if ((defines[defineIndex].type == LONG) || (defines[defineIndex].type == FLOAT)) defineValueEnd--; // Remove number postfix
+        int valueLen = defineValueEnd - defineValueStart + 1;
+        if (valueLen > 255) valueLen = 255;
+
+        if (valueLen > 0) MemoryCopy(defines[defineIndex].value, &linePtr[defineValueStart], valueLen);
+
+        // Extracting description
+        if ((linePtr[j] == '/') && linePtr[j + 1] == '/')
+        {
+            j += 2;
+            while (linePtr[j] == ' ') j++;
+            int commentStart = j;
+            while ((linePtr[j] != '\\') && (linePtr[j] != '\0')) j++;
+            int commentEnd = j-1;
+            int commentLen = commentEnd - commentStart + 1;
+            if (commentLen > 127) commentLen = 127;
+
+            MemoryCopy(defines[defineIndex].desc, &linePtr[commentStart], commentLen);
+        }
+
+        // Parse defines of type UNKNOWN to find calculated numbers
+        if (defines[defineIndex].type == UNKNOWN)
+        {
+            int largestType = UNKNOWN;
+            bool isMath = true;
+            char *valuePtr = defines[defineIndex].value;
+
+            for (unsigned int c = 0; c < TextLength(valuePtr); c++)
+            {
+                char ch = valuePtr[c];
+
+                // Skip operators and whitespace
+                if ((ch == '(') ||
+                    (ch == ')') ||
+                    (ch == '+') ||
+                    (ch == '-') ||
+                    (ch == '*') ||
+                    (ch == '/') ||
+                    (ch == ' ') ||
+                    (ch == '\t')) continue;
+
+                // Read number operand
+                else if (isdigit(ch))
+                {
+                    bool isNumber = true, isFloat = false;
+                    while (!((ch == '(') ||
+                             (ch == ')') ||
+                             (ch == '*') ||
+                             (ch == '/') ||
+                             (ch == ' ') ||
+                             (ch == '\t') ||
+                             (ch == '\0')))
+                    {
+                        if (ch == '.') isFloat = true;
+                        if (!(isdigit(ch) ||
+                            ((ch >= 'a') && (ch <= 'f')) ||
+                            ((ch >= 'A') && (ch <= 'F')) ||
+                            (ch == 'x') ||
+                            (ch == 'L') ||
+                            (ch == '.') ||
+                            (ch == '+') ||
+                            (ch == '-')))
+                        {
+                            isNumber = false;
+                            break;
+                        }
+                        c++;
+                        ch = valuePtr[c];
+                    }
+                    if (isNumber)
+                    {
+                        // Found a valid number -> update largestType
+                        int numberType;
+                        if (isFloat) numberType = (valuePtr[c - 1] == 'f')? FLOAT_MATH : DOUBLE_MATH;
+                        else numberType = (valuePtr[c - 1] == 'L')? LONG_MATH : INT_MATH;
+
+                        if (numberType > largestType) largestType = numberType;
+                    }
+                    else
+                    {
+                        isMath = false;
+                        break;
+                    }
+                }
+                else    // Read string operand
+                {
+                    int operandStart = c;
+                    while (!((ch == '\0') ||
+                             (ch == ' ') ||
+                             (ch == '(') ||
+                             (ch == ')') ||
+                             (ch == '+') ||
+                             (ch == '-') ||
+                             (ch == '*') ||
+                             (ch == '/')))
+                    {
+                        c++;
+                        ch = valuePtr[c];
+                    }
+                    int operandEnd = c;
+                    int operandLength = operandEnd - operandStart;
+
+                    // Search previous defines for operand
+                    bool foundOperand = false;
+                    for (int previousDefineIndex = 0; previousDefineIndex < defineIndex; previousDefineIndex++)
+                    {
+                        if (IsTextEqual(defines[previousDefineIndex].name, &valuePtr[operandStart], operandLength))
+                        {
+                            if ((defines[previousDefineIndex].type >= INT) && (defines[previousDefineIndex].type <= DOUBLE_MATH))
+                            {
+                                // Found operand and it's a number -> update largestType
+                                if (defines[previousDefineIndex].type > largestType) largestType = defines[previousDefineIndex].type;
+                                foundOperand = true;
+                            }
+                            break;
+                        }
+                    }
+                    if (!foundOperand)
+                    {
+                        isMath = false;
+                        break;
+                    }
+                }
+            }
+
+            if (isMath)
+            {
+                // Define is a calculated number -> update type
+                if (largestType == INT) largestType = INT_MATH;
+                else if (largestType == LONG) largestType = LONG_MATH;
+                else if (largestType == FLOAT) largestType = FLOAT_MATH;
+                else if (largestType == DOUBLE) largestType = DOUBLE_MATH;
+                defines[defineIndex].type = largestType;
+            }
+        }
+
+        defineIndex++;
+    }
+    defineCount = defineIndex;
+    free(defineLines);
+
+    // Structs info data
+    structs = (StructInfo *)calloc(MAX_STRUCTS_TO_PARSE, sizeof(StructInfo));
+
+    for (int i = 0; i < structCount; i++)
+    {
+        char **linesPtr = &lines[structLines[i]];
+
+        // Parse struct description
+        GetDescription(linesPtr[-1], structs[i].desc);
+
+        // Get struct name: typedef struct name {
+        const int TDS_LEN = 15; // length of "typedef struct "
+        for (int c = TDS_LEN; c < 64 + TDS_LEN; c++)
+        {
+            if ((linesPtr[0][c] == '{') || (linesPtr[0][c] == ' '))
+            {
+                int nameLen = c - TDS_LEN;
+                while (linesPtr[0][TDS_LEN + nameLen - 1] == ' ') nameLen--;
+                MemoryCopy(structs[i].name, &linesPtr[0][TDS_LEN], nameLen);
+                break;
+            }
+        }
+
+        // Get struct fields and count them -> fields finish with ;
+        int l = 1;
+        while (linesPtr[l][0] != '}')
+        {
+            // WARNING: Some structs have empty spaces and comments -> OK, processed
+            if ((linesPtr[l][0] != ' ') && (linesPtr[l][0] != '\0'))
+            {
+                // Scan one field line
+                char *fieldLine = linesPtr[l];
+                int fieldEndPos = 0;
+                while (fieldLine[fieldEndPos] != ';') fieldEndPos++;
+
+                if ((fieldLine[0] != '/') && !IsTextEqual(fieldLine, "struct", 6)) // Field line is not a comment and not a struct declaration
+                {
+                    //printf("Struct field: %s_\n", fieldLine);     // OK!
+
+                    // Get struct field type and name
+                    GetDataTypeAndName(fieldLine, fieldEndPos, structs[i].fieldType[structs[i].fieldCount], structs[i].fieldName[structs[i].fieldCount]);
+
+                    // Get the field description
+                    GetDescription(&fieldLine[fieldEndPos], structs[i].fieldDesc[structs[i].fieldCount]);
+
+                    structs[i].fieldCount++;
+
+                    // Split field names containing multiple fields (like Matrix)
+                    int additionalFields = 0;
+                    int originalIndex = structs[i].fieldCount - 1;
+                    for (unsigned int c = 0; c < TextLength(structs[i].fieldName[originalIndex]); c++)
+                    {
+                        if (structs[i].fieldName[originalIndex][c] == ',') additionalFields++;
+                    }
+
+                    if (additionalFields > 0)
+                    {
+                        int originalLength = -1;
+                        int lastStart;
+                        for (unsigned int c = 0; c < TextLength(structs[i].fieldName[originalIndex]) + 1; c++)
+                        {
+                            char v = structs[i].fieldName[originalIndex][c];
+                            bool isEndOfString = (v == '\0');
+                            if ((v == ',') || isEndOfString)
+                            {
+                                if (originalLength == -1)
+                                {
+                                    // Save length of original field name
+                                    // Don't truncate yet, still needed for copying
+                                    originalLength = c;
+                                }
+                                else
+                                {
+                                    // Copy field data from original field
+                                    int nameLength = c - lastStart;
+                                    MemoryCopy(structs[i].fieldName[structs[i].fieldCount], &structs[i].fieldName[originalIndex][lastStart], nameLength);
+                                    MemoryCopy(structs[i].fieldType[structs[i].fieldCount], &structs[i].fieldType[originalIndex][0], TextLength(structs[i].fieldType[originalIndex]));
+                                    MemoryCopy(structs[i].fieldDesc[structs[i].fieldCount], &structs[i].fieldDesc[originalIndex][0], TextLength(structs[i].fieldDesc[originalIndex]));
+                                    structs[i].fieldCount++;
+                                }
+                                if (!isEndOfString)
+                                {
+                                    // Skip comma and spaces
+                                    c++;
+                                    while (structs[i].fieldName[originalIndex][c] == ' ') c++;
+
+                                    // Save position for next field
+                                    lastStart = c;
+                                }
+                            }
+                        }
+                        // Set length of original field to truncate the first field name
+                        structs[i].fieldName[originalIndex][originalLength] = '\0';
+                    }
+
+                    // Split field types containing multiple fields (like MemNode)
+                    additionalFields = 0;
+                    originalIndex = structs[i].fieldCount - 1;
+                    for (unsigned int c = 0; c < TextLength(structs[i].fieldType[originalIndex]); c++)
+                    {
+                        if (structs[i].fieldType[originalIndex][c] == ',') additionalFields++;
+                    }
+
+                    if (additionalFields > 0)
+                    {
+                        // Copy original name to last additional field
+                        structs[i].fieldCount += additionalFields;
+                        MemoryCopy(structs[i].fieldName[originalIndex + additionalFields], &structs[i].fieldName[originalIndex][0], TextLength(structs[i].fieldName[originalIndex]));
+
+                        // Copy names from type to additional fields
+                        int fieldsRemaining = additionalFields;
+                        int nameStart = -1;
+                        int nameEnd = -1;
+                        for (int k = TextLength(structs[i].fieldType[originalIndex]); k > 0; k--)
+                        {
+                            char v = structs[i].fieldType[originalIndex][k];
+                            if ((v == '*') || (v == ' ') || (v == ','))
+                            {
+                                if (nameEnd != -1) {
+                                    // Don't copy to last additional field
+                                    if (fieldsRemaining != additionalFields)
+                                    {
+                                        nameStart = k + 1;
+                                        MemoryCopy(structs[i].fieldName[originalIndex + fieldsRemaining], &structs[i].fieldType[originalIndex][nameStart], nameEnd - nameStart + 1);
+                                    }
+                                    nameEnd = -1;
+                                    fieldsRemaining--;
+                                }
+                            }
+                            else if (nameEnd == -1) nameEnd = k;
+                        }
+
+                        // Truncate original field type
+                        int fieldTypeLength = nameStart;
+                        structs[i].fieldType[originalIndex][fieldTypeLength] = '\0';
+
+                        // Set field type and description of additional fields
+                        for (int j = 1; j <= additionalFields; j++)
+                        {
+                            MemoryCopy(structs[i].fieldType[originalIndex + j], &structs[i].fieldType[originalIndex][0], fieldTypeLength);
+                            MemoryCopy(structs[i].fieldDesc[originalIndex + j], &structs[i].fieldDesc[originalIndex][0], TextLength(structs[i].fieldDesc[originalIndex]));
+                        }
+                    }
+                }
+            }
+
+            l++;
+        }
+
+        // Move array sizes from name to type
+        for (int j = 0; j < structs[i].fieldCount; j++)
+        {
+            MoveArraySize(structs[i].fieldName[j], structs[i].fieldType[j]);
+        }
+    }
+    free(structLines);
+
+    // Alias info data
+    aliases = (AliasInfo *)calloc(MAX_ALIASES_TO_PARSE, sizeof(AliasInfo));
+
+    for (int i = 0; i < aliasCount; i++)
+    {
+        // Description from previous line
+        GetDescription(lines[aliasLines[i] - 1], aliases[i].desc);
+
+        char *linePtr = lines[aliasLines[i]];
+
+        // Skip "typedef "
+        int c = 8;
+
+        // Type
+        int typeStart = c;
+        while(linePtr[c] != ' ') c++;
+        int typeLen = c - typeStart;
+        MemoryCopy(aliases[i].type, &linePtr[typeStart], typeLen);
+
+        // Skip space
+        c++;
+
+        // Name
+        int nameStart = c;
+        while(linePtr[c] != ';') c++;
+        int nameLen = c - nameStart;
+        MemoryCopy(aliases[i].name, &linePtr[nameStart], nameLen);
+
+        // Description
+        GetDescription(&linePtr[c], aliases[i].desc);
+    }
+    free(aliasLines);
+
+    // Enum info data
+    enums = (EnumInfo *)calloc(MAX_ENUMS_TO_PARSE, sizeof(EnumInfo));
+
+    for (int i = 0; i < enumCount; i++)
+    {
+        // Parse enum description
+        // NOTE: This is not necessarily from the line immediately before,
+        // some of the enums have extra lines between the "description"
+        // and the typedef enum
+        for (int j = enumLines[i] - 1; j > 0; j--)
+        {
+            char *linePtr = lines[j];
+            if ((linePtr[0] != '/') || (linePtr[2] != ' '))
+            {
+                GetDescription(&lines[j + 1][0], enums[i].desc);
+                break;
+            }
+        }
+
+        for (int j = 1; j < MAX_ENUM_VALUES*2; j++)   // Maximum number of lines following enum first line
+        {
+            char *linePtr = lines[enumLines[i] + j];
+
+            if ((linePtr[0] >= 'A') && (linePtr[0] <= 'Z'))
+            {
+                // Parse enum value line, possible options:
+                //ENUM_VALUE_NAME,
+                //ENUM_VALUE_NAME
+                //ENUM_VALUE_NAME     = 99
+                //ENUM_VALUE_NAME     = 99,
+                //ENUM_VALUE_NAME     = 0x00000040,   // Value description
+
+                // We start reading the value name
+                int c = 0;
+                while ((linePtr[c] != ',') &&
+                       (linePtr[c] != ' ') &&
+                       (linePtr[c] != '=') &&
+                       (linePtr[c] != '\0'))
+                {
+                    enums[i].valueName[enums[i].valueCount][c] = linePtr[c];
+                    c++;
+                }
+
+                // After the name we can have:
+                //  '='  -> value is provided
+                //  ','  -> value is equal to previous + 1, there could be a description if not '\0'
+                //  ' '  -> value is equal to previous + 1, there could be a description if not '\0'
+                //  '\0' -> value is equal to previous + 1
+
+                // Let's start checking if the line is not finished
+                if ((linePtr[c] != ',') && (linePtr[c] != '\0'))
+                {
+                    // Two options:
+                    //  '='  -> value is provided
+                    //  ' '  -> value is equal to previous + 1, there could be a description if not '\0'
+                    bool foundValue = false;
+                    while ((linePtr[c] != '\0') && (linePtr[c] != '/'))
+                    {
+                        if (linePtr[c] == '=')
+                        {
+                            foundValue = true;
+                            break;
+                        }
+                        c++;
+                    }
+
+                    if (foundValue)
+                    {
+                        if (linePtr[c + 1] == ' ') c += 2;
+                        else c++;
+
+                        // Parse integer value
+                        int n = 0;
+                        char integer[16] = { 0 };
+
+                        while ((linePtr[c] != ',') && (linePtr[c] != ' ') && (linePtr[c] != '\0'))
+                        {
+                            integer[n] = linePtr[c];
+                            c++; n++;
+                        }
+
+                        if (integer[1] == 'x') enums[i].valueInteger[enums[i].valueCount] = (int)strtol(integer, NULL, 16);
+                        else enums[i].valueInteger[enums[i].valueCount] = atoi(integer);
+                    }
+                    else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
+                }
+                else enums[i].valueInteger[enums[i].valueCount] = (enums[i].valueInteger[enums[i].valueCount - 1] + 1);
+
+                // Parse value description
+                GetDescription(&linePtr[c], enums[i].valueDesc[enums[i].valueCount]);
+
+                enums[i].valueCount++;
+            }
+            else if (linePtr[0] == '}')
+            {
+                // Get enum name from typedef
+                int c = 0;
+                while (linePtr[2 + c] != ';')
+                {
+                    enums[i].name[c] = linePtr[2 + c];
+                    c++;
+                }
+
+                break;  // Enum ended, break for() loop
+            }
+        }
+    }
+    free(enumLines);
+
+    // Callback info data
+    callbacks = (FunctionInfo *)calloc(MAX_CALLBACKS_TO_PARSE, sizeof(FunctionInfo));
+
+    for (int i = 0; i < callbackCount; i++)
+    {
+        char *linePtr = lines[callbackLines[i]];
+
+        // Skip "typedef "
+        unsigned int c = 8;
+
+        // Return type
+        int retTypeStart = c;
+        while(linePtr[c] != '(') c++;
+        int retTypeLen = c - retTypeStart;
+        while(linePtr[retTypeStart + retTypeLen - 1] == ' ') retTypeLen--;
+        MemoryCopy(callbacks[i].retType, &linePtr[retTypeStart], retTypeLen);
+
+        // Skip "(*"
+        c += 2;
+
+        // Name
+        int nameStart = c;
+        while(linePtr[c] != ')') c++;
+        int nameLen = c - nameStart;
+        MemoryCopy(callbacks[i].name, &linePtr[nameStart], nameLen);
+
+        // Skip ")("
+        c += 2;
+
+        // Params
+        int paramStart = c;
+        for (; c < MAX_LINE_LENGTH; c++)
+        {
+            if ((linePtr[c] == ',') || (linePtr[c] == ')'))
+            {
+                // Get parameter type + name, extract info
+                int paramLen = c - paramStart;
+                GetDataTypeAndName(&linePtr[paramStart], paramLen, callbacks[i].paramType[callbacks[i].paramCount], callbacks[i].paramName[callbacks[i].paramCount]);
+                callbacks[i].paramCount++;
+                paramStart = c + 1;
+                while(linePtr[paramStart] == ' ') paramStart++;
+            }
+            if (linePtr[c] == ')') break;
+        }
+
+        // Description
+        GetDescription(&linePtr[c], callbacks[i].desc);
+
+        // Move array sizes from name to type
+        for (int j = 0; j < callbacks[i].paramCount; j++)
+        {
+            MoveArraySize(callbacks[i].paramName[j], callbacks[i].paramType[j]);
+        }
+    }
+    free(callbackLines);
+
+    // Functions info data
+    funcs = (FunctionInfo *)calloc(MAX_FUNCS_TO_PARSE, sizeof(FunctionInfo));
+
+    for (int i = 0; i < funcCount; i++)
+    {
+        char *linePtr = lines[funcLines[i]];
+
+        int funcParamsStart = 0;
+        int funcEnd = 0;
+
+        // Get return type and function name from func line
+        for (int c = 0; (c < MAX_LINE_LENGTH) && (linePtr[c] != '\n'); c++)
+        {
+            if (linePtr[c] == '(')     // Starts function parameters
+            {
+                funcParamsStart = c + 1;
+
+                // At this point we have function return type and function name
+                char funcRetTypeName[128] = { 0 };
+                int dc = TextLength(apiDefine) + 1;
+                int funcRetTypeNameLen = c - dc;     // Substract `define` ("RLAPI " for raylib.h)
+                MemoryCopy(funcRetTypeName, &linePtr[dc], funcRetTypeNameLen);
+
+                GetDataTypeAndName(funcRetTypeName, funcRetTypeNameLen, funcs[i].retType, funcs[i].name);
+                break;
+            }
+        }
+
+        // Get parameters from func line
+        for (int c = funcParamsStart; c < MAX_LINE_LENGTH; c++)
+        {
+            if (linePtr[c] == ',')     // Starts function parameters
+            {
+                // Get parameter type + name, extract info
+                char funcParamTypeName[128] = { 0 };
+                int funcParamTypeNameLen = c - funcParamsStart;
+                MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
+
+                GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
+
+                funcParamsStart = c + 1;
+                if (linePtr[c + 1] == ' ') funcParamsStart += 1;
+                funcs[i].paramCount++;      // Move to next parameter
+            }
+            else if (linePtr[c] == ')')
+            {
+                funcEnd = c + 2;
+
+                // Check if there are no parameters
+                if ((funcEnd - funcParamsStart == 2) ||
+                    ((linePtr[c - 4] == 'v') &&
+                     (linePtr[c - 3] == 'o') &&
+                     (linePtr[c - 2] == 'i') &&
+                     (linePtr[c - 1] == 'd'))) {
+                  break;
+                }
+
+                // Get parameter type + name, extract info
+                char funcParamTypeName[128] = { 0 };
+                int funcParamTypeNameLen = c - funcParamsStart;
+                MemoryCopy(funcParamTypeName, &linePtr[funcParamsStart], funcParamTypeNameLen);
+
+                GetDataTypeAndName(funcParamTypeName, funcParamTypeNameLen, funcs[i].paramType[funcs[i].paramCount], funcs[i].paramName[funcs[i].paramCount]);
+
+                funcs[i].paramCount++;      // Move to next parameter
+                break;
+            }
+        }
+
+        // Get function description
+        GetDescription(&linePtr[funcEnd], funcs[i].desc);
+
+        // Move array sizes from name to type
+        for (int j = 0; j < funcs[i].paramCount; j++)
+        {
+            MoveArraySize(funcs[i].paramName[j], funcs[i].paramType[j]);
+        }
+    }
+    free(funcLines);
+
+    UnloadTextLines(lines, lineCount);
+
+    // At this point, all raylib data has been parsed!
+    //----------------------------------------------------------------------------------
+    // defines[]   -> We have all the defines decomposed into pieces for further analysis
+    // structs[]   -> We have all the structs decomposed into pieces for further analysis
+    // aliases[]   -> We have all the aliases decomposed into pieces for further analysis
+    // enums[]     -> We have all the enums decomposed into pieces for further analysis
+    // callbacks[] -> We have all the callbacks decomposed into pieces for further analysis
+    // funcs[]     -> We have all the functions decomposed into pieces for further analysis
+
+    // Export data as required
+    // NOTE: We are exporting data in several common formats (JSON, XML, LUA...) for convenience
+    // but this data can be directly used to create bindings for other languages modifying this
+    // small parser for every binding need, there is no need to use (and re-parse) the
+    // generated files... despite it seems that's the usual approach...
+    printf("\nInput file:       %s", inFileName);
+    printf("\nOutput file:      %s", outFileName);
+    if (outputFormat == DEFAULT) printf("\nOutput format:    DEFAULT\n\n");
+    else if (outputFormat == JSON) printf("\nOutput format:    JSON\n\n");
+    else if (outputFormat == XML) printf("\nOutput format:    XML\n\n");
+    else if (outputFormat == LUA) printf("\nOutput format:    LUA\n\n");
+    else if (outputFormat == CODE) printf("\nOutput format:    CODE\n\n");
+
+    ExportParsedData(outFileName, outputFormat);
+
+    free(defines);
+    free(structs);
+    free(aliases);
+    free(enums);
+    free(callbacks);
+    free(funcs);
+}
+
+//----------------------------------------------------------------------------------
+// Module Functions Definition
+//----------------------------------------------------------------------------------
+// Show command line usage info
+static void ShowCommandLineInfo(void)
+{
+    printf("\n//////////////////////////////////////////////////////////////////////////////////\n");
+    printf("//                                                                              //\n");
+    printf("// rlparser - raylib header API parser                                          //\n");
+    printf("//                                                                              //\n");
+    printf("// more info and bugs-report: github.com/raysan5/raylib/tools/rlparser          //\n");
+    printf("//                                                                              //\n");
+    printf("// Copyright (c) 2021-2026 Ramon Santamaria (@raysan5)                          //\n");
+    printf("//                                                                              //\n");
+    printf("//////////////////////////////////////////////////////////////////////////////////\n\n");
+
+    printf("USAGE:\n\n");
+    printf("    > rlparser [--help] [--input <filename.h>] [--output <filename.ext>] [--format <type>]\n");
+
+    printf("\nOPTIONS:\n\n");
+    printf("    -h, --help                      : Show tool version and command line usage help\n\n");
+    printf("    -i, --input <filename.h>        : Define input header file to parse.\n");
+    printf("                                      NOTE: If not specified, defaults to: raylib.h\n\n");
+    printf("    -o, --output <filename.ext>     : Define output file and format.\n");
+    printf("                                      Supported extensions: .txt, .json, .xml, .lua, .h\n");
+    printf("                                      NOTE: If not specified, defaults to: raylib_api.txt\n\n");
+    printf("    -f, --format <type>             : Define output format for parser data.\n");
+    printf("                                      Supported types: DEFAULT, JSON, XML, LUA, CODE\n\n");
+    printf("    -d, --define <DEF>              : Define functions specifiers (i.e. RLAPI for raylib.h, RMAPI for raymath.h, etc.)\n");
+    printf("                                      NOTE: If no specifier defined, defaults to: RLAPI\n\n");
+    printf("    -t, --truncate <after>          : Define string to truncate input after (i.e. \"RLGL IMPLEMENTATION\" for rlgl.h)\n");
+    printf("                                      NOTE: If not specified, the full input file is parsed.\n\n");
+
+    printf("\nEXAMPLES:\n\n");
+    printf("    > rlparser --input raylib.h --output api.json\n");
+    printf("        Process <raylib.h> to generate <api.json>\n\n");
+    printf("    > rlparser --output raylib_data.info --format XML\n");
+    printf("        Process <raylib.h> to generate <raylib_data.info> as XML text data\n\n");
+    printf("    > rlparser --input raymath.h --output raymath_data.info --format XML --define RMAPI\n");
+    printf("        Process <raymath.h> to generate <raymath_data.info> as XML text data\n\n");
+}
+
+// Process command line arguments
+static void ProcessCommandLine(int argc, char *argv[])
+{
+    for (int i = 1; i < argc; i++)
+    {
+        if (IsTextEqual(argv[i], "-h", 2) || IsTextEqual(argv[i], "--help", 6))
+        {
+            // Show info
+            ShowCommandLineInfo();
+            exit(0);
+        }
+        else if (IsTextEqual(argv[i], "-i", 2) || IsTextEqual(argv[i], "--input", 7))
+        {
+            // Check for valid argument and valid file extension
+            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+            {
+                MemoryCopy(inFileName, argv[i + 1], TextLength(argv[i + 1])); // Read input filename
+                i++;
+            }
+            else printf("WARNING: No input file provided\n");
+        }
+        else if (IsTextEqual(argv[i], "-o", 2) || IsTextEqual(argv[i], "--output", 8))
+        {
+            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+            {
+                MemoryCopy(outFileName, argv[i + 1], TextLength(argv[i + 1])); // Read output filename
+                i++;
+            }
+            else printf("WARNING: No output file provided\n");
+        }
+        else if (IsTextEqual(argv[i], "-f", 2) || IsTextEqual(argv[i], "--format", 8))
+        {
+            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+            {
+                if (IsTextEqual(argv[i + 1], "DEFAULT\0", 8)) outputFormat = DEFAULT;
+                else if (IsTextEqual(argv[i + 1], "JSON\0", 5)) outputFormat = JSON;
+                else if (IsTextEqual(argv[i + 1], "XML\0", 4)) outputFormat = XML;
+                else if (IsTextEqual(argv[i + 1], "LUA\0", 4)) outputFormat = LUA;
+                else if (IsTextEqual(argv[i + 1], "CODE\0", 4)) outputFormat = CODE;
+            }
+            else printf("WARNING: No format parameters provided\n");
+        }
+        else if (IsTextEqual(argv[i], "-d", 2) || IsTextEqual(argv[i], "--define", 8))
+        {
+            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+            {
+                MemoryCopy(apiDefine, argv[i + 1], TextLength(argv[i + 1])); // Read functions define
+                apiDefine[TextLength(argv[i + 1])] = '\0';
+                i++;
+            }
+            else printf("WARNING: No define key provided\n");
+        }
+        else if (IsTextEqual(argv[i], "-t", 2) || IsTextEqual(argv[i], "--truncate", 10))
+        {
+            if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
+            {
+                MemoryCopy(truncAfter, argv[i + 1], TextLength(argv[i + 1])); // Read truncate marker
+                truncAfter[TextLength(argv[i + 1])] = '\0';
+                i++;
+            }
+        }
+    }
+}
+
+// Load text data from file, returns a '\0' terminated string
+// NOTE: text chars array should be freed manually
+static char *LoadFileText(const char *fileName, int *length)
+{
+    char *text = NULL;
+
+    if (fileName != NULL)
+    {
+        FILE *file = fopen(fileName, "rt");
+
+        if (file != NULL)
+        {
+            // WARNING: When reading a file as 'text' file,
+            // text mode causes carriage return-linefeed translation...
+            // ...but using fseek() should return correct byte-offset
+            fseek(file, 0, SEEK_END);
+            int size = ftell(file);
+            fseek(file, 0, SEEK_SET);
+
+            if (size > 0)
+            {
+                text = (char *)calloc((size + 1), sizeof(char));
+                unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
+
+                // WARNING: \r\n is converted to \n on reading, so,
+                // read bytes count gets reduced by the number of lines
+                if (count < (unsigned int)size)
+                {
+                    text = realloc(text, count + 1);
+                    *length = count;
+                }
+                else *length = size;
+
+                // Zero-terminate the string
+                text[count] = '\0';
+            }
+
+            fclose(file);
+        }
+    }
+
+    return text;
+}
+
+// Unload text data
+static void UnloadFileText(char *text)
+{
+    free(text);
+}
+
+// Get all lines from a text buffer (expecting lines ending with '\n')
+static char **LoadTextLines(const char *buffer, int length, int *lineCount)
+{
+    // Get the number of lines in the text
+    int count = 0;
+    for (int i = 0; i < length; i++) if (buffer[i] == '\n') count++;
+
+    printf("Number of text lines in buffer: %i\n", count);
+
+    // Allocate as many pointers as lines
+    char **lines = (char **)malloc(count*sizeof(char **));
+
+    char *bufferPtr = (char *)buffer;
+
+    for (int i = 0; (i < count) || (bufferPtr[0] != '\0'); i++)
+    {
+        lines[i] = (char *)calloc(MAX_LINE_LENGTH, sizeof(char)); // MAX_LINE_LENGTH=1024
+
+        // Remove line leading spaces
+        // Find last index of space/tab character
+        int index = 0;
+        while ((bufferPtr[index] == ' ') || (bufferPtr[index] == '\t')) index++;
+
+        int j = 0;
+        while (bufferPtr[index + j] != '\n' && bufferPtr[index + j] != '\0')
+        {
+            lines[i][j] = bufferPtr[index + j];
+            j++;
+        }
+
+        bufferPtr += (index + j + 1);
+    }
+
+    *lineCount = count;
+    return lines;
+}
+
+// Unload text lines data
+static void UnloadTextLines(char **lines, int lineCount)
+{
+    for (int i = 0; i < lineCount; i++) free(lines[i]);
+    free(lines);
+}
+
+// Get data type and name from a string containing both
+// NOTE: Useful to parse function parameters and struct fields
+static void GetDataTypeAndName(const char *typeName, int typeNameLen, char *type, char *name)
+{
+    for (int k = typeNameLen; k > 0; k--)
+    {
+        if ((typeName[k] == ' ') && (typeName[k - 1] != ','))
+        {
+            // Function name starts at this point (and ret type finishes at this point)
+            MemoryCopy(type, typeName, k);
+            MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
+            break;
+        }
+        else if (typeName[k] == '*')
+        {
+            MemoryCopy(type, typeName, k + 1);
+            MemoryCopy(name, typeName + k + 1, typeNameLen - k - 1);
+            break;
+        }
+        else if ((typeName[k] == '.') && (typeNameLen == 3)) // Handle varargs ...);
+        {
+	    const char *varargsDots = "...";
+	    const char *varargsArg = "args";
+            MemoryCopy(type, varargsDots, TextLength(varargsDots));
+            MemoryCopy(name, varargsArg, TextLength(varargsArg));
+            break;
+        }
+    }
+}
+
+// Get comment from a line, do nothing if no comment in line
+static void GetDescription(const char *line, char *description)
+{
+    int c = 0;
+    int descStart = -1;
+    int lastSlash = -2;
+    bool isValid = false;
+    while (line[c] != '\0')
+    {
+        if (isValid && (descStart == -1) && (line[c] != ' ')) descStart = c;
+        else if (line[c] == '/')
+        {
+            if (lastSlash == c - 1) isValid = true;
+            lastSlash = c;
+        }
+        c++;
+    }
+    if (descStart != -1) MemoryCopy(description, &line[descStart], c - descStart);
+}
+
+// Move array size from name to type
+static void MoveArraySize(char *name, char *type)
+{
+    int nameLength = TextLength(name);
+    if (name[nameLength - 1] == ']')
+    {
+        for (int k = nameLength; k > 0; k--)
+        {
+            if (name[k] == '[')
+            {
+                int sizeLength = nameLength - k;
+                MemoryCopy(&type[TextLength(type)], &name[k], sizeLength);
+                name[k] = '\0';
+            }
+        }
+    }
+}
+
+// Get text length in bytes, check for \0 character
+static unsigned int TextLength(const char *text)
+{
+    unsigned int length = 0;
+
+    if (text != NULL) while (*text++) length++;
+
+    return length;
+}
+
+// Compare two text strings, requires number of characters to compare
+static bool IsTextEqual(const char *text1, const char *text2, unsigned int count)
+{
+    bool result = true;
+
+    for (unsigned int i = 0; i < count; i++)
+    {
+        if (text1[i] != text2[i])
+        {
+            result = false;
+            break;
+        }
+    }
+
+    return result;
+}
+
+// Find first text occurrence within a string
+int TextFindIndex(const char *text, const char *find)
+{
+    int textLen = TextLength(text);
+    int findLen = TextLength(find);
+
+    for (int i = 0; i <= textLen - findLen; i++)
+    {
+        if (IsTextEqual(&text[i], find, findLen)) return i;
+    }
+
+    return -1;
+}
+
+// Custom memcpy() to avoid <string.h>
+static void MemoryCopy(void *dest, const void *src, unsigned int count)
+{
+    char *srcPtr = (char *)src;
+    char *destPtr = (char *)dest;
+
+    for (unsigned int i = 0; i < count; i++) destPtr[i] = srcPtr[i];
+}
+
+// Escape backslashes in a string, writing the escaped string into a static buffer
+static char *EscapeBackslashes(char *text)
+{
+    static char buffer[256] = { 0 };
+
+    int count = 0;
+
+    for (int i = 0; (text[i] != '\0') && (i < 255); i++, count++)
+    {
+        buffer[count] = text[i];
+
+        if (text[i] == '\\')
+        {
+            buffer[count + 1] = '\\';
+            count++;
+        }
+    }
+
+    buffer[count] = '\0';
+
+    return buffer;
+}
+
+// Get string of define type
+static const char *StrDefineType(DefineType type)
+{
+    switch (type)
+    {
+        case UNKNOWN:     return "UNKNOWN";
+        case GUARD:       return "GUARD";
+        case MACRO:       return "MACRO";
+        case INT:         return "INT";
+        case INT_MATH:    return "INT_MATH";
+        case LONG:        return "LONG";
+        case LONG_MATH:   return "LONG_MATH";
+        case FLOAT:       return "FLOAT";
+        case FLOAT_MATH:  return "FLOAT_MATH";
+        case DOUBLE:      return "DOUBLE";
+        case DOUBLE_MATH: return "DOUBLE_MATH";
+        case CHAR:        return "CHAR";
+        case STRING:      return "STRING";
+        case COLOR:       return "COLOR";
+    }
+    return "";
+}
+
+/*
+// Replace text string
+// REQUIRES: strlen(), strstr(), strncpy(), strcpy() -> TODO: Replace by custom implementations!
+// WARNING: Returned buffer must be freed by the user (if return != NULL)
+static char *TextReplace(char *text, const char *replace, const char *by)
+{
+    // Sanity checks and initialization
+    if (!text || !replace || !by) return NULL;
+
+    char *result;
+
+    char *insertPoint;      // Next insert point
+    char *temp;             // Temp pointer
+    int replaceLen;         // Replace string length of (the string to remove)
+    int byLen;              // Replacement length (the string to replace replace by)
+    int lastReplacePos;     // Distance between replace and end of last replace
+    int count;              // Number of replacements
+
+    replaceLen = strlen(replace);
+    if (replaceLen == 0) return NULL;  // Empty replace causes infinite loop during count
+
+    byLen = strlen(by);
+
+    // Count the number of replacements needed
+    insertPoint = text;
+    for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
+
+    // Allocate returning string and point temp to it
+    temp = result = (char *)malloc(strlen(text) + (byLen - replaceLen)*count + 1);
+
+    if (!result) return NULL;   // Memory could not be allocated
+
+    // First time through the loop, all the variable are set correctly from here on,
+    //  - 'temp' points to the end of the result string
+    //  - 'insertPoint' points to the next occurrence of replace in text
+    //  - 'text' points to the remainder of text after "end of replace"
+    while (count--)
+    {
+        insertPoint = strstr(text, replace);
+        lastReplacePos = (int)(insertPoint - text);
+        temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
+        temp = strcpy(temp, by) + byLen;
+        text += lastReplacePos + replaceLen; // Move to next "end of replace"
+    }
+
+    // Copy remaind text part after replacement to result (pointed by moving temp)
+    strcpy(temp, text);
+
+    return result;
+}
+*/
+
+// Export parsed data in desired format
+static void ExportParsedData(const char *fileName, int format)
+{
+    FILE *outFile = fopen(fileName, "wt");
+
+    switch (format)
+    {
+        case DEFAULT:
+        {
+            // Print defines info
+            fprintf(outFile, "\nDefines found: %i\n\n", defineCount);
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "Define %03i: %s\n", i + 1, defines[i].name);
+                fprintf(outFile, "  Name: %s\n", defines[i].name);
+                fprintf(outFile, "  Type: %s\n", StrDefineType(defines[i].type));
+                fprintf(outFile, "  Value: %s\n", defines[i].value);
+                fprintf(outFile, "  Description: %s\n", defines[i].desc);
+            }
+
+            // Print structs info
+            fprintf(outFile, "\nStructures found: %i\n\n", structCount);
+            for (int i = 0; i < structCount; i++)
+            {
+                fprintf(outFile, "Struct %02i: %s (%i fields)\n", i + 1, structs[i].name, structs[i].fieldCount);
+                fprintf(outFile, "  Name: %s\n", structs[i].name);
+                fprintf(outFile, "  Description: %s\n", structs[i].desc);
+                for (int f = 0; f < structs[i].fieldCount; f++)
+                {
+                    fprintf(outFile, "  Field[%i]: %s %s ", f + 1, structs[i].fieldType[f], structs[i].fieldName[f]);
+                    if (structs[i].fieldDesc[f][0]) fprintf(outFile, "// %s\n", structs[i].fieldDesc[f]);
+                    else fprintf(outFile, "\n");
+                }
+            }
+
+            // Print aliases info
+            fprintf(outFile, "\nAliases found: %i\n\n", aliasCount);
+            for (int i = 0; i < aliasCount; i++)
+            {
+                fprintf(outFile, "Alias %03i: %s\n", i + 1, aliases[i].name);
+                fprintf(outFile, "  Type: %s\n", aliases[i].type);
+                fprintf(outFile, "  Name: %s\n", aliases[i].name);
+                fprintf(outFile, "  Description: %s\n", aliases[i].desc);
+            }
+
+            // Print enums info
+            fprintf(outFile, "\nEnums found: %i\n\n", enumCount);
+            for (int i = 0; i < enumCount; i++)
+            {
+                fprintf(outFile, "Enum %02i: %s (%i values)\n", i + 1, enums[i].name, enums[i].valueCount);
+                fprintf(outFile, "  Name: %s\n", enums[i].name);
+                fprintf(outFile, "  Description: %s\n", enums[i].desc);
+                for (int e = 0; e < enums[i].valueCount; e++) fprintf(outFile, "  Value[%s]: %i\n", enums[i].valueName[e], enums[i].valueInteger[e]);
+            }
+
+            // Print callbacks info
+            fprintf(outFile, "\nCallbacks found: %i\n\n", callbackCount);
+            for (int i = 0; i < callbackCount; i++)
+            {
+                fprintf(outFile, "Callback %03i: %s() (%i input parameters)\n", i + 1, callbacks[i].name, callbacks[i].paramCount);
+                fprintf(outFile, "  Name: %s\n", callbacks[i].name);
+                fprintf(outFile, "  Return type: %s\n", callbacks[i].retType);
+                fprintf(outFile, "  Description: %s\n", callbacks[i].desc);
+                for (int p = 0; p < callbacks[i].paramCount; p++) fprintf(outFile, "  Param[%i]: %s (type: %s)\n", p + 1, callbacks[i].paramName[p], callbacks[i].paramType[p]);
+                if (callbacks[i].paramCount == 0) fprintf(outFile, "  No input parameters\n");
+            }
+
+            // Print functions info
+            fprintf(outFile, "\nFunctions found: %i\n\n", funcCount);
+            for (int i = 0; i < funcCount; i++)
+            {
+                fprintf(outFile, "Function %03i: %s() (%i input parameters)\n", i + 1, funcs[i].name, funcs[i].paramCount);
+                fprintf(outFile, "  Name: %s\n", funcs[i].name);
+                fprintf(outFile, "  Return type: %s\n", funcs[i].retType);
+                fprintf(outFile, "  Description: %s\n", funcs[i].desc);
+                for (int p = 0; p < funcs[i].paramCount; p++) fprintf(outFile, "  Param[%i]: %s (type: %s)\n", p + 1, funcs[i].paramName[p], funcs[i].paramType[p]);
+                if (funcs[i].paramCount == 0) fprintf(outFile, "  No input parameters\n");
+            }
+        } break;
+        case JSON:
+        {
+            fprintf(outFile, "{\n");
+
+            // Print defines info
+            fprintf(outFile, "  \"defines\": [\n");
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", defines[i].name);
+                fprintf(outFile, "      \"type\": \"%s\",\n", StrDefineType(defines[i].type));
+                if (defines[i].isHex) // INT or LONG
+                {
+                    fprintf(outFile, "      \"value\": %ld,\n", strtol(defines[i].value, NULL, 16));
+                }
+                else if ((defines[i].type == INT) ||
+                         (defines[i].type == LONG) ||
+                         (defines[i].type == FLOAT) ||
+                         (defines[i].type == DOUBLE) ||
+                         (defines[i].type == STRING))
+                {
+                    fprintf(outFile, "      \"value\": %s,\n", defines[i].value);
+                }
+                else
+                {
+                    fprintf(outFile, "      \"value\": \"%s\",\n", defines[i].value);
+                }
+                fprintf(outFile, "      \"description\": \"%s\"\n", defines[i].desc);
+                fprintf(outFile, "    }");
+
+                if (i < defineCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
+            // Print structs info
+            fprintf(outFile, "  \"structs\": [\n");
+            for (int i = 0; i < structCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", structs[i].name);
+                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(structs[i].desc));
+                fprintf(outFile, "      \"fields\": [\n");
+                for (int f = 0; f < structs[i].fieldCount; f++)
+                {
+                    fprintf(outFile, "        {\n");
+                    fprintf(outFile, "          \"type\": \"%s\",\n", structs[i].fieldType[f]);
+                    fprintf(outFile, "          \"name\": \"%s\",\n", structs[i].fieldName[f]);
+                    fprintf(outFile, "          \"description\": \"%s\"\n", EscapeBackslashes(structs[i].fieldDesc[f]));
+                    fprintf(outFile, "        }");
+                    if (f < structs[i].fieldCount - 1) fprintf(outFile, ",\n");
+                    else fprintf(outFile, "\n");
+                }
+                fprintf(outFile, "      ]\n");
+                fprintf(outFile, "    }");
+                if (i < structCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
+            // Print aliases info
+            fprintf(outFile, "  \"aliases\": [\n");
+            for (int i = 0; i < aliasCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"type\": \"%s\",\n", aliases[i].type);
+                fprintf(outFile, "      \"name\": \"%s\",\n", aliases[i].name);
+                fprintf(outFile, "      \"description\": \"%s\"\n", aliases[i].desc);
+                fprintf(outFile, "    }");
+
+                if (i < aliasCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
+            // Print enums info
+            fprintf(outFile, "  \"enums\": [\n");
+            for (int i = 0; i < enumCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", enums[i].name);
+                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(enums[i].desc));
+                fprintf(outFile, "      \"values\": [\n");
+                for (int e = 0; e < enums[i].valueCount; e++)
+                {
+                    fprintf(outFile, "        {\n");
+                    fprintf(outFile, "          \"name\": \"%s\",\n", enums[i].valueName[e]);
+                    fprintf(outFile, "          \"value\": %i,\n", enums[i].valueInteger[e]);
+                    fprintf(outFile, "          \"description\": \"%s\"\n", EscapeBackslashes(enums[i].valueDesc[e]));
+                    fprintf(outFile, "        }");
+                    if (e < enums[i].valueCount - 1) fprintf(outFile, ",\n");
+                    else fprintf(outFile, "\n");
+                }
+                fprintf(outFile, "      ]\n");
+                fprintf(outFile, "    }");
+                if (i < enumCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
+            // Print callbacks info
+            fprintf(outFile, "  \"callbacks\": [\n");
+            for (int i = 0; i < callbackCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", callbacks[i].name);
+                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(callbacks[i].desc));
+                fprintf(outFile, "      \"returnType\": \"%s\"", callbacks[i].retType);
+
+                if (callbacks[i].paramCount == 0) fprintf(outFile, "\n");
+                else
+                {
+                    fprintf(outFile, ",\n      \"params\": [\n");
+                    for (int p = 0; p < callbacks[i].paramCount; p++)
+                    {
+                        fprintf(outFile, "        {\n");
+                        fprintf(outFile, "          \"type\": \"%s\",\n", callbacks[i].paramType[p]);
+                        fprintf(outFile, "          \"name\": \"%s\"\n", callbacks[i].paramName[p]);
+                        fprintf(outFile, "        }");
+                        if (p < callbacks[i].paramCount - 1) fprintf(outFile, ",\n");
+                        else fprintf(outFile, "\n");
+                    }
+                    fprintf(outFile, "      ]\n");
+                }
+                fprintf(outFile, "    }");
+
+                if (i < callbackCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ],\n");
+
+            // Print functions info
+            fprintf(outFile, "  \"functions\": [\n");
+            for (int i = 0; i < funcCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      \"name\": \"%s\",\n", funcs[i].name);
+                fprintf(outFile, "      \"description\": \"%s\",\n", EscapeBackslashes(funcs[i].desc));
+                fprintf(outFile, "      \"returnType\": \"%s\"", funcs[i].retType);
+
+                if (funcs[i].paramCount == 0) fprintf(outFile, "\n");
+                else
+                {
+                    fprintf(outFile, ",\n      \"params\": [\n");
+                    for (int p = 0; p < funcs[i].paramCount; p++)
+                    {
+                        fprintf(outFile, "        {\n");
+                        fprintf(outFile, "          \"type\": \"%s\",\n", funcs[i].paramType[p]);
+                        fprintf(outFile, "          \"name\": \"%s\"\n", funcs[i].paramName[p]);
+                        fprintf(outFile, "        }");
+                        if (p < funcs[i].paramCount - 1) fprintf(outFile, ",\n");
+                        else fprintf(outFile, "\n");
+                    }
+                    fprintf(outFile, "      ]\n");
+                }
+                fprintf(outFile, "    }");
+
+                if (i < funcCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  ]\n");
+            fprintf(outFile, "}\n");
+        } break;
+        case XML:
+        {
+            // XML format to export data:
+            /*
+            <?xml version="1.0" encoding="Windows-1252" ?>
+            <raylibAPI>
+                <Defines count="">
+                    <Define name="" type="" value="" desc="" />
+                </Defines>
+                <Structs count="">
+                    <Struct name="" fieldCount="" desc="">
+                        <Field type="" name="" desc="" />
+                        <Field type="" name="" desc="" />
+                    </Struct>
+                <Structs>
+                <Aliases count="">
+                    <Alias type="" name="" desc="" />
+                </Aliases>
+                <Enums count="">
+                    <Enum name="" valueCount="" desc="">
+                        <Value name="" integer="" desc="" />
+                        <Value name="" integer="" desc="" />
+                    </Enum>
+                </Enums>
+                <Callbacks count="">
+                    <Callback name="" retType="" paramCount="" desc="">
+                        <Param type="" name="" desc="" />
+                        <Param type="" name="" desc="" />
+                    </Callback>
+                </Callbacks>
+                <Functions count="">
+                    <Function name="" retType="" paramCount="" desc="">
+                        <Param type="" name="" desc="" />
+                        <Param type="" name="" desc="" />
+                    </Function>
+                </Functions>
+            </raylibAPI>
+            */
+
+            fprintf(outFile, "<?xml version=\"1.0\" encoding=\"Windows-1252\" ?>\n");
+            fprintf(outFile, "<raylibAPI>\n");
+
+            // Print defines info
+            fprintf(outFile, "    <Defines count=\"%i\">\n", defineCount);
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "        <Define name=\"%s\" type=\"%s\" ", defines[i].name, StrDefineType(defines[i].type));
+                if (defines[i].type == STRING)
+                {
+                    fprintf(outFile, "value=%s", defines[i].value);
+                }
+                else
+                {
+                    fprintf(outFile, "value=\"%s\"", defines[i].value);
+                }
+                fprintf(outFile, " desc=\"%s\" />\n", defines[i].desc);
+            }
+            fprintf(outFile, "    </Defines>\n");
+
+            // Print structs info
+            fprintf(outFile, "    <Structs count=\"%i\">\n", structCount);
+            for (int i = 0; i < structCount; i++)
+            {
+                fprintf(outFile, "        <Struct name=\"%s\" fieldCount=\"%i\" desc=\"%s\">\n", structs[i].name, structs[i].fieldCount, structs[i].desc);
+                for (int f = 0; f < structs[i].fieldCount; f++)
+                {
+                    fprintf(outFile, "            <Field type=\"%s\" name=\"%s\" desc=\"%s\" />\n", structs[i].fieldType[f], structs[i].fieldName[f], structs[i].fieldDesc[f]);
+                }
+                fprintf(outFile, "        </Struct>\n");
+            }
+            fprintf(outFile, "    </Structs>\n");
+
+            // Print aliases info
+            fprintf(outFile, "    <Aliases count=\"%i\">\n", aliasCount);
+            for (int i = 0; i < aliasCount; i++)
+            {
+                fprintf(outFile, "        <Alias type=\"%s\" name=\"%s\" desc=\"%s\" />\n", aliases[i].name, aliases[i].type, aliases[i].desc);
+            }
+            fprintf(outFile, "    </Aliases>\n");
+
+            // Print enums info
+            fprintf(outFile, "    <Enums count=\"%i\">\n", enumCount);
+            for (int i = 0; i < enumCount; i++)
+            {
+                fprintf(outFile, "        <Enum name=\"%s\" valueCount=\"%i\" desc=\"%s\">\n", enums[i].name, enums[i].valueCount, enums[i].desc);
+                for (int v = 0; v < enums[i].valueCount; v++)
+                {
+                    fprintf(outFile, "            <Value name=\"%s\" integer=\"%i\" desc=\"%s\" />\n", enums[i].valueName[v], enums[i].valueInteger[v], enums[i].valueDesc[v]);
+                }
+                fprintf(outFile, "        </Enum>\n");
+            }
+            fprintf(outFile, "    </Enums>\n");
+
+            // Print callbacks info
+            fprintf(outFile, "    <Callbacks count=\"%i\">\n", callbackCount);
+            for (int i = 0; i < callbackCount; i++)
+            {
+                fprintf(outFile, "        <Callback name=\"%s\" retType=\"%s\" paramCount=\"%i\" desc=\"%s\">\n", callbacks[i].name, callbacks[i].retType, callbacks[i].paramCount, callbacks[i].desc);
+                for (int p = 0; p < callbacks[i].paramCount; p++)
+                {
+                    fprintf(outFile, "            <Param type=\"%s\" name=\"%s\" desc=\"%s\" />\n", callbacks[i].paramType[p], callbacks[i].paramName[p], callbacks[i].paramDesc[p]);
+                }
+                fprintf(outFile, "        </Callback>\n");
+            }
+            fprintf(outFile, "    </Callbacks>\n");
+
+            // Print functions info
+            fprintf(outFile, "    <Functions count=\"%i\">\n", funcCount);
+            for (int i = 0; i < funcCount; i++)
+            {
+                fprintf(outFile, "        <Function name=\"%s\" retType=\"%s\" paramCount=\"%i\" desc=\"%s\">\n", funcs[i].name, funcs[i].retType, funcs[i].paramCount, funcs[i].desc);
+                for (int p = 0; p < funcs[i].paramCount; p++)
+                {
+                    fprintf(outFile, "            <Param type=\"%s\" name=\"%s\" desc=\"%s\" />\n", funcs[i].paramType[p], funcs[i].paramName[p], funcs[i].paramDesc[p]);
+                }
+                fprintf(outFile, "        </Function>\n");
+            }
+            fprintf(outFile, "    </Functions>\n");
+
+            fprintf(outFile, "</raylibAPI>\n");
+
+        } break;
+        case LUA:
+        {
+            fprintf(outFile, "return {\n");
+
+            // Print defines info
+            fprintf(outFile, "  defines = {\n");
+            for (int i = 0; i < defineCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", defines[i].name);
+                fprintf(outFile, "      type = \"%s\",\n", StrDefineType(defines[i].type));
+                if ((defines[i].type == INT) ||
+                    (defines[i].type == LONG) ||
+                    (defines[i].type == FLOAT) ||
+                    (defines[i].type == DOUBLE) ||
+                    (defines[i].type == STRING))
+                {
+                    fprintf(outFile, "      value = %s,\n", defines[i].value);
+                }
+                else
+                {
+                    fprintf(outFile, "      value = \"%s\",\n", defines[i].value);
+                }
+                fprintf(outFile, "      description = \"%s\"\n", defines[i].desc);
+                fprintf(outFile, "    }");
+
+                if (i < defineCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
+            // Print structs info
+            fprintf(outFile, "  structs = {\n");
+            for (int i = 0; i < structCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", structs[i].name);
+                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(structs[i].desc));
+                fprintf(outFile, "      fields = {\n");
+                for (int f = 0; f < structs[i].fieldCount; f++)
+                {
+                    fprintf(outFile, "        {\n");
+                    fprintf(outFile, "          type = \"%s\",\n", structs[i].fieldType[f]);
+                    fprintf(outFile, "          name = \"%s\",\n", structs[i].fieldName[f]);
+                    fprintf(outFile, "          description = \"%s\"\n", EscapeBackslashes(structs[i].fieldDesc[f]));
+                    fprintf(outFile, "        }");
+                    if (f < structs[i].fieldCount - 1) fprintf(outFile, ",\n");
+                    else fprintf(outFile, "\n");
+                }
+                fprintf(outFile, "      }\n");
+                fprintf(outFile, "    }");
+                if (i < structCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
+            // Print aliases info
+            fprintf(outFile, "  aliases = {\n");
+            for (int i = 0; i < aliasCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      type = \"%s\",\n", aliases[i].type);
+                fprintf(outFile, "      name = \"%s\",\n", aliases[i].name);
+                fprintf(outFile, "      description = \"%s\"\n", aliases[i].desc);
+                fprintf(outFile, "    }");
+
+                if (i < aliasCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
+            // Print enums info
+            fprintf(outFile, "  enums = {\n");
+            for (int i = 0; i < enumCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", enums[i].name);
+                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(enums[i].desc));
+                fprintf(outFile, "      values = {\n");
+                for (int e = 0; e < enums[i].valueCount; e++)
+                {
+                    fprintf(outFile, "        {\n");
+                    fprintf(outFile, "          name = \"%s\",\n", enums[i].valueName[e]);
+                    fprintf(outFile, "          value = %i,\n", enums[i].valueInteger[e]);
+                    fprintf(outFile, "          description = \"%s\"\n", EscapeBackslashes(enums[i].valueDesc[e]));
+                    fprintf(outFile, "        }");
+                    if (e < enums[i].valueCount - 1) fprintf(outFile, ",\n");
+                    else fprintf(outFile, "\n");
+                }
+                fprintf(outFile, "      }\n");
+                fprintf(outFile, "    }");
+                if (i < enumCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
+            // Print callbacks info
+            fprintf(outFile, "  callbacks = {\n");
+            for (int i = 0; i < callbackCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", callbacks[i].name);
+                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(callbacks[i].desc));
+                fprintf(outFile, "      returnType = \"%s\"", callbacks[i].retType);
+
+                if (callbacks[i].paramCount == 0) fprintf(outFile, "\n");
+                else
+                {
+                    fprintf(outFile, ",\n      params = {\n");
+                    for (int p = 0; p < callbacks[i].paramCount; p++)
+                    {
+                        fprintf(outFile, "        {type = \"%s\", name = \"%s\"}", callbacks[i].paramType[p], callbacks[i].paramName[p]);
+                        if (p < callbacks[i].paramCount - 1) fprintf(outFile, ",\n");
+                        else fprintf(outFile, "\n");
+                    }
+                    fprintf(outFile, "      }\n");
+                }
+                fprintf(outFile, "    }");
+
+                if (i < callbackCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  },\n");
+
+            // Print functions info
+            fprintf(outFile, "  functions = {\n");
+            for (int i = 0; i < funcCount; i++)
+            {
+                fprintf(outFile, "    {\n");
+                fprintf(outFile, "      name = \"%s\",\n", funcs[i].name);
+                fprintf(outFile, "      description = \"%s\",\n", EscapeBackslashes(funcs[i].desc));
+                fprintf(outFile, "      returnType = \"%s\"", funcs[i].retType);
+
+                if (funcs[i].paramCount == 0) fprintf(outFile, "\n");
+                else
+                {
+                    fprintf(outFile, ",\n      params = {\n");
+                    for (int p = 0; p < funcs[i].paramCount; p++)
+                    {
+                        fprintf(outFile, "        {type = \"%s\", name = \"%s\"}", funcs[i].paramType[p], funcs[i].paramName[p]);
+                        if (p < funcs[i].paramCount - 1) fprintf(outFile, ",\n");
+                        else fprintf(outFile, "\n");
+                    }
+                    fprintf(outFile, "      }\n");
+                }
+                fprintf(outFile, "    }");
+
+                if (i < funcCount - 1) fprintf(outFile, ",\n");
+                else fprintf(outFile, "\n");
+            }
+            fprintf(outFile, "  }\n");
+            fprintf(outFile, "}\n");
+        } break;
+        case CODE:
+        default: break;
+    }
+
+    fclose(outFile);
+}
diff --git a/src/Raylib/Core.hs b/src/Raylib/Core.hs
--- a/src/Raylib/Core.hs
+++ b/src/Raylib/Core.hs
@@ -51,6 +51,7 @@
     getMonitorName,
     setClipboardText,
     getClipboardText,
+    getClipboardImage,
     enableEventWaiting,
     disableEventWaiting,
     swapScreenBuffer,
@@ -122,6 +123,12 @@
     exportDataAsCode,
     loadFileText,
     saveFileText,
+    fileRename,
+    fileRemove,
+    fileCopy,
+    fileMove,
+    fileTextReplace,
+    fileTextFindIndex,
     fileExists,
     directoryExists,
     isFileExtension,
@@ -253,6 +260,7 @@
     c'getMonitorName,
     c'setClipboardText,
     c'getClipboardText,
+    c'getClipboardImage,
     c'enableEventWaiting,
     c'disableEventWaiting,
     c'swapScreenBuffer,
@@ -328,6 +336,12 @@
     c'unloadFileText,
     c'saveFileText,
     c'fileExists,
+    c'fileRename,
+    c'fileRemove,
+    c'fileCopy,
+    c'fileMove,
+    c'fileTextReplace,
+    c'fileTextFindIndex,
     c'directoryExists,
     c'isFileExtension,
     c'getFileLength,
@@ -438,6 +452,7 @@
   )
 import Foreign.C
   ( CBool (..),
+    CChar (..),
     CDouble (..),
     CFloat (..),
     CInt (..),
@@ -544,6 +559,7 @@
        ("c'getMonitorName", "GetMonitorName_", "rl_bindings.h", [t|CInt -> IO CString|]),
        ("c'setClipboardText", "SetClipboardText_", "rl_bindings.h", [t|CString -> IO ()|]),
        ("c'getClipboardText", "GetClipboardText_", "rl_bindings.h", [t|IO CString|]),
+       ("c'getClipboardImage", "GetClipboardImage_", "rl_bindings.h", [t|IO (Ptr Image)|]),
        ("c'enableEventWaiting", "EnableEventWaiting_", "rl_bindings.h", [t|IO ()|]),
        ("c'disableEventWaiting", "DisableEventWaiting_", "rl_bindings.h", [t|IO ()|]),
        ("c'swapScreenBuffer", "SwapScreenBuffer_", "rl_bindings.h", [t|IO ()|]),
@@ -620,6 +636,12 @@
        ("c'unloadFileText", "UnloadFileText_", "rl_bindings.h", [t|CString -> IO ()|]),
        ("c'saveFileText", "SaveFileText_", "rl_bindings.h", [t|CString -> CString -> IO CBool|]),
        ("c'fileExists", "FileExists_", "rl_bindings.h", [t|CString -> IO CBool|]),
+       ("c'fileRename", "FileRename_", "rl_bindings.h", [t|CString -> CString -> IO CInt|]),
+       ("c'fileRemove", "FileRemove_", "rl_bindings.h", [t|CString -> IO CInt|]),
+       ("c'fileCopy", "FileCopy_", "rl_bindings.h", [t|CString -> CString -> IO CInt|]),
+       ("c'fileMove", "FileMove_", "rl_bindings.h", [t|CString -> CString -> IO CInt|]),
+       ("c'fileTextReplace", "FileTextReplace_", "rl_bindings.h", [t|CString -> CString -> CString -> IO CInt|]),
+       ("c'fileTextFindIndex", "FileTextFindIndex_", "rl_bindings.h", [t|CString -> CString -> IO CInt|]),
        ("c'directoryExists", "DirectoryExists_", "rl_bindings.h", [t|CString -> IO CBool|]),
        ("c'isFileExtension", "IsFileExtension_", "rl_bindings.h", [t|CString -> CString -> IO CBool|]),
        ("c'getFileLength", "GetFileLength_", "rl_bindings.h", [t|CString -> IO CBool|]),
@@ -644,7 +666,7 @@
        ("c'compressData", "CompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|]),
        ("c'decompressData", "DecompressData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO (Ptr CUChar)|]),
        ("c'encodeDataBase64", "EncodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> Ptr CInt -> IO CString|]),
-       ("c'decodeDataBase64", "DecodeDataBase64_", "rl_bindings.h", [t|Ptr CUChar -> Ptr CInt -> IO (Ptr CUChar)|]),
+       ("c'decodeDataBase64", "DecodeDataBase64_", "rl_bindings.h", [t|Ptr CChar -> Ptr CInt -> IO (Ptr CUChar)|]),
        ("c'computeCRC32", "ComputeCRC32_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> IO CUInt|]),
        ("c'computeMD5", "ComputeMD5_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> IO (Ptr CUInt)|]),
        ("c'computeSHA1", "ComputeSHA1_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> IO (Ptr CUInt)|]),
@@ -857,6 +879,9 @@
 getClipboardText :: IO String
 getClipboardText = c'getClipboardText >>= peekCString
 
+getClipboardImage :: IO Image
+getClipboardImage = c'getClipboardImage >>= pop
+
 enableEventWaiting :: IO ()
 enableEventWaiting = c'enableEventWaiting
 
@@ -1079,7 +1104,7 @@
   c <- createLoadFileDataCallback callback
   c'setLoadFileDataCallback c
 
-setSaveFileDataCallback :: (Storable a) => SaveFileDataCallback a -> IO ()
+setSaveFileDataCallback :: SaveFileDataCallback a -> IO ()
 setSaveFileDataCallback callback = do
   c <- createSaveFileDataCallback callback
   c'setSaveFileDataCallback c
@@ -1108,7 +1133,7 @@
           )
     )
 
-saveFileData :: (Storable a) => String -> Ptr a -> Integer -> IO Bool
+saveFileData :: String -> Ptr a -> Integer -> IO Bool
 saveFileData fileName contents bytesToWrite =
   toBool <$> withCString fileName (\s -> c'saveFileData s (castPtr contents) (fromIntegral bytesToWrite))
 
@@ -1125,6 +1150,29 @@
 fileExists :: String -> IO Bool
 fileExists fileName = toBool <$> withCString fileName c'fileExists
 
+fileRename :: String -> String -> IO Int
+fileRename fileName fileRename_ = fromIntegral <$> withCString fileName (\fn -> withCString fileRename_ (\rn -> c'fileRename fn rn))
+
+fileRemove :: String -> IO Int
+fileRemove fileName = fromIntegral <$> withCString fileName c'fileRemove
+
+fileCopy :: String -> String -> IO Int
+fileCopy srcPath dstPath = fromIntegral <$> withCString srcPath (\sp -> withCString dstPath (\dp -> c'fileCopy sp dp))
+
+fileMove :: String -> String -> IO Int
+fileMove srcPath dstPath = fromIntegral <$> withCString srcPath (\sp -> withCString dstPath (\dp -> c'fileMove sp dp))
+
+fileTextReplace :: String -> String -> String -> IO Int
+fileTextReplace fileName search replacement = fromIntegral <$>
+  withCString fileName (\fn ->
+    withCString search (\s ->
+      withCString replacement (\r -> c'fileTextReplace fn s r)))
+
+fileTextFindIndex :: String -> String -> IO Int
+fileTextFindIndex fileName search = fromIntegral <$>
+  withCString fileName (\fn ->
+    withCString search (\s -> c'fileTextFindIndex fn s))
+
 directoryExists :: String -> IO Bool
 directoryExists dirPath = toBool <$> withCString dirPath c'directoryExists
 
@@ -1486,7 +1534,7 @@
           newArray (map fromIntegral arr :: [CUChar])
     )
 
-createSaveFileDataCallback :: (Storable a) => SaveFileDataCallback a -> IO C'SaveFileDataCallback
+createSaveFileDataCallback :: SaveFileDataCallback a -> IO C'SaveFileDataCallback
 createSaveFileDataCallback callback =
   mk'saveFileDataCallback
     ( \fileName contents bytesToWrite ->
diff --git a/src/Raylib/Core/Models.hs b/src/Raylib/Core/Models.hs
--- a/src/Raylib/Core/Models.hs
+++ b/src/Raylib/Core/Models.hs
@@ -69,7 +69,7 @@
     loadModelAnimations,
     updateModelAnimation,
     isModelAnimationValid,
-    updateModelAnimationBoneMatrices,
+    updateModelAnimationBones,
     checkCollisionSpheres,
     checkCollisionBoxes,
     checkCollisionBoxSphere,
@@ -147,7 +147,7 @@
     c'unloadModelAnimation,
     c'unloadModelAnimations,
     c'isModelAnimationValid,
-    c'updateModelAnimationBoneMatrices,
+    c'updateModelAnimationBones,
     c'checkCollisionSpheres,
     c'checkCollisionBoxes,
     c'checkCollisionBoxSphere,
@@ -266,7 +266,7 @@
        ("c'unloadModelAnimation", "UnloadModelAnimation_", "rl_bindings.h", [t|Ptr ModelAnimation -> IO ()|]),
        ("c'unloadModelAnimations", "UnloadModelAnimations_", "rl_bindings.h", [t|Ptr ModelAnimation -> CInt -> IO ()|]),
        ("c'isModelAnimationValid", "IsModelAnimationValid_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> IO CBool|]),
-       ("c'updateModelAnimationBoneMatrices", "UpdateModelAnimationBoneMatrices_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> CInt -> IO ()|]),
+       ("c'updateModelAnimationBones", "UpdateModelAnimationBones_", "rl_bindings.h", [t|Ptr Model -> Ptr ModelAnimation -> CInt -> IO ()|]),
        ("c'checkCollisionSpheres", "CheckCollisionSpheres_", "rl_bindings.h", [t|Ptr Vector3 -> CFloat -> Ptr Vector3 -> CFloat -> IO CBool|]),
        ("c'checkCollisionBoxes", "CheckCollisionBoxes_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr BoundingBox -> IO CBool|]),
        ("c'checkCollisionBoxSphere", "CheckCollisionBoxSphere_", "rl_bindings.h", [t|Ptr BoundingBox -> Ptr Vector3 -> CFloat -> IO CBool|]),
@@ -514,8 +514,8 @@
 isModelAnimationValid :: Model -> ModelAnimation -> IO Bool
 isModelAnimationValid model animation = toBool <$> withFreeable model (withFreeable animation . c'isModelAnimationValid)
 
-updateModelAnimationBoneMatrices :: Model -> ModelAnimation -> Int -> IO ()
-updateModelAnimationBoneMatrices model animation frame = withFreeable model (\m -> withFreeable animation (\a -> c'updateModelAnimationBoneMatrices m a (fromIntegral frame)))
+updateModelAnimationBones :: Model -> ModelAnimation -> Int -> IO ()
+updateModelAnimationBones model animation frame = withFreeable model (\m -> withFreeable animation (\a -> c'updateModelAnimationBones m a (fromIntegral frame)))
 
 checkCollisionSpheres :: Vector3 -> Float -> Vector3 -> Float -> Bool
 checkCollisionSpheres center1 radius1 center2 radius2 = toBool $ unsafePerformIO (withFreeable center1 (\c1 -> withFreeable center2 (\c2 -> c'checkCollisionSpheres c1 (realToFrac radius1) c2 (realToFrac radius2))))
diff --git a/src/Raylib/Core/Shapes.hs b/src/Raylib/Core/Shapes.hs
--- a/src/Raylib/Core/Shapes.hs
+++ b/src/Raylib/Core/Shapes.hs
@@ -14,6 +14,7 @@
     drawLineEx,
     drawLineStrip,
     drawLineBezier,
+    drawLineDashed,
     drawCircle,
     drawCircleSector,
     drawCircleSectorLines,
@@ -22,7 +23,9 @@
     drawCircleLines,
     drawCircleLinesV,
     drawEllipse,
+    drawEllipseV,
     drawEllipseLines,
+    drawEllipseLinesV,
     drawRing,
     drawRingLines,
     drawRectangle,
@@ -82,6 +85,7 @@
     c'drawLineEx,
     c'drawLineStrip,
     c'drawLineBezier,
+    c'drawLineDashed,
     c'drawCircle,
     c'drawCircleSector,
     c'drawCircleSectorLines,
@@ -90,7 +94,9 @@
     c'drawCircleLines,
     c'drawCircleLinesV,
     c'drawEllipse,
+    c'drawEllipseV,
     c'drawEllipseLines,
+    c'drawEllipseLinesV,
     c'drawRing,
     c'drawRingLines,
     c'drawRectangle,
@@ -164,6 +170,7 @@
        ("c'drawLineEx", "DrawLineEx_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawLineStrip", "DrawLineStrip_", "rl_bindings.h", [t|Ptr Vector2 -> CInt -> Ptr Color -> IO ()|]),
        ("c'drawLineBezier", "DrawLineBezier_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawLineDashed", "DrawLineDashed_", "rl_bindings.h", [t|Ptr Vector2 -> Ptr Vector2 -> CInt -> CInt -> Ptr Color -> IO ()|]),
        ("c'drawCircle", "DrawCircle_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawCircleSector", "DrawCircleSector_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
        ("c'drawCircleSectorLines", "DrawCircleSectorLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
@@ -172,7 +179,9 @@
        ("c'drawCircleLines", "DrawCircleLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawCircleLinesV", "DrawCircleLinesV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawEllipse", "DrawEllipse_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawEllipseV", "DrawEllipseV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawEllipseLines", "DrawEllipseLines_", "rl_bindings.h", [t|CInt -> CInt -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
+       ("c'drawEllipseLinesV", "DrawEllipseLinesV_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> Ptr Color -> IO ()|]),
        ("c'drawRing", "DrawRing_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
        ("c'drawRingLines", "DrawRingLines_", "rl_bindings.h", [t|Ptr Vector2 -> CFloat -> CFloat -> CFloat -> CFloat -> CInt -> Ptr Color -> IO ()|]),
        ("c'drawRectangle", "DrawRectangle_", "rl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
@@ -256,6 +265,10 @@
 drawLineBezier start end thickness color =
   withFreeable start (\s -> withFreeable end (\e -> withFreeable color (c'drawLineBezier s e (realToFrac thickness))))
 
+drawLineDashed :: Vector2 -> Vector2 -> Int -> Int -> Color -> IO ()
+drawLineDashed start end dashSize spaceSize color =
+  withFreeable start (\s -> withFreeable end (\e -> withFreeable color (c'drawLineDashed s e (fromIntegral dashSize) (fromIntegral spaceSize))))
+
 drawCircle :: Int -> Int -> Float -> Color -> IO ()
 drawCircle centerX centerY radius color = withFreeable color (c'drawCircle (fromIntegral centerX) (fromIntegral centerY) (realToFrac radius))
 
@@ -301,10 +314,18 @@
 drawEllipse centerX centerY radiusH radiusV color =
   withFreeable color (c'drawEllipse (fromIntegral centerX) (fromIntegral centerY) (realToFrac radiusH) (realToFrac radiusV))
 
+drawEllipseV :: Vector2 -> Float -> Float -> Color -> IO ()
+drawEllipseV center radiusH radiusV color =
+  withFreeable center (\c -> withFreeable color (c'drawEllipseV c (realToFrac radiusH) (realToFrac radiusV)))
+
 drawEllipseLines :: Int -> Int -> Float -> Float -> Color -> IO ()
 drawEllipseLines centerX centerY radiusH radiusV color =
   withFreeable color (c'drawEllipseLines (fromIntegral centerX) (fromIntegral centerY) (realToFrac radiusH) (realToFrac radiusV))
 
+drawEllipseLinesV :: Vector2 -> Float -> Float -> Color -> IO ()
+drawEllipseLinesV center radiusH radiusV color =
+  withFreeable center (\c -> withFreeable color (c'drawEllipseLinesV c (realToFrac radiusH) (realToFrac radiusV)))
+
 drawRing :: Vector2 -> Float -> Float -> Float -> Float -> Int -> Color -> IO ()
 drawRing center innerRadius outerRadius startAngle endAngle segments color =
   withFreeable
@@ -378,7 +399,7 @@
     )
 
 drawRectangleGradientEx :: Rectangle -> Color -> Color -> Color -> Color -> IO ()
-drawRectangleGradientEx rect topLeft bottomLeft topRight bottomRight =
+drawRectangleGradientEx rect topLeft bottomLeft bottomRight topRight =
   withFreeable
     rect
     ( \r ->
@@ -388,7 +409,7 @@
               withFreeable
                 bottomLeft
                 ( \c2 ->
-                    withFreeable topRight (withFreeable bottomRight . c'drawRectangleGradientEx r c1 c2)
+                    withFreeable bottomRight (withFreeable topRight . c'drawRectangleGradientEx r c1 c2)
                 )
           )
     )
diff --git a/src/Raylib/Core/Text.hs b/src/Raylib/Core/Text.hs
--- a/src/Raylib/Core/Text.hs
+++ b/src/Raylib/Core/Text.hs
@@ -103,7 +103,7 @@
        ("c'loadFontEx", "LoadFontEx_", "rl_bindings.h", [t|CString -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|]),
        ("c'loadFontFromImage", "LoadFontFromImage_", "rl_bindings.h", [t|Ptr Image -> Ptr Color -> CInt -> IO (Ptr Font)|]),
        ("c'loadFontFromMemory", "LoadFontFromMemory_", "rl_bindings.h", [t|CString -> Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> IO (Ptr Font)|]),
-       ("c'loadFontData", "LoadFontData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> CInt -> IO (Ptr GlyphInfo)|]),
+       ("c'loadFontData", "LoadFontData_", "rl_bindings.h", [t|Ptr CUChar -> CInt -> CInt -> Ptr CInt -> CInt -> CInt -> Ptr CInt -> IO (Ptr GlyphInfo)|]),
        ("c'genImageFontAtlas", "GenImageFontAtlas_", "rl_bindings.h", [t|Ptr GlyphInfo -> Ptr (Ptr Rectangle) -> CInt -> CInt -> CInt -> CInt -> IO (Ptr Image)|]),
        ("c'unloadFontData", "UnloadFontData_", "rl_bindings.h", [t|Ptr GlyphInfo -> CInt -> IO ()|]),
        ("c'isFontValid", "IsFontValid_", "rl_bindings.h", [t|Ptr Font -> IO CBool|]),
@@ -168,17 +168,28 @@
     )
     >>= pop
 
-loadFontData :: [Integer] -> Int -> Maybe [Int] -> FontType -> IO GlyphInfo
+loadFontData :: [Integer] -> Int -> Maybe [Int] -> FontType -> IO [GlyphInfo]
 loadFontData fileData fontSize codepoints fontType =
   withFreeableArrayLen
     (map fromIntegral fileData)
     ( \size d ->
         case codepoints of
           Just codepoints' ->
-            withFreeableArrayLen (map fromIntegral codepoints') (\l c -> c'loadFontData d (fromIntegral (size * sizeOf (0 :: CUChar))) (fromIntegral fontSize) c (fromIntegral l) (fromIntegral (fromEnum fontType)))
-          Nothing -> c'loadFontData d (fromIntegral (size * sizeOf (0 :: CUChar))) (fromIntegral fontSize) nullPtr 0 (fromIntegral (fromEnum fontType))
+            withFreeableArrayLen
+              (map fromIntegral codepoints')
+              (\l c ->
+                withFreeable 0
+                  (\i -> do
+                    arr <- c'loadFontData d (fromIntegral (size * sizeOf (0 :: CUChar))) (fromIntegral fontSize) c (fromIntegral l) (fromIntegral (fromEnum fontType)) i
+                    al <- peek i
+                    popCArray (fromIntegral al) arr))
+          Nothing ->
+            withFreeable 0
+              (\i -> do
+                arr <- c'loadFontData d (fromIntegral (size * sizeOf (0 :: CUChar))) (fromIntegral fontSize) nullPtr 0 (fromIntegral (fromEnum fontType)) i
+                al <- peek i
+                popCArray (fromIntegral al) arr)
     )
-    >>= pop
 
 genImageFontAtlas :: [GlyphInfo] -> [[Rectangle]] -> Int -> Int -> Int -> Int -> IO Image
 genImageFontAtlas chars recs glyphCount fontSize padding packMethod = withFreeableArray chars (\c -> withFreeableArray2D recs (\r -> c'genImageFontAtlas c r (fromIntegral glyphCount) (fromIntegral fontSize) (fromIntegral padding) (fromIntegral packMethod))) >>= pop
diff --git a/src/Raylib/Internal.hs b/src/Raylib/Internal.hs
--- a/src/Raylib/Internal.hs
+++ b/src/Raylib/Internal.hs
@@ -62,6 +62,7 @@
 
     -- * Miscellaneous
     c'rlGetShaderIdDefault,
+    c'rlGetShaderLocsDefault,
     getPixelDataSize,
   )
 where
@@ -151,6 +152,7 @@
 
 $( genNative
      [ ("c'rlGetShaderIdDefault", "rlGetShaderIdDefault_", "rlgl_bindings.h", [t|IO CUInt|]),
+       ("c'rlGetShaderLocsDefault", "rlGetShaderLocsDefault_", "rlgl_bindings.h", [t|IO (Ptr CInt)|]),
        ("c'rlUnloadShaderProgram", "rlUnloadShaderProgram_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
        ("c'rlUnloadTexture", "rlUnloadTexture_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
        ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
diff --git a/src/Raylib/Internal/Foreign.hs b/src/Raylib/Internal/Foreign.hs
--- a/src/Raylib/Internal/Foreign.hs
+++ b/src/Raylib/Internal/Foreign.hs
@@ -136,7 +136,7 @@
   rlFree val ptr
   return val
 
-popCArray :: (Freeable a, Storable a) => Int -> Ptr a -> IO [a]
+popCArray :: (Storable a) => Int -> Ptr a -> IO [a]
 popCArray count ptr = do
   str <- peekArray count ptr
   c'free $ castPtr ptr
diff --git a/src/Raylib/Types/Core.hs b/src/Raylib/Types/Core.hs
--- a/src/Raylib/Types/Core.hs
+++ b/src/Raylib/Types/Core.hs
@@ -172,7 +172,7 @@
   | BorderlessWindowedMode
   | Msaa4xHint
   | InterlacedHint
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Enum ConfigFlag where
   fromEnum g = case g of
@@ -212,7 +212,7 @@
     n -> error $ "(ConfigFlag.toEnum) Invalid value: " ++ show n
 
 data TraceLogLevel = LogAll | LogTrace | LogDebug | LogInfo | LogWarning | LogError | LogFatal | LogNone
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 data KeyboardKey
   = KeyNull
@@ -325,7 +325,7 @@
   | KeyMenu
   | KeyVolumeUp
   | KeyVolumeDown
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum KeyboardKey where
   fromEnum k = case k of
@@ -563,7 +563,7 @@
   | MouseButtonExtra
   | MouseButtonForward
   | MouseButtonBack
-  deriving (Eq, Show, Enum, Bounded)
+  deriving (Eq, Show, Read, Enum, Bounded)
 
 data MouseCursor
   = MouseCursorDefault
@@ -577,7 +577,7 @@
   | MouseCursorResizeNESW
   | MouseCursorResizeAll
   | MouseCursorNotAllowed
-  deriving (Eq, Show, Enum, Bounded)
+  deriving (Eq, Show, Read, Enum, Bounded)
 
 data GamepadButton
   = GamepadButtonUnknown
@@ -598,7 +598,7 @@
   | GamepadButtonMiddleRight
   | GamepadButtonLeftThumb
   | GamepadButtonRightThumb
-  deriving (Eq, Show, Enum, Bounded)
+  deriving (Eq, Show, Read, Enum, Bounded)
 
 data GamepadAxis
   = GamepadAxisLeftX
@@ -607,7 +607,7 @@
   | GamepadAxisRightY
   | GamepadAxisLeftTrigger
   | GamepadAxisRightTrigger
-  deriving (Eq, Show, Enum, Bounded)
+  deriving (Eq, Show, Read, Enum, Bounded)
 
 data BlendMode
   = BlendAlpha
@@ -632,7 +632,7 @@
   | GestureSwipeDown
   | GesturePinchIn
   | GesturePinchOut
-  deriving (Show, Eq)
+  deriving (Show, Read, Eq)
 
 instance Enum Gesture where
   fromEnum n = case n of
@@ -671,7 +671,7 @@
 
 #ifdef DISABLE_LENS
 
-data Vector2' = Vector2' Float Float deriving (Eq, Show, Freeable)
+data Vector2' = Vector2' Float Float deriving (Eq, Show, Read, Freeable)
 instance Storable Vector2' where
   sizeOf _ = 8
   alignment _ = 4
@@ -691,7 +691,7 @@
   } = Vector2' vector2'x vector2'y
 {-# COMPLETE Vector2 :: Vector2' #-}
 
-data Vector3' = Vector3' Float Float Float deriving (Eq, Show, Freeable)
+data Vector3' = Vector3' Float Float Float deriving (Eq, Show, Read, Freeable)
 
 instance Storable Vector3' where
   sizeOf _ = 12
@@ -716,7 +716,7 @@
   } = Vector3' vector3'x vector3'y vector3'z
 {-# COMPLETE Vector3 :: Vector3' #-}
 
-data Vector4' = Vector4' Float Float Float Float deriving (Eq, Show, Freeable)
+data Vector4' = Vector4' Float Float Float Float deriving (Eq, Show, Read, Freeable)
 
 instance Storable Vector4' where
   sizeOf _ = 16
@@ -825,7 +825,7 @@
     matrix'm11 :: Float,
     matrix'm15 :: Float
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Matrix where
   sizeOf _ = 64
@@ -921,7 +921,7 @@
     color'b :: Word8,
     color'a :: Word8
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Color where
   sizeOf _ = 4
@@ -957,7 +957,7 @@
     rectangle'width :: Float,
     rectangle'height :: Float
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Rectangle where
   sizeOf _ = 16
@@ -998,7 +998,7 @@
     vrDeviceInfo'lensDistortionValues :: [Float],
     vrDeviceInfo'chromaAbCorrection :: [Float]
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable VrDeviceInfo where
   sizeOf _ = 60
@@ -1065,7 +1065,7 @@
     vrStereoConfig'scale :: [Float],
     vrStereoConfig'scaleIn :: [Float]
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable VrStereoConfig where
   sizeOf _ = 304
@@ -1127,7 +1127,7 @@
   { filePathList'capacity :: Integer,
     filePathList'paths :: [String]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable FilePathList where
   sizeOf _ = 16
@@ -1168,7 +1168,7 @@
     automationEvent'type :: Integer,
     automationEvent'params :: [Int]
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable AutomationEvent where
   sizeOf _ = 24
@@ -1198,7 +1198,7 @@
   { automationEventList'capacity :: Integer,
     automationEventList'events :: [AutomationEvent]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable AutomationEventList where
   sizeOf _ = 16
diff --git a/src/Raylib/Types/Core/Audio.hs b/src/Raylib/Types/Core/Audio.hs
--- a/src/Raylib/Types/Core/Audio.hs
+++ b/src/Raylib/Types/Core/Audio.hs
@@ -99,7 +99,7 @@
   | MusicAudioQOA
   | MusicModuleXM
   | MusicModuleMOD
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable MusicContextType where
   sizeOf _ = 4
@@ -112,7 +112,7 @@
 data AudioBufferUsage
   = AudioBufferUsageStatic
   | AudioBufferUsageStream
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable AudioBufferUsage where
   sizeOf _ = 4
@@ -133,7 +133,7 @@
     wave'channels :: Integer,
     wave'data :: [Int]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Wave where
   sizeOf _ = 24
diff --git a/src/Raylib/Types/Core/Camera.hs b/src/Raylib/Types/Core/Camera.hs
--- a/src/Raylib/Types/Core/Camera.hs
+++ b/src/Raylib/Types/Core/Camera.hs
@@ -49,7 +49,7 @@
   | CameraModeThirdPerson
   deriving (Enum)
 
-data CameraProjection = CameraPerspective | CameraOrthographic deriving (Eq, Show, Enum)
+data CameraProjection = CameraPerspective | CameraOrthographic deriving (Eq, Show, Read, Enum)
 
 instance Storable CameraProjection where
   sizeOf _ = 4
@@ -70,7 +70,7 @@
     camera3D'fovy :: Float,
     camera3D'projection :: CameraProjection
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Camera3D where
   sizeOf _ = 44
@@ -113,7 +113,7 @@
     camera2D'rotation :: Float,
     camera2D'zoom :: Float
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Camera2D where
   sizeOf _ = 24
diff --git a/src/Raylib/Types/Core/Models.hs b/src/Raylib/Types/Core/Models.hs
--- a/src/Raylib/Types/Core/Models.hs
+++ b/src/Raylib/Types/Core/Models.hs
@@ -94,7 +94,6 @@
     mallocForeignPtr,
     mallocForeignPtrArray,
     newArray,
-    newForeignPtr,
     peekArray,
     plusPtr,
     pokeArray,
@@ -112,8 +111,8 @@
     castCharToCChar,
     peekCString,
   )
-import Raylib.Internal (Closeable (addToWindowResources, close), addShaderId, addTextureId, addVaoId, addVboIds, c'rlGetShaderIdDefault, c'rlUnloadShaderProgram, c'rlUnloadTexture, c'rlUnloadVertexArray, c'rlUnloadVertexBuffer)
-import Raylib.Internal.Foreign (Freeable (rlFreeDependents), c'free, freeMaybePtr, newMaybeArray, p'free, peekMaybeArray, peekStaticArray, pokeStaticArray, rightPad, rlFree, rlFreeMaybeArray)
+import Raylib.Internal (Closeable (addToWindowResources, close), addShaderId, addTextureId, addVaoId, addVboIds, c'rlGetShaderIdDefault, c'rlGetShaderLocsDefault, c'rlUnloadShaderProgram, c'rlUnloadTexture, c'rlUnloadVertexArray, c'rlUnloadVertexBuffer)
+import Raylib.Internal.Foreign (Freeable (rlFreeDependents), c'free, freeMaybePtr, newMaybeArray, peekMaybeArray, peekStaticArray, pokeStaticArray, rightPad, rlFree, rlFreeMaybeArray)
 import Raylib.Types.Core (Color, Matrix, Quaternion, Vector2, Vector3, Vector4, pattern Vector2, pattern Vector3, pattern Vector4)
 import Raylib.Types.Core.Textures (Texture (texture'id))
 
@@ -133,7 +132,7 @@
   | MaterialMapIrradiance
   | MaterialMapPrefilter
   | MaterialMapBrdf
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 data DefaultShaderAttributeLocation
   = DefaultShaderAttribLocationPosition
@@ -145,7 +144,7 @@
   | DefaultShaderAttribLocationIndices
   | DefaultShaderAttribLocationBoneIds
   | DefaultShaderAttribLocationBoneWeights
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 data ShaderLocationIndex
   = ShaderLocVertexPosition
@@ -177,7 +176,8 @@
   | ShaderLocVertexBoneIds
   | ShaderLocVertexBoneWeights
   | ShaderLocBoneMatrices
-  deriving (Eq, Show, Enum)
+  | ShaderLocVertexInstanceTx
+  deriving (Eq, Show, Read, Enum)
 
 data ShaderUniformDataType
   = ShaderUniformFloatType
@@ -188,8 +188,12 @@
   | ShaderUniformIVec2Type
   | ShaderUniformIVec3Type
   | ShaderUniformIVec4Type
+  | ShaderUniformUIntType
+  | ShaderUniformUIVec2Type
+  | ShaderUniformUIVec3Type
+  | ShaderUniformUIVec4Type
   | ShaderUniformSampler2DType
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 data ShaderUniformData
   = ShaderUniformFloat Float
@@ -201,7 +205,7 @@
   | ShaderUniformIVec3 (Int, Int, Int)
   | ShaderUniformIVec4 (Int, Int, Int, Int)
   | ShaderUniformSampler2D Texture
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 data ShaderUniformDataV
   = ShaderUniformFloatV [Float]
@@ -213,7 +217,7 @@
   | ShaderUniformIVec3V [(Int, Int, Int)]
   | ShaderUniformIVec4V [(Int, Int, Int, Int)]
   | ShaderUniformSampler2DV [Texture]
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 -- | Internal use
 unpackShaderUniformData :: ShaderUniformData -> IO (ShaderUniformDataType, ForeignPtr ())
@@ -321,7 +325,7 @@
   | ShaderAttribVec2
   | ShaderAttribVec3
   | ShaderAttribVec4
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 ---------------------------------------
 -- models structures ------------------
@@ -331,7 +335,7 @@
   { mesh'vertexCount :: Int,
     mesh'triangleCount :: Int,
     mesh'vertices :: [Vector3],
-    mesh'texcoords :: [Vector2],
+    mesh'texcoords :: Maybe [Vector2],
     mesh'texcoords2 :: Maybe [Vector2],
     mesh'normals :: [Vector3],
     mesh'tangents :: Maybe [Vector4],
@@ -347,7 +351,7 @@
     -- | Use `toEnum` on `DefaultShaderAttributeLocation` for indices
     mesh'vboId :: Maybe [Integer]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Mesh where
   sizeOf _ = 120
@@ -356,7 +360,7 @@
     vertexCount <- fromIntegral <$> peek (p'mesh'vertexCount _p)
     triangleCount <- fromIntegral <$> peek (p'mesh'triangleCount _p)
     vertices <- peekArray vertexCount =<< peek (p'mesh'vertices _p)
-    texcoords <- peekArray vertexCount =<< peek (p'mesh'texcoords _p)
+    texcoords <- peekMaybeArray vertexCount =<< peek (p'mesh'texcoords _p)
     texcoords2 <- peekMaybeArray vertexCount =<< peek (p'mesh'texcoords2 _p)
     normals <- peekArray vertexCount =<< peek (p'mesh'normals _p)
     tangents <- peekMaybeArray vertexCount =<< peek (p'mesh'tangents _p)
@@ -365,8 +369,8 @@
     animVertices <- peekMaybeArray vertexCount =<< peek (p'mesh'animVertices _p)
     animNormals <- peekMaybeArray vertexCount =<< peek (p'mesh'animNormals _p)
     boneCount <- fromIntegral <$> peek (p'mesh'boneCount _p)
-    boneIds <- (map fromIntegral <$>) <$> (peekMaybeArray boneCount =<< peek (p'mesh'boneIds _p))
-    boneWeights <- (map realToFrac <$>) <$> (peekMaybeArray boneCount =<< peek (p'mesh'boneWeights _p))
+    boneIds <- (map fromIntegral <$>) <$> (peekMaybeArray (4 * vertexCount) =<< peek (p'mesh'boneIds _p))
+    boneWeights <- (map realToFrac <$>) <$> (peekMaybeArray (4 * vertexCount) =<< peek (p'mesh'boneWeights _p))
     boneMatrices <- peekMaybeArray boneCount =<< peek (p'mesh'boneMatrices _p)
     vaoId <- fromIntegral <$> peek (p'mesh'vaoId _p)
     vboId <- (map fromIntegral <$>) <$> (peekMaybeArray 9 =<< peek (p'mesh'vboId _p))
@@ -375,7 +379,7 @@
     poke (p'mesh'vertexCount _p) (fromIntegral vertexCount)
     poke (p'mesh'triangleCount _p) (fromIntegral triangleCount)
     poke (p'mesh'vertices _p) =<< newArray vertices
-    poke (p'mesh'texcoords _p) =<< newArray texcoords
+    poke (p'mesh'texcoords _p) =<< newMaybeArray texcoords
     poke (p'mesh'texcoords2 _p) =<< newMaybeArray texcoords2
     poke (p'mesh'normals _p) =<< newArray normals
     poke (p'mesh'tangents _p) =<< newMaybeArray tangents
@@ -411,7 +415,7 @@
 p'mesh'vertices :: Ptr Mesh -> Ptr (Ptr Vector3)
 p'mesh'vertices = (`plusPtr` 8)
 
--- array (mesh'vertexCount)
+-- maybe array (mesh'vertexCount)
 p'mesh'texcoords :: Ptr Mesh -> Ptr (Ptr Vector2)
 p'mesh'texcoords = (`plusPtr` 16)
 
@@ -496,7 +500,7 @@
   { shader'id :: Integer,
     shader'locs :: [Int]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Shader where
   sizeOf _ = 16
@@ -507,13 +511,7 @@
     return $ Shader sId locs
   poke _p (Shader sId locs) = do
     poke (p'shader'id _p) (fromIntegral sId)
-    defaultShaderId <- c'rlGetShaderIdDefault
-    locsArr <- newArray (map fromIntegral locs)
-    if sId == fromIntegral defaultShaderId
-      then do
-        locsPtr <- newForeignPtr p'free locsArr
-        withForeignPtr locsPtr $ poke (p'shader'locs _p)
-      else poke (p'shader'locs _p) locsArr
+    poke (p'shader'locs _p) =<< newArray (map fromIntegral locs)
     return ()
 
 instance Closeable Shader where
@@ -531,21 +529,19 @@
 p'shader'locs = (`plusPtr` 8)
 
 instance Freeable Shader where
-  rlFreeDependents val ptr = do
-    defaultShaderId <- c'rlGetShaderIdDefault
+  rlFreeDependents _ ptr = do
+    defaultShaderLocs <- c'rlGetShaderLocsDefault
+    locsPtr <- peek (p'shader'locs ptr)
     unless
-      (shader'id val == fromIntegral defaultShaderId)
-      ( do
-          locsPtr <- peek (p'shader'locs ptr)
-          c'free $ castPtr locsPtr
-      )
+      (locsPtr == defaultShaderLocs)
+      (c'free $ castPtr locsPtr)
 
 data MaterialMap = MaterialMap
   { materialMap'texture :: Texture,
     materialMap'color :: Color,
     materialMap'value :: Float
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable MaterialMap where
   sizeOf _ = 28
@@ -575,7 +571,7 @@
     material'maps :: Maybe [MaterialMap],
     material'params :: [Float]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Material where
   sizeOf _ = 40
@@ -633,7 +629,7 @@
     transform'rotation :: Quaternion,
     transform'scale :: Vector3
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Transform where
   sizeOf _ = 40
@@ -662,7 +658,7 @@
   { boneInfo'name :: String,
     boneInfo'parent :: Int
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable BoneInfo where
   sizeOf _ = 36
@@ -692,7 +688,7 @@
     model'bones :: Maybe [BoneInfo],
     model'bindPose :: Maybe [Transform]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Model where
   sizeOf _ = 120
@@ -775,7 +771,7 @@
     modelAnimation'framePoses :: [[Transform]],
     modelAnimation'name :: String
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable ModelAnimation where
   sizeOf _ = 56
@@ -827,7 +823,7 @@
   { ray'position :: Vector3,
     ray'direction :: Vector3
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Ray where
   sizeOf _ = 24
@@ -853,7 +849,7 @@
     rayCollision'point :: Vector3,
     rayCollision'normal :: Vector3
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable RayCollision where
   sizeOf _ = 32
@@ -887,7 +883,7 @@
   { boundingBox'min :: Vector3,
     boundingBox'max :: Vector3
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable BoundingBox where
   sizeOf _ = 24
diff --git a/src/Raylib/Types/Core/Text.hs b/src/Raylib/Types/Core/Text.hs
--- a/src/Raylib/Types/Core/Text.hs
+++ b/src/Raylib/Types/Core/Text.hs
@@ -55,7 +55,7 @@
     glyphInfo'advanceX :: Int,
     glyphInfo'image :: Image
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable GlyphInfo where
   sizeOf _ = 40
@@ -101,7 +101,7 @@
     font'recs :: [Rectangle],
     font'glyphs :: [GlyphInfo]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Font where
   sizeOf _ = 48
diff --git a/src/Raylib/Types/Core/Textures.hs b/src/Raylib/Types/Core/Textures.hs
--- a/src/Raylib/Types/Core/Textures.hs
+++ b/src/Raylib/Types/Core/Textures.hs
@@ -90,7 +90,7 @@
   | PixelFormatCompressedPvrtRgba
   | PixelFormatCompressedAstc4x4Rgba
   | PixelFormatCompressedAstc8x8Rgba
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable PixelFormat where
   sizeOf _ = 4
@@ -179,10 +179,9 @@
   | CubemapLayoutLineHorizontal
   | CubemapLayoutCrossThreeByFour
   | CubemapLayoutCrossThreeByThree
-  | CubemapLayoutPanorama
   deriving (Enum)
 
-data NPatchLayout = NPatchNinePatch | NPatchThreePatchVertical | NPatchThreePatchHorizontal deriving (Eq, Show, Enum)
+data NPatchLayout = NPatchNinePatch | NPatchThreePatchVertical | NPatchThreePatchHorizontal deriving (Eq, Show, Read, Enum)
 
 instance Storable NPatchLayout where
   sizeOf _ = 4
@@ -203,7 +202,7 @@
     image'mipmaps :: Int,
     image'format :: PixelFormat
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable Image where
   sizeOf _ = 24
@@ -250,7 +249,7 @@
     texture'mipmaps :: Int,
     texture'format :: PixelFormat
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable Texture where
   sizeOf _ = 20
@@ -298,7 +297,7 @@
     renderTexture'texture :: Texture,
     renderTexture'depth :: Texture
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable RenderTexture where
   sizeOf _ = 44
@@ -342,7 +341,7 @@
     nPatchInfo'bottom :: Int,
     nPatchInfo'layout :: NPatchLayout
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable NPatchInfo where
   sizeOf _ = 36
diff --git a/src/Raylib/Types/Util/GUI.hs b/src/Raylib/Types/Util/GUI.hs
--- a/src/Raylib/Types/Util/GUI.hs
+++ b/src/Raylib/Types/Util/GUI.hs
@@ -18,7 +18,7 @@
     GuiComboBoxProperty (..),
     GuiDropdownBoxProperty (..),
     GuiTextBoxProperty (..),
-    GuiSpinnerProperty (..),
+    GuiValueBoxProperty (..),
     GuiListViewProperty (..),
     GuiColorPickerProperty (..),
     GuiIconName (..),
@@ -56,7 +56,7 @@
   | StateFocused
   | StatePressed
   | StateDisabled
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiState where
   fromEnum x = case x of
@@ -84,7 +84,7 @@
   = TextAlignLeft
   | TextAlignCenter
   | TextAlignRight
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiTextAlignment where
   fromEnum x = case x of
@@ -110,7 +110,7 @@
   = TextAlignTop
   | TextAlignMiddle
   | TextAlignBottom
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiTextAlignmentVertical where
   fromEnum x = case x of
@@ -136,7 +136,7 @@
   = TextWrapNone
   | TextWrapChar
   | TextWrapWord
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiTextWrapMode where
   fromEnum x = case x of
@@ -174,13 +174,12 @@
   | -- | Used also for: TEXTBOXMULTI
     Textbox
   | Valuebox
-  | -- | Uses: BUTTON, VALUEBOX
-    Spinner
+  | Control11
   | Listview
   | Colorpicker
   | Scrollbar
   | Statusbar
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiControl where
   fromEnum x = case x of
@@ -195,7 +194,7 @@
     Dropdownbox -> 8
     Textbox -> 9
     Valuebox -> 10
-    Spinner -> 11
+    Control11 -> 11
     Listview -> 12
     Colorpicker -> 13
     Scrollbar -> 14
@@ -212,7 +211,7 @@
     8 -> Dropdownbox
     9 -> Textbox
     10 -> Valuebox
-    11 -> Spinner
+    11 -> Control11
     12 -> Listview
     13 -> Colorpicker
     14 -> Scrollbar
@@ -259,7 +258,7 @@
     TextPadding
   | -- | Control text horizontal alignment inside control text bound (after border and padding)
     TextAlignment
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiControlProperty where
   fromEnum x = case x of
@@ -320,7 +319,7 @@
     TextAlignmentVertical
   | -- | Text wrap-mode inside text bounds
     TextWrapMode
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiDefaultProperty where
   fromEnum x = case x of
@@ -353,7 +352,7 @@
 data GuiToggleProperty
   = -- | ToggleGroup separation between toggles
     GroupPadding
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiToggleProperty where
   fromEnum x = case x of
@@ -376,7 +375,7 @@
     SliderWidth
   | -- | Slider/SliderBar internal bar padding
     SliderPadding
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiSliderProperty where
   fromEnum x = case x of
@@ -399,7 +398,7 @@
 data GuiProgressBarProperty
   = -- | ProgressBar internal padding
     ProgressPadding
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiProgressBarProperty where
   fromEnum x = case x of
@@ -430,7 +429,7 @@
     ScrollPadding
   | -- | ScrollBar scrolling speed
     ScrollSpeed
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiScrollBarProperty where
   fromEnum x = case x of
@@ -461,7 +460,7 @@
 data GuiCheckBoxProperty
   = -- | CheckBox internal check padding
     CheckPadding
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiCheckBoxProperty where
   fromEnum x = case x of
@@ -484,7 +483,7 @@
     ComboButtonWidth
   | -- | ComboBox button separation
     ComboButtonSpacing
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiComboBoxProperty where
   fromEnum x = case x of
@@ -513,7 +512,7 @@
     DropdownArrowHidden
   | -- | DropdownBox roll up flag (default rolls down)
     DropdownRollUp
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiDropdownBoxProperty where
   fromEnum x = case x of
@@ -540,7 +539,7 @@
 data GuiTextBoxProperty
   = -- | TextBox in read-only mode: 0-text editable, 1-text no-editable
     TextReadonly
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiTextBoxProperty where
   fromEnum x = case x of
@@ -557,24 +556,24 @@
     return $ toEnum $ fromEnum (val :: CInt)
   poke ptr v = poke (castPtr ptr) (fromIntegral (fromEnum v) :: CInt)
 
--- | Spinner
-data GuiSpinnerProperty
+-- | ValueBox/Spinner
+data GuiValueBoxProperty
   = -- | Spinner left/right buttons width
     SpinButtonWidth
   | -- | Spinner buttons separation
     SpinButtonSpacing
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
-instance Enum GuiSpinnerProperty where
+instance Enum GuiValueBoxProperty where
   fromEnum x = case x of
     SpinButtonWidth -> 16
     SpinButtonSpacing -> 17
   toEnum x = case x of
     16 -> SpinButtonWidth
     17 -> SpinButtonSpacing
-    n -> error $ "(GuiSpinnerProperty.toEnum) Invalid value: " ++ show n
+    n -> error $ "(GuiValueBoxProperty.toEnum) Invalid value: " ++ show n
 
-instance Storable GuiSpinnerProperty where
+instance Storable GuiValueBoxProperty where
   sizeOf _ = 4
   alignment _ = 4
   peek ptr = do
@@ -592,9 +591,11 @@
     ScrollbarWidth
   | -- | ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE)
     ScrollbarSide
+  | -- | ListView items border enabled in normal state
+    ListItemsBorderNormal
   | -- | ListView items border width
     ListItemsBorderWidth
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiListViewProperty where
   fromEnum x = case x of
@@ -602,13 +603,15 @@
     ListItemsSpacing -> 17
     ScrollbarWidth -> 18
     ScrollbarSide -> 19
-    ListItemsBorderWidth -> 20
+    ListItemsBorderNormal -> 20
+    ListItemsBorderWidth -> 21
   toEnum x = case x of
     16 -> ListItemsHeight
     17 -> ListItemsSpacing
     18 -> ScrollbarWidth
     19 -> ScrollbarSide
-    20 -> ListItemsBorderWidth
+    20 -> ListItemsBorderNormal
+    21 -> ListItemsBorderWidth
     n -> error $ "(GuiListViewProperty.toEnum) Invalid value: " ++ show n
 
 instance Storable GuiListViewProperty where
@@ -630,7 +633,7 @@
     HuebarSelectorHeight
   | -- | ColorPicker right hue bar selector overflow
     HuebarSelectorOverflow
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiColorPickerProperty where
   fromEnum x = case x of
@@ -885,34 +888,34 @@
   | IconMLayers
   | IconMaps
   | IconHot
-  | Icon229
-  | Icon230
-  | Icon231
-  | Icon232
-  | Icon233
-  | Icon234
-  | Icon235
-  | Icon236
-  | Icon237
-  | Icon238
-  | Icon239
-  | Icon240
-  | Icon241
-  | Icon242
-  | Icon243
-  | Icon244
-  | Icon245
-  | Icon246
-  | Icon247
-  | Icon248
-  | Icon249
+  | IconLabel
+  | IconNameId
+  | IconSlicing
+  | IconManualControl
+  | IconCollision
+  | IconCircleAdd
+  | IconCircleAddFill
+  | IconCircleWarning
+  | IconCircleWarningFill
+  | IconBoxMore
+  | IconBoxMoreFill
+  | IconBoxMinus
+  | IconBoxMinusFill
+  | IconUnion
+  | IconIntersection
+  | IconDifference
+  | IconSphere
+  | IconCylinder
+  | IconCone
+  | IconEllipsoid
+  | IconCapsule
   | Icon250
   | Icon251
   | Icon252
   | Icon253
   | Icon254
   | Icon255
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum GuiIconName where
   fromEnum x = case x of
@@ -1145,27 +1148,27 @@
     IconMLayers -> 226
     IconMaps -> 227
     IconHot -> 228
-    Icon229 -> 229
-    Icon230 -> 230
-    Icon231 -> 231
-    Icon232 -> 232
-    Icon233 -> 233
-    Icon234 -> 234
-    Icon235 -> 235
-    Icon236 -> 236
-    Icon237 -> 237
-    Icon238 -> 238
-    Icon239 -> 239
-    Icon240 -> 240
-    Icon241 -> 241
-    Icon242 -> 242
-    Icon243 -> 243
-    Icon244 -> 244
-    Icon245 -> 245
-    Icon246 -> 246
-    Icon247 -> 247
-    Icon248 -> 248
-    Icon249 -> 249
+    IconLabel -> 229
+    IconNameId -> 230
+    IconSlicing -> 231
+    IconManualControl -> 232
+    IconCollision -> 233
+    IconCircleAdd -> 234
+    IconCircleAddFill -> 235
+    IconCircleWarning -> 236
+    IconCircleWarningFill -> 237
+    IconBoxMore -> 238
+    IconBoxMoreFill -> 239
+    IconBoxMinus -> 240
+    IconBoxMinusFill -> 241
+    IconUnion -> 242
+    IconIntersection -> 243
+    IconDifference -> 244
+    IconSphere -> 245
+    IconCylinder -> 246
+    IconCone -> 247
+    IconEllipsoid -> 248
+    IconCapsule -> 249
     Icon250 -> 250
     Icon251 -> 251
     Icon252 -> 252
@@ -1402,27 +1405,27 @@
     226 -> IconMLayers
     227 -> IconMaps
     228 -> IconHot
-    229 -> Icon229
-    230 -> Icon230
-    231 -> Icon231
-    232 -> Icon232
-    233 -> Icon233
-    234 -> Icon234
-    235 -> Icon235
-    236 -> Icon236
-    237 -> Icon237
-    238 -> Icon238
-    239 -> Icon239
-    240 -> Icon240
-    241 -> Icon241
-    242 -> Icon242
-    243 -> Icon243
-    244 -> Icon244
-    245 -> Icon245
-    246 -> Icon246
-    247 -> Icon247
-    248 -> Icon248
-    249 -> Icon249
+    229 -> IconLabel
+    230 -> IconNameId
+    231 -> IconSlicing
+    232 -> IconManualControl
+    233 -> IconCollision
+    234 -> IconCircleAdd
+    235 -> IconCircleAddFill
+    236 -> IconCircleWarning
+    237 -> IconCircleWarningFill
+    238 -> IconBoxMore
+    239 -> IconBoxMoreFill
+    240 -> IconBoxMinus
+    241 -> IconBoxMinusFill
+    242 -> IconUnion
+    243 -> IconIntersection
+    244 -> IconDifference
+    245 -> IconSphere
+    246 -> IconCylinder
+    247 -> IconCone
+    248 -> IconEllipsoid
+    249 -> IconCapsule
     250 -> Icon250
     251 -> Icon251
     252 -> Icon252
@@ -1454,7 +1457,7 @@
     -- | Property value
     guiStyleProp'propertyValue :: Int
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable GuiStyleProp where
   sizeOf _ = 8
diff --git a/src/Raylib/Types/Util/RLGL.hs b/src/Raylib/Types/Util/RLGL.hs
--- a/src/Raylib/Types/Util/RLGL.hs
+++ b/src/Raylib/Types/Util/RLGL.hs
@@ -79,7 +79,7 @@
     RLOpenGL43
   | -- | OpenGL ES 2.0 (GLSL 100)
     RLOpenGLES20
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLGLVersion where
   fromEnum n = case n of
@@ -123,7 +123,7 @@
     RLLogFatal
   | -- | Disable logging
     RLLogNone
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLTraceLogLevel where
   sizeOf _ = 4
@@ -184,7 +184,7 @@
     RLPixelFormatCompressedAstc4x4Rgba
   | -- | 2 bpp
     RLPixelFormatCompressedAstc8x8Rgba
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLPixelFormat where
   fromEnum n = case n of
@@ -264,7 +264,7 @@
     RLTextureFilterAnisotropic8x
   | -- | Anisotropic filtering 16x
     RLTextureFilterAnisotropic16x
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLTextureFilter where
   sizeOf _ = 4
@@ -292,7 +292,7 @@
     RlBlendCustom
   | -- | Blend textures using custom src/dst factors (use rlSetBlendFactorsSeparate())
     RlBlendCustomSeparate
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLBlendMode where
   sizeOf _ = 4
@@ -356,7 +356,7 @@
     RLShaderLocMapPrefilter
   | -- | Shader location: sampler2d texture: brdf
     RLShaderLocMapBRDF
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLShaderLocationIndex where
   sizeOf _ = 4
@@ -394,7 +394,7 @@
     RLShaderUniformUIVec4
   | -- | Shader uniform type: sampler2d
     RLShaderUniformSampler2D
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLShaderUniformDataType where
   sizeOf _ = 4
@@ -414,7 +414,7 @@
     RLShaderAttribVec3
   | -- | Shader attribute type: vec4 (4 float)
     RLShaderAttribVec4
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLShaderAttributeDataType where
   sizeOf _ = 4
@@ -447,7 +447,7 @@
     RLAttachmentDepth
   | -- | Framebuffer attachment type: stencil
     RLAttachmentStencil
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLFramebufferAttachType where
   fromEnum n = case n of
@@ -501,7 +501,7 @@
     RLAttachmentTexture2D
   | -- | Framebuffer texture attachment type: renderbuffer
     RLAttachmentRenderBuffer
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLFramebufferAttachTextureType where
   fromEnum n = case n of
@@ -537,7 +537,7 @@
 data RLCullMode
   = RLCullFaceFront
   | RLCullFaceBack
-  deriving (Eq, Show, Enum)
+  deriving (Eq, Show, Read, Enum)
 
 instance Storable RLCullMode where
   sizeOf _ = 4
@@ -555,7 +555,7 @@
     RLProjection
   | -- | GL_TEXTURE
     RLTexture
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLMatrixMode where
   fromEnum n = case n of
@@ -585,7 +585,7 @@
     RLTriangles
   | -- | GL_QUADS
     RLQuads
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLDrawMode where
   fromEnum n = case n of
@@ -633,7 +633,7 @@
     RLTextureParamFilterAnisotropic
   | -- | Texture mipmap bias, percentage ratio (custom identifier)
     RLTextureParamMipmapBiasRatio
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLTextureParam where
   fromEnum n = case n of
@@ -681,7 +681,7 @@
     RLVertexShader
   | -- | GL_COMPUTE_SHADER
     RLComputeShader
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLShaderType where
   fromEnum n = case n of
@@ -723,7 +723,7 @@
     RLBufferHintDynamicRead
   | -- | GL_DYNAMIC_COPY
     RLBufferHintDynamicCopy
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLBufferHint where
   fromEnum n = case n of
@@ -765,7 +765,7 @@
     RLGLDepthBuffer
   | -- | GL_STENCIL_BUFFER_BIT
     RLGLStencilBuffer
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Enum RLBitField where
   fromEnum n = case n of
@@ -810,7 +810,7 @@
     -- | OpenGL Vertex Buffer Objects id (5 types of vertex data)
     rlVertexBuffer'vboId :: [Integer]
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable RLVertexBuffer where
   sizeOf _ = 72
@@ -888,7 +888,7 @@
     -- | Texture id to be used on the draw -> Used to create new draw call if changed
     rlDrawCall'textureId :: Integer
   }
-  deriving (Eq, Show, Freeable)
+  deriving (Eq, Show, Read, Freeable)
 
 instance Storable RLDrawCall where
   sizeOf _ = 16
@@ -933,7 +933,7 @@
     -- | Current depth value for next draw
     rlRenderBatch'currentDepth :: Float
   }
-  deriving (Eq, Show)
+  deriving (Eq, Show, Read)
 
 instance Storable RLRenderBatch where
   sizeOf _ = 32
diff --git a/src/Raylib/Util/GUI.hs b/src/Raylib/Util/GUI.hs
--- a/src/Raylib/Util/GUI.hs
+++ b/src/Raylib/Util/GUI.hs
@@ -114,6 +114,9 @@
     guiLoadIcons,
     guiDrawIcon,
 
+    -- ** Utility functions
+    guiGetTextWidth,
+
     -- ** Controls
 
     -- *** Container/separator controls, useful for controls organization
@@ -180,6 +183,7 @@
     c'guiGetIcons,
     c'guiLoadIcons,
     c'guiDrawIcon,
+    c'guiGetTextWidth,
     c'guiWindowBox,
     c'guiGroupBox,
     c'guiLine,
@@ -258,6 +262,7 @@
        ("c'guiGetIcons", "GuiGetIcons_", "rgui_bindings.h", [t|IO (Ptr CUInt)|]),
        ("c'guiLoadIcons", "GuiLoadIcons_", "rgui_bindings.h", [t|CString -> CBool -> IO (Ptr CString)|]),
        ("c'guiDrawIcon", "GuiDrawIcon_", "rgui_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> Ptr Color -> IO ()|]),
+       ("c'guiGetTextWidth", "GuiGetTextWidth_", "rgui_bindings.h", [t|CString -> IO CInt|]),
        ("c'guiWindowBox", "GuiWindowBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
        ("c'guiGroupBox", "GuiGroupBox_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
        ("c'guiLine", "GuiLine_", "rgui_bindings.h", [t|Ptr Rectangle -> CString -> IO CInt|]),
@@ -627,6 +632,10 @@
 -- | Draw icon using pixel size at specified position
 guiDrawIcon :: GuiIconName -> Int -> Int -> Int -> Color -> IO ()
 guiDrawIcon icon posX posY pixelSize color = withFreeable color (c'guiDrawIcon (fromIntegral (fromEnum icon)) (fromIntegral posX) (fromIntegral posY) (fromIntegral pixelSize))
+
+-- | Get text width considering gui style and icon size (if required)
+guiGetTextWidth :: String -> IO Int
+guiGetTextWidth text = fromIntegral <$> withCString text c'guiGetTextWidth
 
 -- | Window Box control, shows a window that can be closed
 guiWindowBox ::
diff --git a/src/Raylib/Util/Math.hs b/src/Raylib/Util/Math.hs
--- a/src/Raylib/Util/Math.hs
+++ b/src/Raylib/Util/Math.hs
@@ -26,6 +26,7 @@
     Vector (..),
 
     -- ** Vector2 math
+    vector2CrossProduct,
     vector2Angle,
     vector2LineAngle,
     vector2Transform,
@@ -74,6 +75,7 @@
     matrixPerspective,
     matrixOrtho,
     matrixLookAt,
+    matrixCompose,
     matrixDecompose,
 
     -- * Quaternion math
@@ -285,7 +287,7 @@
 
   -- | Normalize vector (same direction, magnitude 1)
   vectorNormalize :: a -> a
-  vectorNormalize v = v |/ magnitude v
+  vectorNormalize v = if m == 0 then v else v |/ m where m = magnitude v
 
   -- | Lerp between two vectors
   vectorLerp :: a -> a -> Float -> a
@@ -378,7 +380,11 @@
 -- Vector2 math --------------------------------
 ------------------------------------------------
 
--- | Angle between two 2D vectors
+-- | 2D vector cross-product
+vector2CrossProduct :: Vector2 -> Vector2 -> Float
+vector2CrossProduct (Vector2 x1 y1) (Vector2 x2 y2) = x1 * y2 - y1 * x2
+
+-- | Angle between two 2D vectors (positive angles are clockwise)
 vector2Angle :: Vector2 -> Vector2 -> Float
 vector2Angle (Vector2 x1 y1) (Vector2 x2 y2) = atan2 (x1 * x2 + y1 * y2) (x1 * y2 - y1 * x2)
 
@@ -650,32 +656,7 @@
 
 -- | Compute matrix determinant
 matrixDeterminant :: Matrix -> Float
-matrixDeterminant
-  (Matrix a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33) =
-    a30 * a21 * a12 * a03
-      - a20 * a31 * a12 * a03
-      - a30 * a11 * a22 * a03
-      + a10 * a31 * a22 * a03
-      + a20 * a11 * a32 * a03
-      - a10 * a21 * a32 * a03
-      - a30 * a21 * a02 * a13
-      + a20 * a31 * a02 * a13
-      + a30 * a01 * a22 * a13
-      - a00 * a31 * a22 * a13
-      - a20 * a01 * a32 * a13
-      + a00 * a21 * a32 * a13
-      + a30 * a11 * a02 * a23
-      - a10 * a31 * a02 * a23
-      - a30 * a01 * a12 * a23
-      + a00 * a31 * a12 * a23
-      + a10 * a01 * a32 * a23
-      - a00 * a11 * a32 * a23
-      - a20 * a11 * a02 * a33
-      + a10 * a21 * a02 * a33
-      + a20 * a01 * a12 * a33
-      - a00 * a21 * a12 * a33
-      - a10 * a01 * a22 * a33
-      + a00 * a11 * a22 * a33
+matrixDeterminant (Matrix a0 a4 a8 a12 a1 a5 a9 a13 a2 a6 a10 a14 a3 a7 a11 a15) = (a0*((a5*(a10*a15 - a11*a14) - a9*(a6*a15 - a7*a14) + a13*(a6*a11 - a7*a10))) - a4*((a1*(a10*a15 - a11*a14) - a9*(a2*a15 - a3*a14) + a13*(a2*a11 - a3*a10))) + a8*((a1*(a6*a15 - a7*a14) - a5*(a2*a15 - a3*a14) + a13*(a2*a7 - a3*a6))) - a12*((a1*(a6*a11 - a7*a10) - a5*(a2*a11 - a3*a10) + a9*(a2*a7 - a3*a6))));
 
 -- | Trace of a matrix (sum of the values along the diagonal)
 matrixTrace :: Matrix -> Float
@@ -935,34 +916,49 @@
     vx@(Vector3 xx xy xz) = vectorNormalize $ vector3CrossProduct up vz
     vy@(Vector3 yx yy yz) = vector3CrossProduct vz vx
 
+-- | Compose a transformation matrix from rotational, translational and scaling components
+matrixCompose :: Vector3 -> Quaternion -> Vector3 -> Matrix
+matrixCompose (Vector3 tx ty tz) rotation (Vector3 sx sy sz) =
+  Matrix rx ux fx tx ry uy fy ty rz uz fz tz 0 0 0 1
+  where
+    (Vector3 rx ry rz) = vector3RotateByQuaternion (Vector3 sx 0 0) rotation
+    (Vector3 ux uy uz) = vector3RotateByQuaternion (Vector3 0 sy 0) rotation
+    (Vector3 fx fy fz) = vector3RotateByQuaternion (Vector3 0 0 sz) rotation
+
 -- | Decompose a transformation matrix into its rotational, translational and scaling components
 matrixDecompose :: Matrix -> (Vector3, Quaternion, Vector3)
-matrixDecompose (Matrix m0 m4 m8 m12 m1 m5 m9 m13 m2 m6 m10 m14 m3 m7 m11 m15) =
-  (translation, rotation, scale)
+matrixDecompose (Matrix m0 m4 m8 m12 m1 m5 m9 m13 m2 m6 m10 m14 _ _ _ _) =
+  (translation, rotation, scale')
   where
-    a = m0
-    b = m4
-    c = m8
-    d = m1
-    e = m5
-    f = m9
-    g = m2
-    h = m6
-    i = m10
+    eps = 1e-9
     translation = Vector3 m12 m13 m14
-    a' = e*i - f*h
-    b' = f*g - d*i
-    c' = d*h - e*g
-    det = a*a' + b*b' + c*c'
-    abc = Vector3 a b c
-    def = Vector3 d e f
-    ghi = Vector3 g h i
-    scalex = magnitude abc
-    scaley = magnitude def
-    scalez = magnitude ghi
-    s = Vector3 scalex scaley scalez
-    scale@(Vector3 sx sy sz) = if det < 0 then additiveInverse s else s
-    rotation = if floatEquals det 0 then quaternionIdentity else quaternionFromMatrix (Matrix (m0 / sx) m4 m8 m12 m1 (m5 / sy) m9 m13 m2 m6 (m10 / sz) m14 m3 m7 m11 m15)
+    stabilizer = maximum (eps : map abs [m0, m1, m2, m4, m5, m6, m8, m9, m10])
+    (scl1, col1) =
+      let c = (Vector3 m0 m4 m8) |/ stabilizer
+          m = magnitude c
+      in (m, if m > eps then c |/ m else c)
+    (scl2, col2) =
+      let c = (Vector3 m1 m5 m9) |/ stabilizer
+          shr = col1 |.| c
+          c' = c |-| (col1 |* shr)
+          m = magnitude c'
+      in (m, if m > eps then c' |/ m else c')
+    (scl3, col3) =
+      let c = (Vector3 m2 m6 m10) |/ stabilizer
+          shr2 = col1 |.| c
+          c' = c |-| (col1 |* shr2)
+          shr3 = col2 |.| c'
+          c'' = c' |-| (col2 |* shr3)
+          m = magnitude c''
+      in if m > eps
+        then (m, c'' |/ m)
+        else (m, c'')
+    scale = (Vector3 scl1 scl2 scl3) |* stabilizer
+    ((Vector3 c1x c1y c1z), (Vector3 c2x c2y c2z), (Vector3 c3x c3y c3z), scale') =
+      if (col1 |.| vector3CrossProduct col2 col3) < 0
+        then (additiveInverse col1, additiveInverse col2, additiveInverse col3, additiveInverse scale)
+        else (col1, col2, col3, scale)
+    rotation = quaternionFromMatrix (Matrix c1x c1y c1z 0 c2x c2y c2z 0 c3x c3y c3z 0 0 0 0 1)
 
 ------------------------------------------------
 -- Quaternion math -----------------------------
@@ -1088,7 +1084,7 @@
           fourZSquaredMinus1
         ]
     biggestVal = sqrt (fourBiggestSquaredMinus1 + 1) / 2
-    mult = 0.5 / biggestVal
+    mult = 0.25 / biggestVal
 
 -- | Create a rotation matrix from a quaternion
 quaternionToMatrix :: Quaternion -> Matrix
diff --git a/src/Raylib/Util/RLGL.hs b/src/Raylib/Util/RLGL.hs
--- a/src/Raylib/Util/RLGL.hs
+++ b/src/Raylib/Util/RLGL.hs
@@ -82,8 +82,11 @@
     rlEnableScissorTest,
     rlDisableScissorTest,
     rlScissor,
-    rlEnableWireMode,
     rlEnablePointMode,
+    rlDisablePointMode,
+    rlSetPointSize,
+    rlGetPointSize,
+    rlEnableWireMode,
     rlDisableWireMode,
     rlSetLineWidth,
     rlGetLineWidth,
@@ -105,6 +108,7 @@
     rlglInit,
     rlglClose,
     rlLoadExtensions,
+    rlGetProcAddress,
     rlGetVersion,
     rlSetFramebufferWidth,
     rlGetFramebufferWidth,
@@ -158,6 +162,8 @@
     rlLoadFramebuffer,
     rlFramebufferAttach,
     rlFramebufferComplete,
+    rlCopyFramebuffer,
+    rlResizeFramebuffer,
     rlUnloadFramebuffer,
 
     -- *** Shaders management
@@ -251,6 +257,7 @@
     c'rlSetBlendFactorsSeparate,
     c'rlglInit,
     c'rlLoadExtensions,
+    c'rlGetProcAddress,
     c'rlGetVersion,
     c'rlSetFramebufferWidth,
     c'rlGetFramebufferWidth,
@@ -292,6 +299,8 @@
     c'rlLoadFramebuffer,
     c'rlFramebufferAttach,
     c'rlFramebufferComplete,
+    c'rlCopyFramebuffer,
+    c'rlResizeFramebuffer,
     c'rlUnloadFramebuffer,
     c'rlLoadShaderCode,
     c'rlCompileShader,
@@ -346,8 +355,11 @@
     c'rlDisableBackfaceCulling,
     c'rlEnableScissorTest,
     c'rlDisableScissorTest,
-    c'rlEnableWireMode,
     c'rlEnablePointMode,
+    c'rlDisablePointMode,
+    c'rlSetPointSize,
+    c'rlGetPointSize,
+    c'rlEnableWireMode,
     c'rlDisableWireMode,
     c'rlEnableSmoothLines,
     c'rlDisableSmoothLines,
@@ -461,6 +473,7 @@
        ("c'rlSetBlendFactorsSeparate", "rlSetBlendFactorsSeparate_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> CInt -> IO ()|]),
        ("c'rlglInit", "rlglInit_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|]),
        ("c'rlLoadExtensions", "rlLoadExtensions_", "rlgl_bindings.h", [t|Ptr () -> IO ()|]),
+       ("c'rlGetProcAddress", "rlGetProcAddress_", "rlgl_bindings.h", [t|CString -> IO (Ptr ())|]),
        ("c'rlGetVersion", "rlGetVersion_", "rlgl_bindings.h", [t|IO CInt|]),
        ("c'rlSetFramebufferWidth", "rlSetFramebufferWidth_", "rlgl_bindings.h", [t|CInt -> IO ()|]),
        ("c'rlGetFramebufferWidth", "rlGetFramebufferWidth_", "rlgl_bindings.h", [t|IO CInt|]),
@@ -491,7 +504,7 @@
        ("c'rlDrawVertexArrayElementsInstanced", "rlDrawVertexArrayElementsInstanced_", "rlgl_bindings.h", [t|CInt -> CInt -> Ptr () -> CInt -> IO ()|]),
        ("c'rlLoadTexture", "rlLoadTexture_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> CInt -> CInt -> IO CUInt|]),
        ("c'rlLoadTextureDepth", "rlLoadTextureDepth_", "rlgl_bindings.h", [t|CInt -> CInt -> CBool -> IO CUInt|]),
-       ("c'rlLoadTextureCubemap", "rlLoadTextureCubemap_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> IO CUInt|]),
+       ("c'rlLoadTextureCubemap", "rlLoadTextureCubemap_", "rlgl_bindings.h", [t|Ptr () -> CInt -> CInt -> CInt -> IO CUInt|]),
        ("c'rlUpdateTexture", "rlUpdateTexture_", "rlgl_bindings.h", [t|CUInt -> CInt -> CInt -> CInt -> CInt -> CInt -> Ptr () -> IO ()|]),
        ("c'rlGetGlTextureFormats", "rlGetGlTextureFormats_", "rlgl_bindings.h", [t|CInt -> Ptr CUInt -> Ptr CUInt -> Ptr CUInt -> IO ()|]),
        ("c'rlGetPixelFormatName", "rlGetPixelFormatName_", "rlgl_bindings.h", [t|CUInt -> IO CString|]),
@@ -502,6 +515,8 @@
        ("c'rlLoadFramebuffer", "rlLoadFramebuffer_", "rlgl_bindings.h", [t|IO CUInt|]),
        ("c'rlFramebufferAttach", "rlFramebufferAttach_", "rlgl_bindings.h", [t|CUInt -> CUInt -> CInt -> CInt -> CInt -> IO ()|]),
        ("c'rlFramebufferComplete", "rlFramebufferComplete_", "rlgl_bindings.h", [t|CUInt -> IO CBool|]),
+       ("c'rlCopyFramebuffer", "rlCopyFramebuffer_", "rlgl_bindings.h", [t|CInt -> CInt -> CInt -> CInt -> CInt -> Ptr CUChar -> IO ()|]),
+       ("c'rlResizeFramebuffer", "rlResizeFramebuffer_", "rlgl_bindings.h", [t|CInt -> CInt -> IO ()|]),
        ("c'rlUnloadFramebuffer", "rlUnloadFramebuffer_", "rlgl_bindings.h", [t|CUInt -> IO ()|]),
        ("c'rlLoadShaderCode", "rlLoadShaderCode_", "rlgl_bindings.h", [t|CString -> CString -> IO CUInt|]),
        ("c'rlCompileShader", "rlCompileShader_", "rlgl_bindings.h", [t|CString -> CInt -> IO CUInt|]),
@@ -556,8 +571,11 @@
        ("c'rlDisableBackfaceCulling", "rlDisableBackfaceCulling_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlEnableScissorTest", "rlEnableScissorTest_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlDisableScissorTest", "rlDisableScissorTest_", "rlgl_bindings.h", [t|IO ()|]),
-       ("c'rlEnableWireMode", "rlEnableWireMode_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlEnablePointMode", "rlEnablePointMode_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlDisablePointMode", "rlDisablePointMode_", "rlgl_bindings.h", [t|IO ()|]),
+       ("c'rlSetPointSize", "rlSetPointSize_", "rlgl_bindings.h", [t|CFloat -> IO ()|]),
+       ("c'rlGetPointSize", "rlGetPointSize_", "rlgl_bindings.h", [t|IO CFloat|]),
+       ("c'rlEnableWireMode", "rlEnableWireMode_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlDisableWireMode", "rlDisableWireMode_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlEnableSmoothLines", "rlEnableSmoothLines_", "rlgl_bindings.h", [t|IO ()|]),
        ("c'rlDisableSmoothLines", "rlDisableSmoothLines_", "rlgl_bindings.h", [t|IO ()|]),
@@ -820,14 +838,26 @@
 rlScissor :: Int -> Int -> Int -> Int -> IO ()
 rlScissor x y width height = c'rlScissor (fromIntegral x) (fromIntegral y) (fromIntegral width) (fromIntegral height)
 
--- | Enable wire mode
-rlEnableWireMode :: IO ()
-rlEnableWireMode = c'rlEnableWireMode
-
 -- | Enable point mode
 rlEnablePointMode :: IO ()
 rlEnablePointMode = c'rlEnablePointMode
 
+-- | Disable point mode
+rlDisablePointMode :: IO ()
+rlDisablePointMode = c'rlDisablePointMode
+
+-- | Set the point drawing size
+rlSetPointSize :: Float -> IO ()
+rlSetPointSize size = c'rlSetPointSize (realToFrac size)
+
+-- | Get the point drawing size
+rlGetPointSize :: IO Float
+rlGetPointSize = realToFrac <$> c'rlGetPointSize
+
+-- | Enable wire mode
+rlEnableWireMode :: IO ()
+rlEnableWireMode = c'rlEnableWireMode
+
 -- | Disable wire and point mode
 rlDisableWireMode :: IO ()
 rlDisableWireMode = c'rlDisableWireMode
@@ -897,6 +927,10 @@
 rlLoadExtensions :: Ptr () -> IO ()
 rlLoadExtensions = c'rlLoadExtensions
 
+-- | Get OpenGL procedure address
+rlGetProcAddress :: String -> IO (Ptr ())
+rlGetProcAddress procName = withCString procName c'rlGetProcAddress
+
 -- | Get current OpenGL version
 rlGetVersion :: IO Int
 rlGetVersion = fromIntegral <$> c'rlGetVersion
@@ -1048,10 +1082,10 @@
 rlLoadTextureDepth width height useRenderBuffer = fromIntegral <$> c'rlLoadTextureDepth (fromIntegral width) (fromIntegral height) (fromBool useRenderBuffer)
 
 -- | Load texture cubemap
-rlLoadTextureCubemap :: [Int] -> RLPixelFormat -> IO Integer
-rlLoadTextureCubemap tData format =
+rlLoadTextureCubemap :: [Int] -> RLPixelFormat -> Int -> IO Integer
+rlLoadTextureCubemap tData format mipmapCount =
   fromIntegral
-    <$> withFreeableArrayLen (map fromIntegral tData :: [CUShort]) (\l p -> c'rlLoadTextureCubemap (castPtr p) (fromIntegral $ l * sizeOf (0 :: CUShort)) (fromIntegral $ fromEnum format))
+    <$> withFreeableArrayLen (map fromIntegral tData :: [CUShort]) (\l p -> c'rlLoadTextureCubemap (castPtr p) (fromIntegral $ l * sizeOf (0 :: CUShort)) (fromIntegral $ fromEnum format) (fromIntegral mipmapCount))
 
 -- | Update GPU texture with new data
 rlUpdateTexture :: (Freeable a, Storable a) => Integer -> Int -> Int -> Int -> Int -> RLPixelFormat -> [a] -> IO ()
@@ -1150,6 +1184,14 @@
 -- | Verify framebuffer is complete
 rlFramebufferComplete :: Integer -> IO Bool
 rlFramebufferComplete fboId = toBool <$> c'rlFramebufferComplete (fromIntegral fboId)
+
+-- | Copy framebuffer pixel data to internal buffer
+rlCopyFramebuffer :: Int -> Int -> Int -> Int -> RLPixelFormat -> [Word8] -> IO ()
+rlCopyFramebuffer x y width height format pixels = withFreeableArray (map fromIntegral pixels) $ c'rlCopyFramebuffer (fromIntegral x) (fromIntegral y) (fromIntegral width) (fromIntegral height) (fromIntegral $ fromEnum format)
+
+-- | Resize internal framebuffer
+rlResizeFramebuffer :: Int -> Int -> IO ()
+rlResizeFramebuffer width height = c'rlResizeFramebuffer (fromIntegral width) (fromIntegral height)
 
 -- | Delete framebuffer from GPU
 rlUnloadFramebuffer :: Integer -> IO ()
